// Path: lang/java/src/main/java/com/google/devtools/build/bfg/ReferencedClassesParser.java
// @AutoValue
// public abstract static class Metadata {
//   static final Metadata EMPTY = create(0, 0, false);
// 
//   /** Line where a simple/qualified name starts. */
//   public abstract int line();
// 
//   /** Column where a simple/qualified name starts. */
//   public abstract int column();
// 
//   public abstract boolean isMethod();
// 
//   public static Metadata create(int line, int column, boolean isMethod) {
//     return new AutoValue_ReferencedClassesParser_Metadata(line, column, isMethod);
//   }
// }
// 
// Path: lang/java/src/main/java/com/google/devtools/build/bfg/ReferencedClassesParser.java
// @AutoValue
// public abstract static class QualifiedName {
//   public static final Pattern CLASS_NAME_PATTERN = Pattern.compile("^[A-Z][a-zA-Z0-9_$]*$");
// 
//   public static QualifiedName create(String value, Metadata metadata) {
//     return new AutoValue_ReferencedClassesParser_QualifiedName(value, metadata);
//   }
// 
//   public abstract String value();
// 
//   public abstract Metadata metadata();
// 
//   @Memoized
//   public ImmutableList<String> parts() {
//     return ImmutableList.copyOf(DOT_SPLITTER.split(value()));
//   }
// 
//   private static int findTopLevelName(List<String> parts) {
//     for (int i = 0; i < parts.size(); i++) {
//       String part = parts.get(i);
//       if (CLASS_NAME_PATTERN.matcher(part).matches()) {
//         // if i == 0, 's' starts with a capitalized-camel name,
//         // e.g., "MobileLocalDetailsJslayoutProto".
//         return i > 0 ? i : -1;
//       }
//       if (isLowerCase(part)) {
//         continue;
//       }
//       return -1;
//     }
//     return -1;
//   }
// 
//   /**
//    * "java.util.Map" => "java.util.Map" "java.util.Map.Entry" => "java.util.Map"
//    * "org.mockito.Mockito.mock" => "org.mockito.Mockito"
//    */
//   public QualifiedName getTopLevelQualifiedName() {
//     return create(DOT_JOINER.join(parts().subList(0, findTopLevelName(parts()) + 1)), metadata());
//   }
// }
// 
// Path: lang/java/src/main/java/com/google/devtools/build/bfg/ReferencedClassesParser.java
// @AutoValue
// public abstract static class SimpleName {
//   public abstract String value();
// 
//   public abstract Metadata metadata();
// 
//   public static SimpleName create(String value, Metadata metadata) {
//     return new AutoValue_ReferencedClassesParser_SimpleName(value, metadata);
//   }
// }


