gt:             this(0, classType, className, parent, mods);
context: /*
   * Copyright (c) 2015, 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.
   *
   * 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.
   */
  
  import java.lang.annotation.RetentionPolicy;
  import java.util.*;
  import java.util.stream.Collectors;
  
  public class TestCase {
  
      /**
       * The top-level classes of the test case.
       */
      public final Map<String, TestClassInfo> classes = new LinkedHashMap<>();
  
      /**
       * Constructs a test class info with {@code classType} as top-level class,
       * with {@code outerClassName} as name and {@code mods} as modifiers.
       *
       * @param classType a class type
       * @param outerClassName a name
       * @param mods an array of modifiers
       */
      public TestClassInfo addClassInfo(ClassType classType, String outerClassName, String...mods) {
          return addClassInfo(null, classType, outerClassName, mods);
      }
  
      /**
       * Constructs a test class info with {@code classType} as top-level class,
       * with {@code outerClassName} as name, {@code parent} class name
       * as parent class and {@code mods} as modifiers.
       *
       * @param classType a class type
       * @param outerClassName a name
       * @param mods an array of modifiers
       */
      public TestClassInfo addClassInfo(String parent, ClassType classType, String outerClassName, String...mods) {
          TestClassInfo clazz = new TestClassInfo(classType, outerClassName, parent, mods);
          if (classes.put(outerClassName, clazz) != null) {
              throw new IllegalArgumentException("Duplicate class name: " + outerClassName);
          }
          return clazz;
      }
  
      public String generateSource() {
          return classes.values().stream()
                  .map(TestMemberInfo::generateSource)
                  .collect(Collectors.joining("\n"));
      }
  
      /**
       * Returns {@code TestClassInfo} by class signature.
       * Example, {@code getTestClassInfo(&quot;Test$1Local&quot;)}
       * returns local inner class of class {@code Test}.
       *
       * @param classSignature a class signature
       * @return {@code TestClassInfo} by class signature
       */
      public TestClassInfo getTestClassInfo(String classSignature) {
          String[] cs = classSignature.split("\\$");
          if (cs.length > 0 && classes.containsKey(cs[0])) {
              // check signature corresponds to top level class
              if (cs.length == 1) {
                  return classes.get(cs[0]);
              }
          } else {
              throw new IllegalArgumentException("Cannot find class : " + classSignature);
          }
          TestClassInfo current = classes.get(cs[0]);
          // find class info in the inner classes
          for (int i = 1; i < cs.length; ++i) {
              Map<String, TestClassInfo> innerClasses = current.innerClasses;
              Map<String, TestMethodInfo> methods = current.methods;
              current = innerClasses.get(cs[i]);
              // if current is null then class info does not exist or the class is local
              if (current == null) {
                  if (!cs[i].isEmpty()) {
                      // the class is local, remove leading digit
                      String className = cs[i].substring(1);
                      Optional<TestClassInfo> opt = methods.values().stream()
                              .flatMap(c -> c.localClasses.values().stream())
                              .filter(c -> c.name.equals(className)).findAny();
                      if (opt.isPresent()) {
                          current = opt.get();
                          // continue analysis of local class
                          continue;
                      }
                  }
                  throw new IllegalArgumentException("Cannot find class : " + classSignature);
              }
          }
          return current;
      }
  
      /**
       * Class represents a program member.
       */
      public static abstract class TestMemberInfo {
          // next two fields are used for formatting
          protected final int indention;
          protected final ClassType containerType;
          public final List<String> mods;
          public final String name;
          public final Map<String, TestAnnotationInfo> annotations;
  
          TestMemberInfo(int indention, ClassType containerType, String name, String... mods) {
              this.indention = indention;
              this.containerType = containerType;
              this.mods = Arrays.asList(mods);
              this.name = name;
              this.annotations = new HashMap<>();
          }
  
          public abstract String generateSource();
  
          public boolean isAnnotated(RetentionPolicy policy) {
              return annotations.values().stream()
                      .filter(a -> a.policy == policy)
                      .findAny().isPresent();
          }
  
          public Set<String> getRuntimeVisibleAnnotations() {
              return getRuntimeAnnotations(RetentionPolicy.RUNTIME);
          }
  
          public Set<String> getRuntimeInvisibleAnnotations() {
              return getRuntimeAnnotations(RetentionPolicy.CLASS);
          }
  
          private Set<String> getRuntimeAnnotations(RetentionPolicy policy) {
              return annotations.values().stream()
                      .filter(e -> e.policy == policy)
                      .map(a -> a.annotationName)
                      .distinct()
                      .collect(Collectors.toSet());
          }
  
          /**
           * Generates source for annotations.
           *
           * @param prefix a leading text
           * @param suffix a trailing text
           * @param joining a text between annotations
           * @return source for annotations
           */
          protected String generateSourceForAnnotations(String prefix, String suffix, String joining) {
              StringBuilder sb = new StringBuilder();
              for (TestAnnotationInfo annotation : annotations.values()) {
                  sb.append(prefix);
                  if (annotation.isContainer) {
                      // the annotation is repeatable
                      // container consists of an array of annotations
                      TestAnnotationInfo.TestArrayElementValue containerElementValue =
                              (TestAnnotationInfo.TestArrayElementValue) annotation.elementValues.get(0).elementValue;
                      // concatenate sources of repeatable annotations
                      sb.append(containerElementValue.values.stream()
                              .map(TestAnnotationInfo.TestElementValue::toString)
                              .collect(Collectors.joining(joining)));
                  } else {
                      sb.append(annotation);
                  }
                  sb.append(suffix);
              }
              String src = sb.toString();
              return src.trim().isEmpty() ? "" : src;
  
          }
  
          /**
           * Generates source for annotations.
           *
           * @return source for annotations
           */
          public String generateSourceForAnnotations() {
              return generateSourceForAnnotations(indention(), "\n", "\n" + indention());
          }
  
          /**
           * Adds annotation info to the member.
           *
           * @param anno an annotation info
           */
          public void addAnnotation(TestAnnotationInfo anno) {
              String containerName = anno.annotationName + "Container";
              TestAnnotationInfo annotation = annotations.get(anno.annotationName);
              TestAnnotationInfo containerAnnotation = annotations.get(containerName);
  
              if (annotation == null) {
                  // if annotation is null then either it is first adding of the annotation to the member
                  // or there is the container of the annotation.
                  if (containerAnnotation == null) {
                      // first adding to the member
                      annotations.put(anno.annotationName, anno);
                  } else {
                      // add annotation to container
                      TestAnnotationInfo.TestArrayElementValue containerElementValue =
                              ((TestAnnotationInfo.TestArrayElementValue) containerAnnotation.elementValues.get(0).elementValue);
                      containerElementValue.values.add(new TestAnnotationInfo.TestAnnotationElementValue(anno.annotationName, anno));
                  }
              } else {
                  // remove previously added annotation and add new container of repeatable annotation
                  // which contains previously added and new annotation
                  annotations.remove(anno.annotationName);
                  containerAnnotation = new TestAnnotationInfo(
                          containerName,
                          anno.policy,
                          true,
                          new TestAnnotationInfo.Pair("value",
                                  new TestAnnotationInfo.TestArrayElementValue(
                                          new TestAnnotationInfo.TestAnnotationElementValue(anno.annotationName, annotation),
                                          new TestAnnotationInfo.TestAnnotationElementValue(anno.annotationName, anno))));
                  annotations.put(containerName, containerAnnotation);
              }
          }
  
          public String indention() {
              char[] a = new char[4 * indention];
              Arrays.fill(a, ' ');
              return new String(a);
          }
  
          public String getName() {
              return name;
          }
      }
  
      /**
       * The class represents a class.
       */
      public static class TestClassInfo extends TestMemberInfo {
          public final ClassType classType;
          public final String parent;
          public final Map<String, TestClassInfo> innerClasses;
          public final Map<String, TestMethodInfo> methods;
          public final Map<String, TestFieldInfo> fields;
  
          TestClassInfo(int indention, ClassType classType, String className, String... mods) {
              this(indention, classType, className, null, mods);
          }
  
          TestClassInfo(ClassType classType, String className, String parent, String... mods) {
  