com.squareup.javapoet.ClassName.java Source code

Java tutorial

Introduction

Here is the source code for com.squareup.javapoet.ClassName.java

Source

/*
 * Copyright (C) 2014 Google, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.squareup.javapoet;

import com.google.common.base.Ascii;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.NestingKind;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static javax.lang.model.element.NestingKind.MEMBER;
import static javax.lang.model.element.NestingKind.TOP_LEVEL;

/** A fully-qualified class name for top-level and member classes. */
public final class ClassName implements Type, Comparable<ClassName> {
    public static final ClassName OBJECT = ClassName.get(Object.class);

    /** From top to bottom. This will be ["java.util", "Map", "Entry"] for {@link Map.Entry}. */
    final ImmutableList<String> names;
    final String canonicalName;

    private ClassName(List<String> names) {
        for (int i = 1; i < names.size(); i++) {
            checkArgument(SourceVersion.isName(names.get(i)), "part '%s' is keyword", names.get(i));
        }
        this.names = ImmutableList.copyOf(names);
        this.canonicalName = Joiner.on(".").join(names.get(0).isEmpty() ? names.subList(1, names.size()) : names);
    }

    /** Returns the package name, like {@code "java.util"} for {@code Map.Entry}. */
    public String packageName() {
        return names.get(0);
    }

    /**
     * Returns the enclosing class, like {@link Map} for {@code Map.Entry}. Returns null if this class
     * is not nested in another class.
     */
    public ClassName enclosingClassName() {
        if (names.size() == 2)
            return null;
        return new ClassName(names.subList(0, names.size() - 1));
    }

    /**
     * Returns a new {@link ClassName} instance for the specified {@code name} as nested inside this
     * class.
     */
    public ClassName nestedClass(String name) {
        checkNotNull(name, "name == null");
        return new ClassName(new ImmutableList.Builder<String>().addAll(names).add(name).build());
    }

    public ImmutableList<String> simpleNames() {
        return names.subList(1, names.size());
    }

    /**
     * Returns a class that shares the same enclosing package or class. If this class is enclosed by
     * another class, this is equivalent to {@code enclosingClassName().nestedClass(name)}. Otherwise
     * it is equivalent to {@code get(packageName(), name)}.
     */
    public ClassName peerClass(String name) {
        return new ClassName(
                new ImmutableList.Builder<String>().addAll(names.subList(0, names.size() - 1)).add(name).build());
    }

    /** Returns the simple name of this class, like {@code "Entry"} for {@link Map.Entry}. */
    public String simpleName() {
        return Iterables.getLast(names);
    }

    public static ClassName get(Class<?> clazz) {
        checkNotNull(clazz, "clazz == null");
        checkArgument(!clazz.isPrimitive(), "primitive types cannot be represented as a ClassName");
        checkArgument(!void.class.equals(clazz), "'void' type cannot be represented as a ClassName");
        checkArgument(!clazz.isArray(), "array types cannot be represented as a ClassName");
        List<String> names = new ArrayList<>();
        for (Class<?> c = clazz; c != null; c = c.getEnclosingClass()) {
            names.add(c.getSimpleName());
        }
        names.add(clazz.getPackage().getName());
        Collections.reverse(names);
        return new ClassName(names);
    }

    /**
     * Returns a new {@link ClassName} instance for the given fully-qualified class name string. This
     * method assumes that the input is ASCII and follows typical Java style (lowercase package
     * names, UpperCamelCase class names) and may produce incorrect results or throw
     * {@link IllegalArgumentException} otherwise. For that reason, {@link #get(Class)} and
     * {@link #get(Class)} should be preferred as they can correctly create {@link ClassName}
     * instances without such restrictions.
     */
    public static ClassName bestGuess(String classNameString) {
        List<String> names = new ArrayList<>();

        // Add the package name, like "java.util.concurrent", or "" for no package.
        int p = 0;
        while (p < classNameString.length() && Ascii.isLowerCase(classNameString.charAt(p))) {
            p = classNameString.indexOf('.', p) + 1;
            checkArgument(p != 0, "couldn't make a guess for %s", classNameString);
        }
        names.add(p != 0 ? classNameString.substring(0, p - 1) : "");

        // Add the class names, like "Map" and "Entry".
        for (String part : Splitter.on('.').split(classNameString.substring(p))) {
            checkArgument(!part.isEmpty() && Ascii.isUpperCase(part.charAt(0)), "couldn't make a guess for %s",
                    classNameString);
            names.add(part);
        }

        checkArgument(names.size() >= 2, "couldn't make a guess for %s", classNameString);
        return new ClassName(names);
    }

    /**
     * Returns a class name created from the given parts. For example, calling this with package name
     * {@code "java.util"} and simple names {@code "Map"}, {@code "Entry"} yields {@link Map.Entry}.
     */
    public static ClassName get(String packageName, String simpleName, String... simpleNames) {
        return new ClassName(
                new ImmutableList.Builder<String>().add(packageName).add(simpleName).add(simpleNames).build());
    }

    private static final ImmutableSet<NestingKind> ACCEPTABLE_NESTING_KINDS = Sets.immutableEnumSet(TOP_LEVEL,
            MEMBER);

    /** Returns the class name for {@code element}. */
    public static ClassName get(TypeElement element) {
        checkNotNull(element, "element == null");
        List<String> names = new ArrayList<>();
        for (Element e = element; isClassOrInterface(e); e = e.getEnclosingElement()) {
            checkArgument(ACCEPTABLE_NESTING_KINDS.contains(element.getNestingKind()));
            names.add(e.getSimpleName().toString());
        }
        names.add(getPackage(element).getQualifiedName().toString());
        Collections.reverse(names);
        return new ClassName(names);
    }

    private static boolean isClassOrInterface(Element e) {
        return e.getKind().isClass() || e.getKind().isInterface();
    }

    private static PackageElement getPackage(Element type) {
        while (type.getKind() != ElementKind.PACKAGE) {
            type = type.getEnclosingElement();
        }
        return (PackageElement) type;
    }

    @Override
    public boolean equals(Object o) {
        return o instanceof ClassName && canonicalName.equals(((ClassName) o).canonicalName);
    }

    @Override
    public int hashCode() {
        return canonicalName.hashCode();
    }

    @Override
    public int compareTo(ClassName o) {
        return canonicalName.compareTo(o.canonicalName);
    }

    @Override
    public String toString() {
        return canonicalName;
    }
}