content: /*
   * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Oracle designates this
   * particular file as subject to the "Classpath" exception as provided
   * by Oracle in the LICENSE file that accompanied this code.
   *
   * This code is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   * version 2 for more details (a copy is included in the LICENSE file that
   * accompanied this code).
   *
   * You should have received a copy of the GNU General Public License version
   * 2 along with this work; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   *
   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  
  package openjdk.tools.classfile;
  
  import java.util.Arrays;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Set;
  
  /*
   *  Family of classes used to represent the parsed form of a {@link Descriptor}
   *  or {@link Signature}.
   *
   *  <p><b>This is NOT part of any supported API.
   *  If you write code that depends on this, you do so at your own risk.
   *  This code and its internal interfaces are subject to change or
   *  deletion without notice.</b>
   */
  public abstract class Type {
      protected Type() { }
  
      public boolean isObject() {
          return false;
      }
  
      public abstract <R,D> R accept(Visitor<R,D> visitor, D data);
  
      protected static void append(StringBuilder sb, String prefix, List<? extends Type> types, String suffix) {
          sb.append(prefix);
          String sep = "";
          for (Type t: types) {
              sb.append(sep);
              sb.append(t);
              sep = ", ";
          }
          sb.append(suffix);
      }
  
      protected static void appendIfNotEmpty(StringBuilder sb, String prefix, List<? extends Type> types, String suffix) {
          if (types != null && types.size() > 0)
              append(sb, prefix, types, suffix);
      }
  
      public interface Visitor<R,P> {
          R visitSimpleType(SimpleType type, P p);
          R visitArrayType(ArrayType type, P p);
          R visitMethodType(MethodType type, P p);
          R visitClassSigType(ClassSigType type, P p);
          R visitClassType(ClassType type, P p);
          R visitTypeParamType(TypeParamType type, P p);
          R visitWildcardType(WildcardType type, P p);
      }
  
      /**
       * Represents a type signature with a simple name. The name may be that of a
       * primitive type, such "{@code int}, {@code float}, etc
       * or that of a type argument, such as {@code T}, {@code K}, {@code V}, etc.
       *
       * See:
       * JVMS 4.3.2
       *      BaseType:
       *          {@code B}, {@code C}, {@code D}, {@code F}, {@code I},
       *          {@code J}, {@code S}, {@code Z};
       *      VoidDescriptor:
       *          {@code V};
       * JVMS 4.3.4
       *      TypeVariableSignature:
       *          {@code T} Identifier {@code ;}
       */
      public static class SimpleType extends Type {
          public SimpleType(String name) {
              this.name = name;
          }
  
          public <R, D> R accept(Visitor<R, D> visitor, D data) {
              return visitor.visitSimpleType(this, data);
          }
  
          public boolean isPrimitiveType() {
              return primitiveTypes.contains(name);
          }
          // where
          private static final Set<String> primitiveTypes = new HashSet<>(Arrays.asList(
              "boolean", "byte", "char", "double", "float", "int", "long", "short", "void"));
  
          @Override
          public String toString() {
              return name;
          }
  
          public final String name;
      }
  
      /**
       * Represents an array type signature.
       *
       * See:
       * JVMS 4.3.4
       *      ArrayTypeSignature:
       *          {@code [} TypeSignature {@code ]}
       */
      public static class ArrayType extends Type {
          public ArrayType(Type elemType) {
              this.elemType = elemType;
          }
  
          public <R, D> R accept(Visitor<R, D> visitor, D data) {
              return visitor.visitArrayType(this, data);
          }
  
          @Override
          public String toString() {
              return elemType + "[]";
          }
  
          public final Type elemType;
      }
  
      /**
       * Represents a method type signature.
       *
       * See;
       * JVMS 4.3.4
       *      MethodTypeSignature:
       *          FormalTypeParameters_opt {@code (} TypeSignature* {@code)} ReturnType
       *              ThrowsSignature*
       */
      public static class MethodType extends Type {
          public MethodType(List<? extends Type> paramTypes, Type resultType) {
              this(null, paramTypes, resultType, null);
          }
  
          public MethodType(List<? extends TypeParamType> typeParamTypes,
                  List<? extends Type> paramTypes,
                  Type returnType,
                  List<? extends Type> throwsTypes) {
              this.typeParamTypes = typeParamTypes;
              this.paramTypes = paramTypes;
              this.returnType = returnType;
              this.throwsTypes = throwsTypes;
          }
  
          public <R, D> R accept(Visitor<R, D> visitor, D data) {
              return visitor.visitMethodType(this, data);
          }
  
          @Override
          public String toString() {
              StringBuilder sb = new StringBuilder();
              appendIfNotEmpty(sb, "<", typeParamTypes, "> ");
              sb.append(returnType);
              append(sb, " (", paramTypes, ")");
              appendIfNotEmpty(sb, " throws ", throwsTypes, "");
              return sb.toString();
          }
  
          public final List<? extends TypeParamType> typeParamTypes;
          public final List<? extends Type> paramTypes;
          public final Type returnType;
          public final List<? extends Type> throwsTypes;
      }
  
      /**
       * Represents a class signature. These describe the signature of
       * a class that has type arguments.
       *
       * See:
       * JVMS 4.3.4
       *      ClassSignature:
       *          FormalTypeParameters_opt SuperclassSignature SuperinterfaceSignature*
       */
      public static class ClassSigType extends Type {
          public ClassSigType(List<TypeParamType> typeParamTypes, Type superclassType,
                  List<Type> superinterfaceTypes) {
              this.typeParamTypes = typeParamTypes;
              this.superclassType = superclassType;
              this.superinterfaceTypes = superinterfaceTypes;
          }
  
          public <R, D> R accept(Visitor<R, D> visitor, D data) {
              return visitor.visitClassSigType(this, data);
          }
  
          @Override
          public String toString() {
              StringBuilder sb = new StringBuilder();
              appendIfNotEmpty(sb, "<", typeParamTypes, ">");
              if (superclassType != null) {
                  sb.append(" extends ");
                  sb.append(superclassType);
              }
              appendIfNotEmpty(sb, " implements ", superinterfaceTypes, "");
              return sb.toString();
          }
  
          public final List<TypeParamType> typeParamTypes;
          public final Type superclassType;
          public final List<Type> superinterfaceTypes;
      }
  
      /**
       * Represents a class type signature. This is used to represent a
       * reference to a class, such as in a field, parameter, return type, etc.
       *
       * See:
       * JVMS 4.3.4
       *      ClassTypeSignature:
       *          {@code L} PackageSpecifier_opt SimpleClassTypeSignature
       *                  ClassTypeSignatureSuffix* {@code ;}
       *      PackageSpecifier:
       *          Identifier {@code /} PackageSpecifier*
       *      SimpleClassTypeSignature:
       *          Identifier TypeArguments_opt }
       *      ClassTypeSignatureSuffix:
       *          {@code .} SimpleClassTypeSignature
       */
      public static class ClassType extends Type {
          public ClassType(ClassType outerType, String name, List<Type> typeArgs) {
              this.outerType = outerType;
              this.name = name;
              this.typeArgs = typeArgs;
          }
  
          public <R, D> R accept(Visitor<R, D> visitor, D data) {
              return visitor.visitClassType(this, data);
          }
  
          public String getBinaryName() {
              if (outerType == null)
                  return name;
              else
                  return (outerType.getBinaryName() + "$" + name);
          }
  
          @Override
          public String toString() {
              StringBuilder sb = new StringBuilder();
              if (outerType != null) {
                  sb.append(outerType);
                  sb.append(".");
              }
              sb.append(name);
              appendIfNotEmpty(sb, "<", typeArgs, ">");
              return sb.toString();
          }
  
          @Override
          public boolean isObject() {
              return (outerType == null)
                      && name.equals("java/lang/Object")
                      && (typeArgs == null || typeArgs.isEmpty());
          }
  
          public final ClassType outerType;
          public final String name;
          public final List<Type> typeArgs;
      }
  
      /**
       * Represents a FormalTypeParameter. These are used to declare the type
       * parameters for generic classes and methods.
       *
       * See:
       * JVMS 4.3.4
       *     FormalTypeParameters:
       *          {@code <} FormalTypeParameter+ {@code >}
       *     FormalTypeParameter:
       *          Identifier ClassBound InterfaceBound*
       *     ClassBound:
       *          {@code :} FieldTypeSignature_opt
       *     InterfaceBound:
       *          {@code :} FieldTypeSignature
       */
      public static class TypeParamType extends Type {
          public TypeParamType(String name, Type classBound, List<Type> interfaceBounds) {
              this.name = name;
              this.classBound = classBound;
              this.interfaceBounds = interfaceBounds;
          }
  
          public <R, D> R accept(Visitor<R, D> visitor, D data) {
              return visitor.visitTypeParamType(this, data);
          }
  
          @Override
          public String toString() {
              StringBuilder sb = new StringBuilder();
              sb.append(name);
              String sep = " extends ";
              if (classBound != null) {
                  sb.append(sep);
                  sb.append(classBound);
                  sep = " & ";
              }
              if (interfaceBounds != null) {
                  for (Type bound: interfaceBounds) {
                      sb.append(sep);
                      sb.append(bound);
                      sep = " & ";
                  }
              }
              return sb.toString();
          }
  
          public final String name;
          public final Type classBound;
          public final List<Type> interfaceBounds;
      }
  
      /**
       * Represents a wildcard type argument.  A type argument that is not a
       * wildcard type argument will be represented by a ClassType, ArrayType, etc.
       *
       * See:
       * JVMS 4.3.4
       *      TypeArgument:
       *          WildcardIndicator_opt FieldTypeSignature
       *          {@code *}
       *      WildcardIndicator:
       *          {@code +}
       *          {@code -}
       */
      public static class WildcardType extends Type {
          public enum Kind { UNBOUNDED, EXTENDS, SUPER }
  
          public WildcardType() {
              this(Kind.UNBOUNDED, null);
          }
          public WildcardType(Kind kind, Type boundType) {
              this.kind = kind;
              this.boundType = boundType;
          }
  
          public <R, D> R accept(Visitor<R, D> visitor, D data) {
              return visitor.visitWildcardType(this, data);
          }
  
          @Override
          public String toString() {
              switch (kind) {
                  case UNBOUNDED:
                      return "?";
                  case EXTENDS:
                      return "? extends " + boundType;
                  case SUPER:
                      return "? super " + boundType;
                  default:
                      throw new AssertionError();
              }
          }
  
          public final Kind kind;
          public final Type boundType;
      }
  }
  