import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.Iterables.any;
import static com.google.common.collect.Iterables.getLast;
import static com.google.common.collect.Iterables.getOnlyElement;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.file.Files.readAllBytes;
import com.google.common.base.Strings;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Ordering;
import com.google.common.graph.GraphBuilder;
import com.google.common.graph.ImmutableGraph;
import com.google.common.graph.MutableGraph;
import com.google.devtools.build.bfg.ReferencedClassesParser.Metadata;
import com.google.devtools.build.bfg.ReferencedClassesParser.QualifiedName;
import com.google.devtools.build.bfg.ReferencedClassesParser.SimpleName;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Type;
        getOnlyElement((List<SingleVariableDeclaration>) methodDeclaration.parameters());
    IVariableBinding vb = pt.resolveBinding();
    if (vb == null) {
      return false;
    }
    ITypeBinding tb = vb.getType();
    return tb != null && "java.lang.String[]".equals(tb.getQualifiedName());
  }

  /** Create a cycle in 'graph' comprised of classes from 'classes'. */
  private static void putOnCycle(Collection<String> classes, MutableGraph<String> graph) {
    if (classes.size() == 1) {
      return;
    }

    ImmutableList<String> sortedClasses = Ordering.natural().immutableSortedCopy(classes);

    for (int i = 1; i < sortedClasses.size(); i++) {
      graph.putEdge(sortedClasses.get(i - 1), sortedClasses.get(i));
    }
    graph.putEdge(getLast(sortedClasses), sortedClasses.get(0));
  }

  /**
   * Given an arbitrary class name, if the class name is fully qualified, then it returns the fully
   * qualified name of the top level class. If the fully qualified name has no package, or if it is
   * a simple name, then it simply returns the top level class name.
   */
  private static String stripInnerClassFromName(String className) {
    QualifiedName topLevelQualifiedName =
// Path: lang/java/src/main/java/com/google/devtools/build/bfg/ReferencedClassesParser.java
// @AutoValue
// public abstract static class Metadata {
//   static final Metadata EMPTY = create(0, 0, false);
// 
//   /** Line where a simple/qualified name starts. */
//   public abstract int line();
// 
//   /** Column where a simple/qualified name starts. */
//   public abstract int column();
// 
//   public abstract boolean isMethod();
// 
//   public static Metadata create(int line, int column, boolean isMethod) {
//     return new AutoValue_ReferencedClassesParser_Metadata(line, column, isMethod);
//   }
// }
// 
// Path: lang/java/src/main/java/com/google/devtools/build/bfg/ReferencedClassesParser.java
// @AutoValue
// public abstract static class QualifiedName {
//   public static final Pattern CLASS_NAME_PATTERN = Pattern.compile("^[A-Z][a-zA-Z0-9_$]*$");
// 
//   public static QualifiedName create(String value, Metadata metadata) {
//     return new AutoValue_ReferencedClassesParser_QualifiedName(value, metadata);
//   }
// 
//   public abstract String value();
// 
//   public abstract Metadata metadata();
// 
//   @Memoized
//   public ImmutableList<String> parts() {
//     return ImmutableList.copyOf(DOT_SPLITTER.split(value()));
//   }
// 
//   private static int findTopLevelName(List<String> parts) {
//     for (int i = 0; i < parts.size(); i++) {
//       String part = parts.get(i);
//       if (CLASS_NAME_PATTERN.matcher(part).matches()) {
//         // if i == 0, 's' starts with a capitalized-camel name,
//         // e.g., "MobileLocalDetailsJslayoutProto".
//         return i > 0 ? i : -1;
//       }
//       if (isLowerCase(part)) {
//         continue;
//       }
//       return -1;
//     }
//     return -1;
//   }
// 
//   /**
//    * "java.util.Map" => "java.util.Map" "java.util.Map.Entry" => "java.util.Map"
//    * "org.mockito.Mockito.mock" => "org.mockito.Mockito"
//    */
//   public QualifiedName getTopLevelQualifiedName() {
//     return create(DOT_JOINER.join(parts().subList(0, findTopLevelName(parts()) + 1)), metadata());
//   }
// }
// 
// Path: lang/java/src/main/java/com/google/devtools/build/bfg/ReferencedClassesParser.java
// @AutoValue
// public abstract static class SimpleName {
//   public abstract String value();
// 
//   public abstract Metadata metadata();
// 
//   public static SimpleName create(String value, Metadata metadata) {
//     return new AutoValue_ReferencedClassesParser_SimpleName(value, metadata);
//   }
// }


// Path: lang/java/src/main/java/com/google/devtools/build/bfg/JavaSourceFileParser.java
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.Iterables.any;
import static com.google.common.collect.Iterables.getLast;
import static com.google.common.collect.Iterables.getOnlyElement;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.file.Files.readAllBytes;
import com.google.common.base.Strings;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Ordering;
import com.google.common.graph.GraphBuilder;
import com.google.common.graph.ImmutableGraph;
import com.google.common.graph.MutableGraph;
import com.google.devtools.build.bfg.ReferencedClassesParser.Metadata;
import com.google.devtools.build.bfg.ReferencedClassesParser.QualifiedName;
import com.google.devtools.build.bfg.ReferencedClassesParser.SimpleName;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Type;

        getOnlyElement((List<SingleVariableDeclaration>) methodDeclaration.parameters());
    IVariableBinding vb = pt.resolveBinding();
    if (vb == null) {
      return false;
    }
    ITypeBinding tb = vb.getType();
    return tb != null && "java.lang.String[]".equals(tb.getQualifiedName());
  }

  /** Create a cycle in 'graph' comprised of classes from 'classes'. */
  private static void putOnCycle(Collection<String> classes, MutableGraph<String> graph) {
    if (classes.size() == 1) {
      return;
    }

    ImmutableList<String> sortedClasses = Ordering.natural().immutableSortedCopy(classes);

    for (int i = 1; i < sortedClasses.size(); i++) {
      graph.putEdge(sortedClasses.get(i - 1), sortedClasses.get(i));
    }
    graph.putEdge(getLast(sortedClasses), sortedClasses.get(0));
  }

  /**
   * Given an arbitrary class name, if the class name is fully qualified, then it returns the fully
   * qualified name of the top level class. If the fully qualified name has no package, or if it is
   * a simple name, then it simply returns the top level class name.
   */
  private static String stripInnerClassFromName(String className) {
    QualifiedName topLevelQualifiedName =
        QualifiedName.create(className, Metadata.EMPTY).getTopLevelQualifiedName();