All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.squareup.javapoet.x2c.ClassName Maven / Gradle / Ivy

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 com.squareup.javapoet.x2c;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

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 static com.squareup.javapoet.x2c.Util.checkArgument;
import static com.squareup.javapoet.x2c.Util.checkNotNull;

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

  /** The package name 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 name, like "Entry" for java.util.Map.Entry. */
  final String simpleName;

  /** The full class name like "java.util.Map.Entry". */
  final String canonicalName;

  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 = packageName;
    this.enclosingClassName = enclosingClassName;
    this.simpleName = simpleName;
    this.canonicalName = enclosingClassName != null
        ? (enclosingClassName.canonicalName + '.' + simpleName)
        : (packageName.isEmpty() ? simpleName : packageName + '.' + simpleName);
  }

  @Override public ClassName annotated(List annotations) {
    return new ClassName(packageName, enclosingClassName, simpleName,
        concatAnnotations(annotations));
  }

  @Override public ClassName withoutAnnotations() {
    if (!isAnnotated()) return this;
    ClassName resultEnclosingClassName = enclosingClassName != null
        ? enclosingClassName.withoutAnnotations()
        : null;
    return new ClassName(packageName, resultEnclosingClassName, simpleName);
  }

  @Override public boolean isAnnotated() {
    return super.isAnnotated() || (enclosingClassName != null && enclosingClassName.isAnnotated());
  }


  public String packageName() {
    return packageName;
  }


  public ClassName enclosingClassName() {
    return enclosingClassName;
  }


  public ClassName topLevelClassName() {
    return enclosingClassName != null ? enclosingClassName.topLevelClassName() : this;
  }


  public String reflectionName() {
    return enclosingClassName != null
        ? (enclosingClassName.reflectionName() + '$' + simpleName)
        : (packageName.isEmpty() ? simpleName : packageName + '.' + simpleName);
  }

  public List simpleNames() {
    List simpleNames = new ArrayList<>();
    if (enclosingClassName != null) {
      simpleNames.addAll(enclosingClassName().simpleNames());
    }
    simpleNames.add(simpleName);
    return simpleNames;
  }


  public ClassName peerClass(String name) {
    return new ClassName(packageName, enclosingClassName, name);
  }


  public ClassName nestedClass(String name) {
    return new ClassName(packageName, this, name);
  }


  public String simpleName() {
    return simpleName;
  }

  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");

    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) : null;
      return new ClassName(packageName, null, name);
    }

    return ClassName.get(clazz.getEnclosingClass()).nestedClass(name);
  }


  public static ClassName bestGuess(String classNameString) {
    // Add the package name, 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;
      checkArgument(p != 0, "couldn't make a guess for %s", classNameString);
    }
    String packageName = p == 0 ? "" : classNameString.substring(0, p - 1);

    // Add class names like "Map" and "Entry".
    ClassName className = null;
    for (String simpleName : classNameString.substring(p).split("\\.", -1)) {
      checkArgument(!simpleName.isEmpty() && Character.isUpperCase(simpleName.codePointAt(0)),
          "couldn't make a guess for %s", classNameString);
      className = new ClassName(packageName, className, simpleName);
    }

    return className;
  }


  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;
  }


  public static ClassName get(TypeElement element) {
    checkNotNull(element, "element == null");
    String simpleName = element.getSimpleName().toString();

    return element.getEnclosingElement().accept(new SimpleElementVisitor8() {
      @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);
      }

      @Override public ClassName visitUnknown(Element unknown, Void p) {
        return get("", simpleName);
      }

      @Override public ClassName defaultAction(Element enclosingElement, Void p) {
        throw new IllegalArgumentException("Unexpected type nesting: " + element);
      }
    }, null);
  }

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

  @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 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;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy