tech.deplant.javapoet.ClassName Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javapoet-core Show documentation
Show all versions of javapoet-core Show documentation
Use beautiful Java code to generate beautiful Java code.
The newest version!
/*
* 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 tech.deplant.javapoet;
import javax.lang.model.element.Element;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.SimpleElementVisitor8;
import java.io.IOException;
import java.util.*;
/**
* A fully-qualified class parameterName for top-level and member classes.
*/
public final class ClassName extends TypeName implements Comparable {
/**
* The parameterName representing the default Java package.
*/
private static final String NO_PACKAGE = "";
public static final ClassName OBJECT = ClassName.get(Object.class);
/**
* The package parameterName of this class, or "" if this is in the default package.
*/
final String packageName;
/**
* The enclosing class, or null if this is not enclosed in another class.
*/
final ClassName enclosingClassName;
/**
* This class parameterName, like "Entry" for java.util.Map.Entry.
*/
final String simpleName;
/**
* The full class parameterName like "java.util.Map.Entry".
*/
final String canonicalName;
private List simpleNames;
private ClassName(String packageName, ClassName enclosingClassName, String simpleName) {
this(packageName, enclosingClassName, simpleName, Collections.emptyList());
}
private ClassName(String packageName,
ClassName enclosingClassName,
String simpleName,
List annotations) {
super(annotations);
this.packageName = Objects.requireNonNull(packageName, "packageName == null");
this.enclosingClassName = enclosingClassName;
this.simpleName = simpleName;
this.canonicalName = enclosingClassName != null ? (enclosingClassName.canonicalName + '.' +
simpleName) : (packageName.isEmpty() ? simpleName :
packageName + '.' + simpleName);
}
public static ClassName get(Class> clazz) {
Util.checkNotNull(clazz, "clazz == null");
Util.checkArgument(!clazz.isPrimitive(), "primitive types cannot be represented as a ClassName");
Util.checkArgument(!void.class.equals(clazz), "'void' type cannot be represented as a ClassName");
Util.checkArgument(!clazz.isArray(), "array types cannot be represented as a ClassName");
String anonymousSuffix = "";
while (clazz.isAnonymousClass()) {
int lastDollar = clazz.getName().lastIndexOf('$');
anonymousSuffix = clazz.getName().substring(lastDollar) + anonymousSuffix;
clazz = clazz.getEnclosingClass();
}
String name = clazz.getSimpleName() + anonymousSuffix;
if (clazz.getEnclosingClass() == null) {
// Avoid unreliable Class.getPackage(). https://github.com/square/javapoet/issues/295
int lastDot = clazz.getName().lastIndexOf('.');
String packageName = (lastDot != -1) ? clazz.getName().substring(0, lastDot) : NO_PACKAGE;
return new ClassName(packageName, null, name);
}
return ClassName.get(clazz.getEnclosingClass()).nestedClass(name);
}
/**
* Returns a new {@link ClassName} instance for the given fully-qualified class parameterName 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) {
// Add the package parameterName, like "java.util.concurrent", or "" for no package.
int p = 0;
while (p < classNameString.length() && Character.isLowerCase(classNameString.codePointAt(p))) {
p = classNameString.indexOf('.', p) + 1;
Util.checkArgument(p != 0, "couldn't make a guess for %s", classNameString);
}
String packageName = p == 0 ? NO_PACKAGE : classNameString.substring(0, p - 1);
// Add class names like "Map" and "Entry".
ClassName className = null;
for (String simpleName : classNameString.substring(p).split("\\.", -1)) {
Util.checkArgument(!simpleName.isEmpty() && Character.isUpperCase(simpleName.codePointAt(0)),
"couldn't make a guess for %s",
classNameString);
className = new ClassName(packageName, className, simpleName);
}
return className;
}
/**
* Returns a class parameterName created from the given parts. For example, calling this with package parameterName
* {@code "java.util"} and simple names {@code "Map"}, {@code "Entry"} yields {@link Map.Entry}.
*/
public static ClassName get(String packageName, String simpleName, String... simpleNames) {
ClassName className = new ClassName(packageName, null, simpleName);
for (String name : simpleNames) {
className = className.nestedClass(name);
}
return className;
}
/**
* Returns the class parameterName for {@code element}.
*/
public static ClassName get(TypeElement element) {
Util.checkNotNull(element, "element == null");
String simpleName = element.getSimpleName().toString();
return element.getEnclosingElement().accept(new SimpleElementVisitor8() {
@Override
public ClassName visitUnknown(Element unknown, Void p) {
return ClassName.get("", simpleName);
}
@Override
public ClassName defaultAction(Element enclosingElement, Void p) {
throw new IllegalArgumentException("Unexpected type nesting: " + element);
}
@Override
public ClassName visitPackage(PackageElement packageElement, Void p) {
return new ClassName(packageElement.getQualifiedName().toString(), null, simpleName);
}
@Override
public ClassName visitType(TypeElement enclosingClass, Void p) {
return ClassName.get(enclosingClass).nestedClass(simpleName);
}
}, null);
}
@Override
public ClassName annotated(List annotations) {
return new ClassName(this.packageName,
this.enclosingClassName,
this.simpleName,
concatAnnotations(annotations));
}
@Override
public ClassName withoutAnnotations() {
if (!isAnnotated()) {
return this;
}
ClassName resultEnclosingClassName =
this.enclosingClassName != null ? this.enclosingClassName.withoutAnnotations() : null;
return new ClassName(this.packageName, resultEnclosingClassName, this.simpleName);
}
@Override
public boolean isAnnotated() {
return super.isAnnotated() || (this.enclosingClassName != null && this.enclosingClassName.isAnnotated());
}
@Override
CodeWriter emit(CodeWriter out) throws IOException {
boolean charsEmitted = false;
for (ClassName className : enclosingClasses()) {
String simpleName;
if (charsEmitted) {
// We've already emitted an enclosing class. Emit as we go.
out.emit(".");
simpleName = className.simpleName;
} else if (className.isAnnotated() || className == this) {
// We encountered the first enclosing class that must be emitted.
String qualifiedName = out.lookupName(className);
int dot = qualifiedName.lastIndexOf('.');
if (dot != -1) {
out.emitAndIndent(qualifiedName.substring(0, dot + 1));
simpleName = qualifiedName.substring(dot + 1);
charsEmitted = true;
} else {
simpleName = qualifiedName;
}
} else {
// Don't emit this enclosing type. Keep going so we can be more precise.
continue;
}
if (className.isAnnotated()) {
if (charsEmitted) {
out.emit(" ");
}
className.emitAnnotations(out);
}
out.emit(simpleName);
charsEmitted = true;
}
return out;
}
/**
* Returns the package parameterName, like {@code "java.util"} for {@code Map.Entry}. Returns the empty
* string for the default package.
*/
public String packageName() {
return this.packageName;
}
/**
* 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() {
return this.enclosingClassName;
}
/**
* Returns the top class in this nesting group. Equivalent to chained calls to {@link
* #enclosingClassName()} until the result's enclosing class is null.
*/
public ClassName topLevelClassName() {
return this.enclosingClassName != null ? this.enclosingClassName.topLevelClassName() : this;
}
/**
* Return the binary parameterName of a class.
*/
public String reflectionName() {
return this.enclosingClassName != null ? (this.enclosingClassName.reflectionName() + '$' +
this.simpleName) : (this.packageName.isEmpty() ? this.simpleName :
this.packageName + '.' + this.simpleName);
}
public List simpleNames() {
if (this.simpleNames != null) {
return this.simpleNames;
}
if (this.enclosingClassName == null) {
this.simpleNames = Collections.singletonList(this.simpleName);
} else {
List mutableNames = new ArrayList<>();
mutableNames.addAll(enclosingClassName().simpleNames());
mutableNames.add(this.simpleName);
this.simpleNames = Collections.unmodifiableList(mutableNames);
}
return this.simpleNames;
}
/**
* 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(parameterName)}. Otherwise
* it is equivalent to {@code get(packageName(), parameterName)}.
*/
public ClassName peerClass(String name) {
return new ClassName(this.packageName, this.enclosingClassName, name);
}
/**
* Returns a new {@link ClassName} instance for the specified {@code parameterName} as nested inside this
* class.
*/
public ClassName nestedClass(String name) {
return new ClassName(this.packageName, this, name);
}
/**
* Returns the simple parameterName of this class, like {@code "Entry"} for {@link Map.Entry}.
*/
public String simpleName() {
return this.simpleName;
}
/**
* Returns the full class parameterName of this class.
* Like {@code "java.util.Map.Entry"} for {@link Map.Entry}.
*/
public String canonicalName() {
return this.canonicalName;
}
@Override
public int compareTo(ClassName o) {
return this.canonicalName.compareTo(o.canonicalName);
}
/**
* Returns all enclosing classes in this, outermost first.
*/
private List enclosingClasses() {
List result = new ArrayList<>();
for (ClassName c = this; c != null; c = c.enclosingClassName) {
result.add(c);
}
Collections.reverse(result);
return result;
}
}