diff --git a/java-ast/addon/src/main/java/org/jboss/windup/ast/java/ASTException.java b/java-ast/addon/src/main/java/org/jboss/windup/ast/java/ASTException.java
index 8b36ab86f2..e272e09600 100644
--- a/java-ast/addon/src/main/java/org/jboss/windup/ast/java/ASTException.java
+++ b/java-ast/addon/src/main/java/org/jboss/windup/ast/java/ASTException.java
@@ -1,7 +1,7 @@
package org.jboss.windup.ast.java;
/**
- * Thrown due to errors parsing Java source with the {@link ASTProcessor}
+ * Thrown due to errors parsing Java source with the {@link ASTReferenceResolver}
*
* @author Jesse Sightler
*
@@ -10,24 +10,28 @@ public class ASTException extends RuntimeException
{
private static final long serialVersionUID = 1L;
+ /**
+ * Create an exception.
+ */
public ASTException()
{
super();
}
+ /**
+ * Create an exception with the given message and cause.
+ */
public ASTException(String message, Throwable cause)
{
super(message, cause);
}
+ /**
+ * Create an exception with the given message.
+ */
public ASTException(String message)
{
super(message);
}
- public ASTException(Throwable cause)
- {
- super(cause);
- }
-
}
diff --git a/java-ast/addon/src/main/java/org/jboss/windup/ast/java/ASTProcessor.java b/java-ast/addon/src/main/java/org/jboss/windup/ast/java/ASTProcessor.java
index d411918cca..83263eaa61 100644
--- a/java-ast/addon/src/main/java/org/jboss/windup/ast/java/ASTProcessor.java
+++ b/java-ast/addon/src/main/java/org/jboss/windup/ast/java/ASTProcessor.java
@@ -2,1145 +2,62 @@
import java.io.IOException;
import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.Stack;
-import java.util.logging.Logger;
import org.apache.commons.io.FileUtils;
-import org.apache.commons.lang3.StringUtils;
+import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
-import org.eclipse.jdt.core.dom.ASTVisitor;
-import org.eclipse.jdt.core.dom.Annotation;
-import org.eclipse.jdt.core.dom.ArrayInitializer;
-import org.eclipse.jdt.core.dom.BooleanLiteral;
-import org.eclipse.jdt.core.dom.CastExpression;
-import org.eclipse.jdt.core.dom.CharacterLiteral;
-import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.CompilationUnit;
-import org.eclipse.jdt.core.dom.Expression;
-import org.eclipse.jdt.core.dom.FieldAccess;
-import org.eclipse.jdt.core.dom.FieldDeclaration;
-import org.eclipse.jdt.core.dom.IMethodBinding;
-import org.eclipse.jdt.core.dom.ITypeBinding;
-import org.eclipse.jdt.core.dom.ImportDeclaration;
-import org.eclipse.jdt.core.dom.InfixExpression;
-import org.eclipse.jdt.core.dom.InstanceofExpression;
-import org.eclipse.jdt.core.dom.MarkerAnnotation;
-import org.eclipse.jdt.core.dom.MemberValuePair;
-import org.eclipse.jdt.core.dom.MethodDeclaration;
-import org.eclipse.jdt.core.dom.MethodInvocation;
-import org.eclipse.jdt.core.dom.NormalAnnotation;
-import org.eclipse.jdt.core.dom.NumberLiteral;
-import org.eclipse.jdt.core.dom.PackageDeclaration;
-import org.eclipse.jdt.core.dom.QualifiedName;
-import org.eclipse.jdt.core.dom.ReturnStatement;
-import org.eclipse.jdt.core.dom.SimpleName;
-import org.eclipse.jdt.core.dom.SimpleType;
-import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
-import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
-import org.eclipse.jdt.core.dom.StringLiteral;
-import org.eclipse.jdt.core.dom.Type;
-import org.eclipse.jdt.core.dom.TypeDeclaration;
-import org.eclipse.jdt.core.dom.TypeLiteral;
-import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
-import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
import org.jboss.windup.ast.java.data.ClassReference;
-import org.jboss.windup.ast.java.data.ClassReferences;
-import org.jboss.windup.ast.java.data.TypeReferenceLocation;
-import org.jboss.windup.ast.java.data.annotations.AnnotationArrayValue;
-import org.jboss.windup.ast.java.data.annotations.AnnotationClassReference;
-import org.jboss.windup.ast.java.data.annotations.AnnotationLiteralValue;
-import org.jboss.windup.ast.java.data.annotations.AnnotationValue;
/**
- * Provides the ability to parse a Java file and return a {@link ClassReferences} object containing the fully qualified names of all of the contained
- * references.
- *
* @author Jesse Sightler
- *
*/
-public class ASTProcessor extends ASTVisitor
-{
- private static Logger LOG = Logger.getLogger(ASTProcessor.class.getName());
-
- private final WildcardImportResolver wildcardImportResolver;
- private CompilationUnit cu;
- private Path javaFile;
- private ASTParser parser;
-
- private final Set libraryPaths;
- private final Set sourcePaths;
-
- /**
- * Contains all wildcard imports (import com.example.*) lines from the source file.
- *
- * These are used for type resolution throughout the class.
- */
- private final List wildcardImports = new ArrayList<>();
-
- /**
- * Indicates that we have already attempted to query the graph for this particular shortname. The shortname will exist here even if no results
- * were found.
- */
- private final Set classNameLookedUp = new HashSet<>();
-
- /**
- * Contains a map of class short names (eg, MyClass) to qualified names (eg, com.example.MyClass)
- */
- private final Map classNameToFQCN = new HashMap<>();
- /**
- * Maintains a set of all variable names that have been resolved
- */
- private final Set names = new HashSet();
-
- /**
- * Maintains a map of nameInstances to fully qualified class names.
- */
- private final Map nameInstance = new HashMap();
-
- private ClassReferences classReferences;
-
+public class ASTProcessor {
/**
* Processes a java file using the default {@link WildcardImportResolver}.
- *
- * See also: {@see JavaASTProcessor#analyzeJavaFile(WildcardImportResolver, Set, Set, Path)}
+ *
+ * See also: {@see JavaASTProcessor#analyze(WildcardImportResolver, Set, Set, Path)}
*/
- public static ClassReferences analyzeJavaFile(Set libraryPaths, Set sourcePaths, Path sourceFile)
+ public static List analyze(Set libraryPaths, Set sourcePaths, Path sourceFile)
{
- return new ASTProcessor(new NoopWildcardImportResolver(), libraryPaths, sourcePaths).analyzeFile(sourceFile);
+ return analyze(new NoopWildcardImportResolver(), libraryPaths, sourcePaths, sourceFile);
}
/**
* Parses the provided file, using the given libraryPaths and sourcePaths as context. The libraries may be either jar files or references to
* directories containing class files.
- *
+ *
* The sourcePaths must be a reference to the top level directory for sources (eg, for a file src/main/java/org/example/Foo.java, the source path
* would be src/main/java).
- *
+ *
* The wildcard resolver provides a fallback for processing wildcard imports that the underlying parser was unable to resolve.
*/
- public static ClassReferences analyzeJavaFile(WildcardImportResolver importResolver, Set libraryPaths, Set sourcePaths,
- Path sourceFile)
+ public static List analyze(WildcardImportResolver importResolver, Set libraryPaths, Set sourcePaths,
+ Path sourceFile)
{
- return new ASTProcessor(importResolver, libraryPaths, sourcePaths).analyzeFile(sourceFile);
- }
-
- public ASTProcessor(WildcardImportResolver importResolver, Set libraryPaths, Set sourcePaths)
- {
- this.wildcardImportResolver = importResolver;
- this.parser = ASTParser.newParser(AST.JLS8);
- this.libraryPaths = libraryPaths;
- this.sourcePaths = sourcePaths;
- }
-
- public ClassReferences analyzeFile(Path javaFile)
- {
- this.classReferences = new ClassReferences();
- this.wildcardImports.clear();
- this.classNameLookedUp.clear();
- this.classNameToFQCN.clear();
- this.names.clear();
- this.nameInstance.clear();
- this.javaFile = javaFile;
-
+ ASTParser parser = ASTParser.newParser(AST.JLS8);
parser.setEnvironment(libraryPaths.toArray(new String[libraryPaths.size()]), sourcePaths.toArray(new String[sourcePaths.size()]), null, true);
parser.setBindingsRecovery(false);
parser.setResolveBindings(true);
-
- String fileName = javaFile.getFileName().toString();
+ Map options = JavaCore.getOptions();
+ JavaCore.setComplianceOptions(JavaCore.VERSION_1_8, options);
+ parser.setCompilerOptions(options);
+ String fileName = sourceFile.getFileName().toString();
parser.setUnitName(fileName);
try
{
- parser.setSource(FileUtils.readFileToString(javaFile.toFile()).toCharArray());
+ parser.setSource(FileUtils.readFileToString(sourceFile.toFile()).toCharArray());
}
catch (IOException e)
{
- throw new ASTException("Failed to get source for file: " + javaFile.toString() + " due to: " + e.getMessage(), e);
+ throw new ASTException("Failed to get source for file: " + sourceFile.toString() + " due to: " + e.getMessage(), e);
}
parser.setKind(ASTParser.K_COMPILATION_UNIT);
- this.cu = (CompilationUnit) parser.createAST(null);
-
- PackageDeclaration packageDeclaration = cu.getPackage();
- String packageName = packageDeclaration == null ? "" : packageDeclaration.getName().getFullyQualifiedName();
- @SuppressWarnings("unchecked")
- List types = cu.types();
- String fqcn = null;
- if (!types.isEmpty())
- {
- TypeDeclaration typeDeclaration = (TypeDeclaration) types.get(0);
- String className = typeDeclaration.getName().getFullyQualifiedName();
-
- if (packageName.equals(""))
- {
- fqcn = className;
- }
- else
- {
- fqcn = packageName + "." + className;
- }
- this.classReferences.addReference(new ClassReference(fqcn, TypeReferenceLocation.TYPE, cu.getLineNumber(typeDeclaration
- .getStartPosition()), cu.getColumnNumber(cu.getStartPosition()), cu.getLength(), extractDefinitionLine(typeDeclaration
- .toString())));
-
- Type superclassType = typeDeclaration.getSuperclassType();
- ITypeBinding resolveBinding = null;
- if (superclassType != null)
- {
- resolveBinding = superclassType.resolveBinding();
- }
-
- while (resolveBinding != null)
- {
- if (superclassType.resolveBinding() != null)
- {
- this.classReferences.addReference(new ClassReference(resolveBinding.getQualifiedName(), TypeReferenceLocation.TYPE, cu
- .getLineNumber(typeDeclaration
- .getStartPosition()), cu.getColumnNumber(cu.getStartPosition()), cu.getLength(),
- extractDefinitionLine(typeDeclaration.toString())));
- }
- resolveBinding = resolveBinding.getSuperclass();
- }
- }
-
- this.names.add("this");
- this.nameInstance.put("this", fqcn);
-
- cu.accept(this);
- return this.classReferences;
- }
-
- private String extractDefinitionLine(String typeDeclaration)
- {
- String typeLine = "";
- String[] lines = typeDeclaration.split("\n");
- for (String line : lines)
- {
- typeLine = line;
- if (line.contains("{"))
- {
- break;
- }
- }
- return typeLine;
- }
-
- public ClassReferences getJavaClassReferences()
- {
- return this.classReferences;
- }
-
- private void processConstructor(ConstructorType interest, int lineNumber, int columnNumber, int length, String line)
- {
- String text = interest.toString();
- this.classReferences.addReference(new ClassReference(text, TypeReferenceLocation.CONSTRUCTOR_CALL, lineNumber, columnNumber, length,
- line));
- }
-
- private void processMethod(MethodType interest, TypeReferenceLocation location, int lineNumber, int columnNumber, int length, String line)
- {
- String text = interest.toString();
- this.classReferences.addReference(new ClassReference(text, location, lineNumber, columnNumber, length, line));
- }
-
- private void processImport(String interest, int lineNumber, int columnNumber, int length, String line)
- {
- this.classReferences.addReference(new ClassReference(interest, TypeReferenceLocation.IMPORT, lineNumber, columnNumber, length, line));
- }
-
- private ClassReference processTypeBinding(ITypeBinding type, TypeReferenceLocation referenceLocation, int lineNumber, int columnNumber,
- int length, String line)
- {
- if (type == null)
- return null;
- String sourceString = type.getQualifiedName();
- return processTypeAsString(sourceString, referenceLocation, lineNumber, columnNumber, length, line);
- }
-
- /**
- * The method determines if the type can be resolved and if not, will try to guess the qualified name using the information from the imports.
- */
- private ClassReference processType(Type type, TypeReferenceLocation typeReferenceLocation, int lineNumber, int columnNumber, int length,
- String line)
- {
- if (type == null)
- return null;
-
- ITypeBinding resolveBinding = type.resolveBinding();
- if (resolveBinding == null)
- {
- return processTypeAsString(resolveClassname(type.toString()), typeReferenceLocation, lineNumber,
- columnNumber, length, line);
- }
- else
- {
- return processTypeBinding(type.resolveBinding(), typeReferenceLocation, lineNumber,
- columnNumber, length, line);
- }
-
- }
-
- private ClassReference processTypeAsString(String sourceString, TypeReferenceLocation referenceLocation, int lineNumber,
- int columnNumber, int length, String line)
- {
- if (sourceString == null)
- return null;
- line = line.replaceAll("(\\n)|(\\r)", "");
- ClassReference typeRef = new ClassReference(sourceString, referenceLocation, lineNumber, columnNumber, length, line);
- this.classReferences.addReference(typeRef);
- return typeRef;
- }
-
- @Override
- public boolean visit(MethodDeclaration node)
- {
- //register method return type
- IMethodBinding resolveBinding = node.resolveBinding();
- ITypeBinding returnType = null;
- if (resolveBinding != null)
- {
- returnType = node.resolveBinding().getReturnType();
- }
- if (returnType != null)
- {
- processTypeBinding(returnType, TypeReferenceLocation.RETURN_TYPE, cu.getLineNumber(node.getStartPosition()),
- cu.getColumnNumber(node.getStartPosition()), node.getLength(), extractDefinitionLine(node.toString()));
- }
- else
- {
- Type methodReturnType = node.getReturnType2();
- processType(methodReturnType, TypeReferenceLocation.RETURN_TYPE, cu.getLineNumber(node.getStartPosition()),
- cu.getColumnNumber(node.getStartPosition()), node.getLength(), extractDefinitionLine(node.toString()));
- }
- // register parameters and register them for next processing
- List qualifiedArguments = new ArrayList();
- @SuppressWarnings("unchecked")
- List parameters = (List) node.parameters();
- if (parameters != null)
- {
- for (SingleVariableDeclaration type : parameters)
- {
- this.names.add(type.getName().toString());
- String typeName = type.getType().toString();
- typeName = resolveClassname(typeName);
- qualifiedArguments.add(typeName);
- this.nameInstance.put(type.getName().toString(), typeName);
- processType(type.getType(), TypeReferenceLocation.METHOD_PARAMETER, cu.getLineNumber(node.getStartPosition()),
- cu.getColumnNumber(node.getStartPosition()), node.getLength(), extractDefinitionLine(node.toString()));
- }
- }
- // register thow declarations
- @SuppressWarnings("unchecked")
- List throwsTypes = node.thrownExceptionTypes();
- if (throwsTypes != null)
- {
- for (Type type : throwsTypes)
- {
- processType(type, TypeReferenceLocation.THROWS_METHOD_DECLARATION,
- cu.getLineNumber(node.getStartPosition()),
- cu.getColumnNumber(type.getStartPosition()), type.getLength(), extractDefinitionLine(node.toString()));
- }
- }
-
- // register method declaration
- MethodType methodCall = new MethodType(nameInstance.get("this"), node.getName().toString(), qualifiedArguments);
- processMethod(methodCall, TypeReferenceLocation.METHOD, cu.getLineNumber(node.getName().getStartPosition()),
- cu.getColumnNumber(node.getName().getStartPosition()), node.getName().getLength(), extractDefinitionLine(node.toString()));
- return super.visit(node);
- }
-
- @Override
- public boolean visit(InstanceofExpression node)
- {
- Type type = node.getRightOperand();
- processType(type, TypeReferenceLocation.INSTANCE_OF, cu.getLineNumber(node.getStartPosition()),
- cu.getColumnNumber(type.getStartPosition()), type.getLength(), node.toString());
-
- return super.visit(node);
- }
-
- public boolean visit(org.eclipse.jdt.core.dom.ThrowStatement node)
- {
- if (node.getExpression() instanceof ClassInstanceCreation)
- {
- ClassInstanceCreation cic = (ClassInstanceCreation) node.getExpression();
- Type type = cic.getType();
- processType(type, TypeReferenceLocation.THROW_STATEMENT, cu.getLineNumber(node.getStartPosition()),
- cu.getColumnNumber(cic.getStartPosition()), cic.getLength(), node.toString());
- }
-
- return super.visit(node);
- }
-
- public boolean visit(org.eclipse.jdt.core.dom.CatchClause node)
- {
- Type catchType = node.getException().getType();
- processType(catchType, TypeReferenceLocation.CATCH_EXCEPTION_STATEMENT, cu.getLineNumber(node.getStartPosition()),
- cu.getColumnNumber(catchType.getStartPosition()), catchType.getLength(), node.toString());
-
- return super.visit(node);
- }
-
- @Override
- public boolean visit(ReturnStatement node)
- {
- if (node.getExpression() instanceof ClassInstanceCreation)
- {
- ClassInstanceCreation cic = (ClassInstanceCreation) node.getExpression();
- processTypeBinding(cic.getType().resolveBinding(), TypeReferenceLocation.CONSTRUCTOR_CALL, cu.getLineNumber(node.getStartPosition()),
- cu.getColumnNumber(cic.getStartPosition()), cic.getLength(), node.toString());
- }
- return super.visit(node);
- }
-
- @Override
- public boolean visit(FieldDeclaration node)
- {
- for (int i = 0; i < node.fragments().size(); ++i)
- {
- String nodeType = node.getType().toString();
- nodeType = resolveClassname(nodeType);
- VariableDeclarationFragment frag = (VariableDeclarationFragment) node.fragments().get(i);
- frag.resolveBinding();
- this.names.add(frag.getName().getIdentifier());
- this.nameInstance.put(frag.getName().toString(), nodeType.toString());
-
- processTypeBinding(node.getType().resolveBinding(), TypeReferenceLocation.FIELD_DECLARATION, cu.getLineNumber(node.getStartPosition()),
- cu.getColumnNumber(node.getStartPosition()), node.getLength(), node.toString());
- }
- return true;
- }
-
- private AnnotationValue getAnnotationValueForExpression(Expression expression)
- {
- AnnotationValue value;
- if (expression instanceof BooleanLiteral)
- value = new AnnotationLiteralValue(boolean.class, ((BooleanLiteral) expression).booleanValue());
- else if (expression instanceof CharacterLiteral)
- value = new AnnotationLiteralValue(char.class, ((CharacterLiteral) expression).charValue());
- else if (expression instanceof NumberLiteral)
- {
- ITypeBinding binding = expression.resolveTypeBinding();
- if (binding == null)
- {
- value = new AnnotationLiteralValue(String.class, expression.toString());
- }
- else
- {
- value = new AnnotationLiteralValue(resolveLiteralType(binding), ((NumberLiteral) expression).resolveConstantExpressionValue());
- }
- }
- else if (expression instanceof TypeLiteral)
- value = new AnnotationLiteralValue(Class.class, ((TypeLiteral) expression).resolveConstantExpressionValue());
- else if (expression instanceof StringLiteral)
- value = new AnnotationLiteralValue(String.class, ((StringLiteral) expression).getLiteralValue());
- else if (expression instanceof NormalAnnotation)
- value = processAnnotation((NormalAnnotation) expression);
- else if (expression instanceof ArrayInitializer)
- {
- ArrayInitializer arrayInitializer = (ArrayInitializer) expression;
- List arrayValues = new ArrayList<>(arrayInitializer.expressions().size());
- for (Object arrayExpression : arrayInitializer.expressions())
- {
- arrayValues.add(getAnnotationValueForExpression((Expression) arrayExpression));
- }
- value = new AnnotationArrayValue(arrayValues);
- }
- else if (expression instanceof QualifiedName)
- {
- QualifiedName qualifiedName = (QualifiedName) expression;
- Object expressionValue = qualifiedName.resolveConstantExpressionValue();
- if (expressionValue == null)
- {
- value = new AnnotationLiteralValue(String.class, qualifiedName.toString());
- }
- else
- {
- Class> expressionType = expressionValue.getClass();
- value = new AnnotationLiteralValue(expressionType, expressionValue);
- }
- }
- else if (expression instanceof CastExpression)
- {
- CastExpression cast = (CastExpression) expression;
- AnnotationValue castExpressionValue = getAnnotationValueForExpression(cast.getExpression());
- if (castExpressionValue instanceof AnnotationLiteralValue)
- {
- AnnotationLiteralValue literalValue = (AnnotationLiteralValue) castExpressionValue;
- ITypeBinding binding = cast.getType().resolveBinding();
- if (binding == null)
- {
- value = new AnnotationLiteralValue(String.class, literalValue.getLiteralValue());
- }
- else
- {
- Class> type = resolveLiteralType(cast.getType().resolveBinding());
- value = new AnnotationLiteralValue(type, literalValue.getLiteralValue());
- }
- }
- else
- {
- value = castExpressionValue;
- }
- }
- else if (expression instanceof SimpleName)
- {
- SimpleName simpleName = (SimpleName) expression;
- ITypeBinding binding = simpleName.resolveTypeBinding();
- if (binding == null)
- {
- value = new AnnotationLiteralValue(String.class, simpleName.toString());
- }
- else
- {
- Object expressionValue = simpleName.resolveConstantExpressionValue();
- Class> expressionType = expressionValue == null ? null : expressionValue.getClass();
- value = new AnnotationLiteralValue(expressionType, expressionValue);
- }
- }
- else
- {
- LOG.warning("Unexpected type: " + expression.getClass().getCanonicalName() + " in file: " + this.javaFile
- + " just attempting to use it as a string value");
- value = new AnnotationLiteralValue(String.class, expression == null ? null : expression.toString());
- }
- return value;
-
- }
-
- /**
- * Adds parameters contained in the annotation into the annotation type reference
- *
- * @param typeRef
- * @param node
- */
- private void addAnnotationValues(AnnotationClassReference typeRef, Annotation node)
- {
- Map annotationValueMap = new HashMap<>();
- if (node instanceof SingleMemberAnnotation)
- {
- SingleMemberAnnotation singleMemberAnnotation = (SingleMemberAnnotation) node;
- AnnotationValue value = getAnnotationValueForExpression(singleMemberAnnotation.getValue());
- annotationValueMap.put("value", value);
- }
- else if (node instanceof NormalAnnotation)
- {
- @SuppressWarnings("unchecked")
- List annotationValues = ((NormalAnnotation) node).values();
- for (MemberValuePair annotationValue : annotationValues)
- {
- String key = annotationValue.getName().toString();
- Expression expression = annotationValue.getValue();
- AnnotationValue value = getAnnotationValueForExpression(expression);
- annotationValueMap.put(key, value);
- }
- }
- typeRef.setAnnotationValues(annotationValueMap);
- }
-
- private Class> resolveLiteralType(ITypeBinding binding)
- {
- switch (binding.getName())
- {
- case "byte":
- return byte.class;
- case "short":
- return short.class;
- case "int":
- return int.class;
- case "long":
- return long.class;
- case "float":
- return float.class;
- case "double":
- return double.class;
- case "boolean":
- return boolean.class;
- case "char":
- return char.class;
- default:
- throw new ASTException("Unrecognized literal type: " + binding.getName());
- }
- }
-
- private AnnotationClassReference processAnnotation(Annotation node)
- {
- ITypeBinding typeBinding = node.resolveTypeBinding();
- AnnotationClassReference reference;
- String qualifiedName;
- if (typeBinding != null)
- qualifiedName = typeBinding.getQualifiedName();
- else
- qualifiedName = resolveClassname(node.getTypeName().toString());
-
- reference = new AnnotationClassReference(
- qualifiedName,
- cu.getLineNumber(node.getStartPosition()),
- cu.getColumnNumber(node.getStartPosition()),
- node.getLength(),
- node.toString());
-
- addAnnotationValues(reference, node);
-
- return reference;
- }
-
- @Override
- public boolean visit(NormalAnnotation node)
- {
- AnnotationClassReference reference = processAnnotation(node);
- this.classReferences.addReference(reference);
-
- // false to avoid recursively processing nested annotations (our code already handles that)
- return false;
- }
-
- @Override
- public boolean visit(SingleMemberAnnotation node)
- {
- AnnotationClassReference reference = processAnnotation(node);
- this.classReferences.addReference(reference);
- return false;
- }
-
- @Override
- public boolean visit(MarkerAnnotation node)
- {
- AnnotationClassReference reference = processAnnotation(node);
- this.classReferences.addReference(reference);
- return false;
- }
-
- public boolean visit(TypeDeclaration node)
- {
- Object clzInterfaces = node.getStructuralProperty(TypeDeclaration.SUPER_INTERFACE_TYPES_PROPERTY);
- Object clzSuperClasses = node.getStructuralProperty(TypeDeclaration.SUPERCLASS_TYPE_PROPERTY);
-
- if (clzInterfaces != null)
- {
- if (List.class.isAssignableFrom(clzInterfaces.getClass()))
- {
- List> clzInterfacesList = (List>) clzInterfaces;
- for (Object clzInterface : clzInterfacesList)
- {
- if (clzInterface instanceof SimpleType)
- {
- ITypeBinding resolvedSuperInterface = ((SimpleType) clzInterface).resolveBinding();
- Stack stack = new Stack();
- stack.push(resolvedSuperInterface);
- // register all the implemented interfaces (even superinterfaces)
- while (!stack.isEmpty())
- {
- resolvedSuperInterface = stack.pop();
- processTypeBinding(resolvedSuperInterface, TypeReferenceLocation.IMPLEMENTS_TYPE,
- cu.getLineNumber(node.getStartPosition()),
- cu.getColumnNumber(node.getStartPosition()), node.getLength(), extractDefinitionLine(node.toString()));
- if (resolvedSuperInterface != null)
- {
- ITypeBinding[] interfaces = resolvedSuperInterface.getInterfaces();
- for (ITypeBinding oneInterface : interfaces)
- {
- stack.push(oneInterface);
- }
- }
- }
- }
- }
- }
- }
- if (clzSuperClasses != null)
- {
- if (clzSuperClasses instanceof SimpleType)
- {
- ITypeBinding resolvedSuperClass = ((SimpleType) clzSuperClasses).resolveBinding();
- // register all the superClasses up to Object
- while (resolvedSuperClass != null && !resolvedSuperClass.getQualifiedName().equals("java.lang.Object"))
- {
- processTypeBinding(resolvedSuperClass, TypeReferenceLocation.INHERITANCE, cu.getLineNumber(node.getStartPosition()),
- cu.getColumnNumber(node.getStartPosition()), node.getLength(), extractDefinitionLine(node.toString()));
- resolvedSuperClass = resolvedSuperClass.getSuperclass();
- }
- }
- }
-
- return super.visit(node);
- }
-
- /**
- * Declaration of the variable within a block
- */
- @Override
- public boolean visit(VariableDeclarationStatement node)
- {
- for (int i = 0; i < node.fragments().size(); ++i)
- {
- String nodeType = node.getType().toString();
- nodeType = resolveClassname(nodeType);
- VariableDeclarationFragment frag = (VariableDeclarationFragment) node.fragments().get(i);
- this.names.add(frag.getName().getIdentifier());
- this.nameInstance.put(frag.getName().toString(), nodeType.toString());
- }
- processType(node.getType(), TypeReferenceLocation.VARIABLE_DECLARATION,
- cu.getLineNumber(node.getStartPosition()),
- cu.getColumnNumber(node.getStartPosition()), node.getLength(), node.toString());
- return super.visit(node);
- }
-
- @Override
- public boolean visit(ImportDeclaration node)
- {
- String name = node.getName().toString();
- if (node.isOnDemand())
- {
- wildcardImports.add(name);
-
- String[] resolvedNames = this.wildcardImportResolver.resolve(name);
- for (String resolvedName : resolvedNames)
- {
- processImport(resolvedName, cu.getLineNumber(node.getName().getStartPosition()),
- cu.getColumnNumber(node.getName().getStartPosition()), node.getName().getLength(), node.toString());
- }
- }
- else
- {
- String clzName = StringUtils.substringAfterLast(name, ".");
- classNameLookedUp.add(clzName);
- classNameToFQCN.put(clzName, name);
- processImport(name, cu.getLineNumber(node.getName().getStartPosition()),
- cu.getColumnNumber(node.getName().getStartPosition()), node.getName().getLength(), node.toString());
- }
-
- return super.visit(node);
- }
-
- /***
- * Takes the MethodInvocation, and attempts to resolve the types of objects passed into the method invocation.
- */
- public boolean visit(MethodInvocation node)
- {
- if (!StringUtils.contains(node.toString(), "."))
- {
- // it must be a local method. ignore.
- return true;
- }
- List qualifiedInstances = new ArrayList();
- List argumentsQualified = new ArrayList();
- // get qualified arguments of the method
- IMethodBinding resolveTypeBinding = node.resolveMethodBinding();
- if (resolveTypeBinding != null)
- {
- ITypeBinding[] arguments = resolveTypeBinding.getParameterTypes();
-
- for (ITypeBinding type : arguments)
- {
- argumentsQualified.add(type.getQualifiedName());
- }
-
- // find the interface declaring the method
-
- if (resolveTypeBinding != null && resolveTypeBinding.getDeclaringClass() != null)
- {
- ITypeBinding declaringClass = resolveTypeBinding.getDeclaringClass();
- qualifiedInstances.add(declaringClass.getQualifiedName());
- ITypeBinding[] interfaces = declaringClass.getInterfaces();
- // Now find all the implemented interfaces having the method called.
- for (ITypeBinding possibleInterface : interfaces)
- {
- IMethodBinding[] declaredMethods = possibleInterface.getDeclaredMethods();
- if (declaredMethods.length != 0)
- {
- for (IMethodBinding interfaceMethod : declaredMethods)
- {
- if (interfaceMethod.getName().equals(node.getName().toString()))
- {
-
- List interfaceMethodArguments = new ArrayList();
- for (ITypeBinding type : interfaceMethod.getParameterTypes())
- {
- interfaceMethodArguments.add(type.getQualifiedName());
- }
- if (interfaceMethodArguments.equals(argumentsQualified))
- {
- qualifiedInstances.add(possibleInterface.getQualifiedName());
- }
- }
- }
- }
-
- }
- }
-
- }
- else
- {
- String nodeName = StringUtils.removeStart(node.toString(), "this.");
- String objRef = StringUtils.substringBefore(nodeName, "." + node.getName().toString());
- if (nameInstance.containsKey(objRef))
- {
- objRef = nameInstance.get(objRef);
- }
- objRef = resolveClassname(objRef);
-
- // not resolved binding
- List arguments = node.arguments();
- for (Expression expression : arguments)
- {
- ITypeBinding argumentBinding = expression.resolveTypeBinding();
- if (argumentBinding != null)
- {
- argumentsQualified.add(argumentBinding.getQualifiedName());
- }
- else
- {
- // TODO: Is toString good option? Just a name of the argument will be saved
- argumentsQualified.add(expression.toString());
- }
- }
- qualifiedInstances.add(objRef);
- }
-
- // register all found qualified names for this method invocation
- for (String qualifiedInstance : qualifiedInstances)
- {
- MethodType methodCall = new MethodType(qualifiedInstance, node.getName().toString(), argumentsQualified);
- processMethod(methodCall, TypeReferenceLocation.METHOD_CALL, cu.getLineNumber(node.getName().getStartPosition()),
- cu.getColumnNumber(node.getName().getStartPosition()), node.getName().getLength(), node.toString());
- }
-
- return super.visit(node);
- }
-
- @Override
- public boolean visit(PackageDeclaration node)
- {
- return super.visit(node);
- }
-
- @Override
- public boolean visit(ClassInstanceCreation node)
- {
- IMethodBinding constructorBinding = node.resolveConstructorBinding();
- // ITypeBinding resolveTypeBinding = node.resolveTypeBinding();
- String qualifiedClass = "";
- List constructorMethodQualifiedArguments = new ArrayList();
- if (constructorBinding != null && constructorBinding.getDeclaringClass() != null)
- {
- ITypeBinding declaringClass = constructorBinding.getDeclaringClass();
- qualifiedClass = declaringClass.getQualifiedName();
- for (ITypeBinding type : constructorBinding.getParameterTypes())
- {
- constructorMethodQualifiedArguments.add(type.getQualifiedName());
- }
- }
-
- if (constructorMethodQualifiedArguments.isEmpty() && !node.arguments().isEmpty())
- {
- List arguments = node.arguments();
- arguments.get(0).resolveTypeBinding();
- for (Expression type : arguments)
- {
- ITypeBinding argumentBinding = type.resolveTypeBinding();
- if (argumentBinding != null)
- {
- constructorMethodQualifiedArguments.add(argumentBinding.getQualifiedName());
- }
- else
- {
- List guessedParam = methodParameterGuesser(Collections.singletonList(type));
- constructorMethodQualifiedArguments.addAll(guessedParam);
- }
-
- }
- }
-
- // qualified class may not be resolved in case of anonymous classes
- if (qualifiedClass == null || qualifiedClass.equals(""))
- {
- qualifiedClass = node.getType().toString();
- qualifiedClass = resolveClassname(qualifiedClass);
- }
-
- ConstructorType resolvedConstructor = new ConstructorType(qualifiedClass, constructorMethodQualifiedArguments);
- processConstructor(resolvedConstructor, cu.getLineNumber(node.getType().getStartPosition()),
- cu.getColumnNumber(node.getType().getStartPosition()), node.getType().getLength(), node.toString());
-
- return super.visit(node);
+ CompilationUnit cu = (CompilationUnit) parser.createAST(null);
+ return new ASTReferenceResolver(importResolver).analyze(sourceFile.toString(), cu);
}
-
- public static class MethodType
- {
- private final String qualifiedName;
- private final String methodName;
- private final List qualifiedParameters;
-
- public MethodType(String qualifiedName, String methodName, List qualifiedParameters)
- {
- this.qualifiedName = qualifiedName;
- this.methodName = methodName;
-
- if (qualifiedParameters != null)
- {
- this.qualifiedParameters = qualifiedParameters;
- }
- else
- {
- this.qualifiedParameters = new LinkedList();
- }
- }
-
- public String getMethodName()
- {
- return methodName;
- }
-
- public String getQualifiedName()
- {
- return qualifiedName;
- }
-
- public List getQualifiedParameters()
- {
- return qualifiedParameters;
- }
-
- @Override
- public String toString()
- {
- StringBuilder builder = new StringBuilder();
- builder.append(qualifiedName + "." + methodName);
- builder.append("(");
-
- for (int i = 0, j = qualifiedParameters.size(); i < j; i++)
- {
- if (i > 0)
- {
- builder.append(", ");
- }
- String param = qualifiedParameters.get(i);
- builder.append(param);
- }
- builder.append(")");
-
- return builder.toString();
- }
- }
-
- public static class ConstructorType
- {
- private final String qualifiedName;
- private final List qualifiedParameters;
-
- public ConstructorType(String qualifiedName, List qualifiedParameters)
- {
- this.qualifiedName = qualifiedName;
- if (qualifiedParameters != null)
- {
- this.qualifiedParameters = qualifiedParameters;
- }
- else
- {
- this.qualifiedParameters = new LinkedList();
- }
-
- }
-
- public String getQualifiedName()
- {
- return qualifiedName;
- }
-
- public List getQualifiedParameters()
- {
- return qualifiedParameters;
- }
-
- @Override
- public String toString()
- {
- StringBuilder builder = new StringBuilder();
- builder.append(qualifiedName);
- builder.append("(");
-
- for (int i = 0, j = qualifiedParameters.size(); i < j; i++)
- {
- if (i > 0)
- {
- builder.append(", ");
- }
- String param = qualifiedParameters.get(i);
- builder.append(param);
- }
- builder.append(")");
-
- return builder.toString();
- }
- }
-
- private List methodParameterGuesser(List> arguements)
- {
- List resolvedParams = new ArrayList(arguements.size());
- for (Object o : arguements)
- {
- if (o instanceof SimpleName)
- {
- String name = nameInstance.get(o.toString());
- if (name != null)
- {
- resolvedParams.add(name);
- }
- else
- {
- resolvedParams.add("Undefined");
- }
- }
- else if (o instanceof StringLiteral)
- {
- resolvedParams.add("java.lang.String");
- }
- else if (o instanceof FieldAccess)
- {
- String field = ((FieldAccess) o).getName().toString();
- if (names.contains(field))
- {
- resolvedParams.add(nameInstance.get(field));
- }
- else
- {
- resolvedParams.add("Undefined");
- }
- }
- else if (o instanceof CastExpression)
- {
- String type = ((CastExpression) o).getType().toString();
- type = qualifyType(type);
- resolvedParams.add(type);
- }
- else if (o instanceof MethodInvocation)
- {
- String on = ((MethodInvocation) o).getName().toString();
- if (StringUtils.equals(on, "toString"))
- {
- if (((MethodInvocation) o).arguments().size() == 0)
- {
- resolvedParams.add("java.lang.String");
- }
- }
- else
- {
- resolvedParams.add("Undefined");
- }
- }
- else if (o instanceof NumberLiteral)
- {
- if (StringUtils.endsWith(o.toString(), "L"))
- {
- resolvedParams.add("long");
- }
- else if (StringUtils.endsWith(o.toString(), "f"))
- {
- resolvedParams.add("float");
- }
- else if (StringUtils.endsWith(o.toString(), "d"))
- {
- resolvedParams.add("double");
- }
- else
- {
- resolvedParams.add("int");
- }
- }
- else if (o instanceof BooleanLiteral)
- {
- resolvedParams.add("boolean");
- }
- else if (o instanceof ClassInstanceCreation)
- {
- String nodeType = ((ClassInstanceCreation) o).getType().toString();
- nodeType = resolveClassname(nodeType);
- resolvedParams.add(nodeType);
- }
- else if (o instanceof org.eclipse.jdt.core.dom.CharacterLiteral)
- {
- resolvedParams.add("char");
- }
- else if (o instanceof InfixExpression)
- {
- String expression = o.toString();
- if (StringUtils.contains(expression, "\""))
- {
- resolvedParams.add("java.lang.String");
- }
- else
- {
- resolvedParams.add("Undefined");
- }
- }
- else
- {
- resolvedParams.add("Undefined");
- }
- }
- return resolvedParams;
- }
-
- private String resolveClassname(String sourceClassname)
- {
- // If the type contains a "." assume that it is fully qualified.
- // FIXME - This is a carryover from the original Windup code, and I don't think
- // that this assumption is valid.
- if (!StringUtils.contains(sourceClassname, "."))
- {
- // Check if we have already looked this one up
- if (classNameLookedUp.contains(sourceClassname))
- {
- // if yes, then just use the looked up name from the map
- String qualifiedName = classNameToFQCN.get(sourceClassname);
- if (qualifiedName != null)
- {
- return qualifiedName;
- }
- else
- {
- // otherwise, just return the provided name (unchanged)
- return sourceClassname;
- }
- }
- else
- {
- classNameLookedUp.add(sourceClassname);
- String resolvedClassName = this.wildcardImportResolver.resolve(this.wildcardImports, sourceClassname);
- if (resolvedClassName != null)
- {
- classNameToFQCN.put(sourceClassname, resolvedClassName);
- return resolvedClassName;
- }
- // nothing was found, so just return the original value
- return sourceClassname;
- }
- }
- else
- {
- return sourceClassname;
- }
- }
-
- private String qualifyType(String objRef)
- {
- // temporarily remove to resolve arrays
- objRef = StringUtils.removeEnd(objRef, "[]");
- if (nameInstance.containsKey(objRef))
- {
- objRef = nameInstance.get(objRef);
- }
- objRef = resolveClassname(objRef);
- return objRef;
- }
-
}
diff --git a/java-ast/addon/src/main/java/org/jboss/windup/ast/java/ASTReferenceResolver.java b/java-ast/addon/src/main/java/org/jboss/windup/ast/java/ASTReferenceResolver.java
new file mode 100644
index 0000000000..f703e4b3d0
--- /dev/null
+++ b/java-ast/addon/src/main/java/org/jboss/windup/ast/java/ASTReferenceResolver.java
@@ -0,0 +1,1104 @@
+package org.jboss.windup.ast.java;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+import java.util.logging.Logger;
+
+import org.apache.commons.lang3.StringUtils;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
+import org.eclipse.jdt.core.dom.Annotation;
+import org.eclipse.jdt.core.dom.ArrayInitializer;
+import org.eclipse.jdt.core.dom.BooleanLiteral;
+import org.eclipse.jdt.core.dom.CastExpression;
+import org.eclipse.jdt.core.dom.CharacterLiteral;
+import org.eclipse.jdt.core.dom.ClassInstanceCreation;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.FieldAccess;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.ImportDeclaration;
+import org.eclipse.jdt.core.dom.InfixExpression;
+import org.eclipse.jdt.core.dom.InstanceofExpression;
+import org.eclipse.jdt.core.dom.MarkerAnnotation;
+import org.eclipse.jdt.core.dom.MemberValuePair;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.NormalAnnotation;
+import org.eclipse.jdt.core.dom.NumberLiteral;
+import org.eclipse.jdt.core.dom.PackageDeclaration;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.ReturnStatement;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SimpleType;
+import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.StringLiteral;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.dom.TypeLiteral;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
+import org.jboss.windup.ast.java.data.ClassReference;
+import org.jboss.windup.ast.java.data.TypeReferenceLocation;
+import org.jboss.windup.ast.java.data.annotations.AnnotationArrayValue;
+import org.jboss.windup.ast.java.data.annotations.AnnotationClassReference;
+import org.jboss.windup.ast.java.data.annotations.AnnotationLiteralValue;
+import org.jboss.windup.ast.java.data.annotations.AnnotationValue;
+
+/**
+ * Provides the ability to parse a Java file and return a {@link List} of {@link ClassReference} objects containing the fully qualified names of all
+ * of the contained references.
+ *
+ * @author Jesse Sightler
+ *
+ */
+public class ASTReferenceResolver extends ASTVisitor
+{
+ private static Logger LOG = Logger.getLogger(ASTReferenceResolver.class.getName());
+
+ private final WildcardImportResolver wildcardImportResolver;
+ private String path;
+ private CompilationUnit compilationUnit;
+
+ /**
+ * Contains all wildcard imports (import com.example.*) lines from the source file.
+ *
+ * These are used for type resolution throughout the class.
+ */
+ private final List wildcardImports = new ArrayList<>();
+
+ /**
+ * Indicates that we have already attempted to query the graph for this particular shortname. The shortname will exist here even if no results
+ * were found.
+ */
+ private final Set classNameLookedUp = new HashSet<>();
+
+ /**
+ * Contains a map of class short names (eg, MyClass) to qualified names (eg, com.example.MyClass)
+ */
+ private final Map classNameToFQCN = new HashMap<>();
+ /**
+ * Maintains a set of all variable names that have been resolved
+ */
+ private final Set names = new HashSet();
+
+ /**
+ * Maintains a map of nameInstances to fully qualified class names.
+ */
+ private final Map nameInstance = new HashMap();
+
+ private List classReferences;
+
+ public ASTReferenceResolver(WildcardImportResolver importResolver)
+ {
+ this.wildcardImportResolver = importResolver;
+ }
+
+ public List analyze(String path, CompilationUnit compilationUnit)
+ {
+ this.compilationUnit = compilationUnit;
+ this.path = path;
+ this.classReferences = new ArrayList<>();
+ this.wildcardImports.clear();
+ this.classNameLookedUp.clear();
+ this.classNameToFQCN.clear();
+ this.names.clear();
+ this.nameInstance.clear();
+
+ PackageDeclaration packageDeclaration = this.compilationUnit.getPackage();
+ String packageName = packageDeclaration == null ? "" : packageDeclaration.getName().getFullyQualifiedName();
+ @SuppressWarnings("unchecked")
+ List types = this.compilationUnit.types();
+ String fqcn = null;
+ if (!types.isEmpty())
+ {
+ AbstractTypeDeclaration typeDeclaration = (AbstractTypeDeclaration) types.get(0);
+ String className = typeDeclaration.getName().getFullyQualifiedName();
+
+ if (packageName.equals(""))
+ {
+ fqcn = className;
+ }
+ else
+ {
+ fqcn = packageName + "." + className;
+ }
+ this.classReferences.add(new ClassReference(fqcn, TypeReferenceLocation.TYPE, this.compilationUnit.getLineNumber(typeDeclaration
+ .getStartPosition()), this.compilationUnit.getColumnNumber(this.compilationUnit.getStartPosition()), this.compilationUnit
+ .getLength(), extractDefinitionLine(typeDeclaration
+ .toString())));
+
+ if (typeDeclaration instanceof TypeDeclaration)
+ {
+ Type superclassType = ((TypeDeclaration) typeDeclaration).getSuperclassType();
+ ITypeBinding resolveBinding = null;
+ if (superclassType != null)
+ {
+ resolveBinding = superclassType.resolveBinding();
+ }
+
+ while (resolveBinding != null)
+ {
+ if (superclassType.resolveBinding() != null)
+ {
+ this.classReferences.add(new ClassReference(resolveBinding.getQualifiedName(), TypeReferenceLocation.TYPE,
+ this.compilationUnit
+ .getLineNumber(typeDeclaration
+ .getStartPosition()), this.compilationUnit.getColumnNumber(this.compilationUnit
+ .getStartPosition()),
+ this.compilationUnit.getLength(),
+ extractDefinitionLine(typeDeclaration.toString())));
+ }
+ resolveBinding = resolveBinding.getSuperclass();
+ }
+ }
+ }
+
+ this.names.add("this");
+ this.nameInstance.put("this", fqcn);
+
+ this.compilationUnit.accept(this);
+ return this.classReferences;
+ }
+
+ private String extractDefinitionLine(String typeDeclaration)
+ {
+ String typeLine = "";
+ String[] lines = typeDeclaration.split("\n");
+ for (String line : lines)
+ {
+ typeLine = line;
+ if (line.contains("{"))
+ {
+ break;
+ }
+ }
+ return typeLine;
+ }
+
+ public List getJavaClassReferences()
+ {
+ return this.classReferences;
+ }
+
+ private void processConstructor(ConstructorType interest, int lineNumber, int columnNumber, int length, String line)
+ {
+ String text = interest.toString();
+ this.classReferences.add(new ClassReference(text, TypeReferenceLocation.CONSTRUCTOR_CALL, lineNumber, columnNumber, length,
+ line));
+ }
+
+ private void processMethod(MethodType interest, TypeReferenceLocation location, int lineNumber, int columnNumber, int length, String line)
+ {
+ String text = interest.toString();
+ this.classReferences.add(new ClassReference(text, location, lineNumber, columnNumber, length, line));
+ }
+
+ private void processImport(String interest, int lineNumber, int columnNumber, int length, String line)
+ {
+ this.classReferences.add(new ClassReference(interest, TypeReferenceLocation.IMPORT, lineNumber, columnNumber, length, line));
+ }
+
+ private ClassReference processTypeBinding(ITypeBinding type, TypeReferenceLocation referenceLocation, int lineNumber, int columnNumber,
+ int length, String line)
+ {
+ if (type == null)
+ return null;
+ String sourceString = type.getQualifiedName();
+ return processTypeAsString(sourceString, referenceLocation, lineNumber, columnNumber, length, line);
+ }
+
+ /**
+ * The method determines if the type can be resolved and if not, will try to guess the qualified name using the information from the imports.
+ */
+ private ClassReference processType(Type type, TypeReferenceLocation typeReferenceLocation, int lineNumber, int columnNumber, int length,
+ String line)
+ {
+ if (type == null)
+ return null;
+
+ ITypeBinding resolveBinding = type.resolveBinding();
+ if (resolveBinding == null)
+ {
+ return processTypeAsString(resolveClassname(type.toString()), typeReferenceLocation, lineNumber,
+ columnNumber, length, line);
+ }
+ else
+ {
+ return processTypeBinding(type.resolveBinding(), typeReferenceLocation, lineNumber,
+ columnNumber, length, line);
+ }
+
+ }
+
+ private ClassReference processTypeAsString(String sourceString, TypeReferenceLocation referenceLocation, int lineNumber,
+ int columnNumber, int length, String line)
+ {
+ if (sourceString == null)
+ return null;
+ line = line.replaceAll("(\\n)|(\\r)", "");
+ ClassReference typeRef = new ClassReference(sourceString, referenceLocation, lineNumber, columnNumber, length, line);
+ this.classReferences.add(typeRef);
+ return typeRef;
+ }
+
+ @Override
+ public boolean visit(MethodDeclaration node)
+ {
+ //register method return type
+ IMethodBinding resolveBinding = node.resolveBinding();
+ ITypeBinding returnType = null;
+ if (resolveBinding != null)
+ {
+ returnType = node.resolveBinding().getReturnType();
+ }
+ if (returnType != null)
+ {
+ processTypeBinding(returnType, TypeReferenceLocation.RETURN_TYPE, compilationUnit.getLineNumber(node.getStartPosition()),
+ compilationUnit.getColumnNumber(node.getStartPosition()), node.getLength(), extractDefinitionLine(node.toString()));
+ }
+ else
+ {
+ Type methodReturnType = node.getReturnType2();
+ processType(methodReturnType, TypeReferenceLocation.RETURN_TYPE, compilationUnit.getLineNumber(node.getStartPosition()),
+ compilationUnit.getColumnNumber(node.getStartPosition()), node.getLength(), extractDefinitionLine(node.toString()));
+ }
+ // register parameters and register them for next processing
+ List qualifiedArguments = new ArrayList();
+ @SuppressWarnings("unchecked")
+ List parameters = (List) node.parameters();
+ if (parameters != null)
+ {
+ for (SingleVariableDeclaration type : parameters)
+ {
+ this.names.add(type.getName().toString());
+ String typeName = type.getType().toString();
+ typeName = resolveClassname(typeName);
+ qualifiedArguments.add(typeName);
+ this.nameInstance.put(type.getName().toString(), typeName);
+ processType(type.getType(), TypeReferenceLocation.METHOD_PARAMETER, compilationUnit.getLineNumber(node.getStartPosition()),
+ compilationUnit.getColumnNumber(node.getStartPosition()), node.getLength(), extractDefinitionLine(node.toString()));
+ }
+ }
+ // register thow declarations
+ @SuppressWarnings("unchecked")
+ List throwsTypes = node.thrownExceptionTypes();
+ if (throwsTypes != null)
+ {
+ for (Type type : throwsTypes)
+ {
+ processType(type, TypeReferenceLocation.THROWS_METHOD_DECLARATION,
+ compilationUnit.getLineNumber(node.getStartPosition()),
+ compilationUnit.getColumnNumber(type.getStartPosition()), type.getLength(), extractDefinitionLine(node.toString()));
+ }
+ }
+
+ // register method declaration
+ MethodType methodCall = new MethodType(nameInstance.get("this"), node.getName().toString(), qualifiedArguments);
+ processMethod(methodCall, TypeReferenceLocation.METHOD, compilationUnit.getLineNumber(node.getName().getStartPosition()),
+ compilationUnit.getColumnNumber(node.getName().getStartPosition()), node.getName().getLength(),
+ extractDefinitionLine(node.toString()));
+ return super.visit(node);
+ }
+
+ @Override
+ public boolean visit(InstanceofExpression node)
+ {
+ Type type = node.getRightOperand();
+ processType(type, TypeReferenceLocation.INSTANCE_OF, compilationUnit.getLineNumber(node.getStartPosition()),
+ compilationUnit.getColumnNumber(type.getStartPosition()), type.getLength(), node.toString());
+
+ return super.visit(node);
+ }
+
+ public boolean visit(org.eclipse.jdt.core.dom.ThrowStatement node)
+ {
+ if (node.getExpression() instanceof ClassInstanceCreation)
+ {
+ ClassInstanceCreation cic = (ClassInstanceCreation) node.getExpression();
+ Type type = cic.getType();
+ processType(type, TypeReferenceLocation.THROW_STATEMENT, compilationUnit.getLineNumber(node.getStartPosition()),
+ compilationUnit.getColumnNumber(cic.getStartPosition()), cic.getLength(), node.toString());
+ }
+
+ return super.visit(node);
+ }
+
+ public boolean visit(org.eclipse.jdt.core.dom.CatchClause node)
+ {
+ Type catchType = node.getException().getType();
+ processType(catchType, TypeReferenceLocation.CATCH_EXCEPTION_STATEMENT, compilationUnit.getLineNumber(node.getStartPosition()),
+ compilationUnit.getColumnNumber(catchType.getStartPosition()), catchType.getLength(), node.toString());
+
+ return super.visit(node);
+ }
+
+ @Override
+ public boolean visit(ReturnStatement node)
+ {
+ if (node.getExpression() instanceof ClassInstanceCreation)
+ {
+ ClassInstanceCreation cic = (ClassInstanceCreation) node.getExpression();
+ processTypeBinding(cic.getType().resolveBinding(), TypeReferenceLocation.CONSTRUCTOR_CALL,
+ compilationUnit.getLineNumber(node.getStartPosition()),
+ compilationUnit.getColumnNumber(cic.getStartPosition()), cic.getLength(), node.toString());
+ }
+ return super.visit(node);
+ }
+
+ @Override
+ public boolean visit(FieldDeclaration node)
+ {
+ for (int i = 0; i < node.fragments().size(); ++i)
+ {
+ String nodeType = node.getType().toString();
+ nodeType = resolveClassname(nodeType);
+ VariableDeclarationFragment frag = (VariableDeclarationFragment) node.fragments().get(i);
+ frag.resolveBinding();
+ this.names.add(frag.getName().getIdentifier());
+ this.nameInstance.put(frag.getName().toString(), nodeType.toString());
+
+ processTypeBinding(node.getType().resolveBinding(), TypeReferenceLocation.FIELD_DECLARATION,
+ compilationUnit.getLineNumber(node.getStartPosition()),
+ compilationUnit.getColumnNumber(node.getStartPosition()), node.getLength(), node.toString());
+ }
+ return true;
+ }
+
+ private AnnotationValue getAnnotationValueForExpression(Expression expression)
+ {
+ AnnotationValue value;
+ if (expression instanceof BooleanLiteral)
+ value = new AnnotationLiteralValue(boolean.class, ((BooleanLiteral) expression).booleanValue());
+ else if (expression instanceof CharacterLiteral)
+ value = new AnnotationLiteralValue(char.class, ((CharacterLiteral) expression).charValue());
+ else if (expression instanceof NumberLiteral)
+ {
+ ITypeBinding binding = expression.resolveTypeBinding();
+ if (binding == null)
+ {
+ value = new AnnotationLiteralValue(String.class, expression.toString());
+ }
+ else
+ {
+ value = new AnnotationLiteralValue(resolveLiteralType(binding), ((NumberLiteral) expression).resolveConstantExpressionValue());
+ }
+ }
+ else if (expression instanceof TypeLiteral)
+ value = new AnnotationLiteralValue(Class.class, ((TypeLiteral) expression).resolveConstantExpressionValue());
+ else if (expression instanceof StringLiteral)
+ value = new AnnotationLiteralValue(String.class, ((StringLiteral) expression).getLiteralValue());
+ else if (expression instanceof NormalAnnotation)
+ value = processAnnotation((NormalAnnotation) expression);
+ else if (expression instanceof ArrayInitializer)
+ {
+ ArrayInitializer arrayInitializer = (ArrayInitializer) expression;
+ List arrayValues = new ArrayList<>(arrayInitializer.expressions().size());
+ for (Object arrayExpression : arrayInitializer.expressions())
+ {
+ arrayValues.add(getAnnotationValueForExpression((Expression) arrayExpression));
+ }
+ value = new AnnotationArrayValue(arrayValues);
+ }
+ else if (expression instanceof QualifiedName)
+ {
+ QualifiedName qualifiedName = (QualifiedName) expression;
+ Object expressionValue = qualifiedName.resolveConstantExpressionValue();
+ if (expressionValue == null)
+ {
+ value = new AnnotationLiteralValue(String.class, qualifiedName.toString());
+ }
+ else
+ {
+ Class> expressionType = expressionValue.getClass();
+ value = new AnnotationLiteralValue(expressionType, expressionValue);
+ }
+ }
+ else if (expression instanceof CastExpression)
+ {
+ CastExpression cast = (CastExpression) expression;
+ AnnotationValue castExpressionValue = getAnnotationValueForExpression(cast.getExpression());
+ if (castExpressionValue instanceof AnnotationLiteralValue)
+ {
+ AnnotationLiteralValue literalValue = (AnnotationLiteralValue) castExpressionValue;
+ ITypeBinding binding = cast.getType().resolveBinding();
+ if (binding == null)
+ {
+ value = new AnnotationLiteralValue(String.class, literalValue.getLiteralValue());
+ }
+ else
+ {
+ Class> type = resolveLiteralType(cast.getType().resolveBinding());
+ value = new AnnotationLiteralValue(type, literalValue.getLiteralValue());
+ }
+ }
+ else
+ {
+ value = castExpressionValue;
+ }
+ }
+ else if (expression instanceof SimpleName)
+ {
+ SimpleName simpleName = (SimpleName) expression;
+ ITypeBinding binding = simpleName.resolveTypeBinding();
+ if (binding == null)
+ {
+ value = new AnnotationLiteralValue(String.class, simpleName.toString());
+ }
+ else
+ {
+ Object expressionValue = simpleName.resolveConstantExpressionValue();
+ Class> expressionType = expressionValue == null ? null : expressionValue.getClass();
+ value = new AnnotationLiteralValue(expressionType, expressionValue);
+ }
+ }
+ else
+ {
+ LOG.warning("Unexpected type: " + expression.getClass().getCanonicalName() + " in file: " + this.path
+ + " just attempting to use it as a string value");
+ value = new AnnotationLiteralValue(String.class, expression == null ? null : expression.toString());
+ }
+ return value;
+
+ }
+
+ /**
+ * Adds parameters contained in the annotation into the annotation type reference
+ *
+ * @param typeRef
+ * @param node
+ */
+ private void addAnnotationValues(AnnotationClassReference typeRef, Annotation node)
+ {
+ Map annotationValueMap = new HashMap<>();
+ if (node instanceof SingleMemberAnnotation)
+ {
+ SingleMemberAnnotation singleMemberAnnotation = (SingleMemberAnnotation) node;
+ AnnotationValue value = getAnnotationValueForExpression(singleMemberAnnotation.getValue());
+ annotationValueMap.put("value", value);
+ }
+ else if (node instanceof NormalAnnotation)
+ {
+ @SuppressWarnings("unchecked")
+ List annotationValues = ((NormalAnnotation) node).values();
+ for (MemberValuePair annotationValue : annotationValues)
+ {
+ String key = annotationValue.getName().toString();
+ Expression expression = annotationValue.getValue();
+ AnnotationValue value = getAnnotationValueForExpression(expression);
+ annotationValueMap.put(key, value);
+ }
+ }
+ typeRef.setAnnotationValues(annotationValueMap);
+ }
+
+ private Class> resolveLiteralType(ITypeBinding binding)
+ {
+ switch (binding.getName())
+ {
+ case "byte":
+ return byte.class;
+ case "short":
+ return short.class;
+ case "int":
+ return int.class;
+ case "long":
+ return long.class;
+ case "float":
+ return float.class;
+ case "double":
+ return double.class;
+ case "boolean":
+ return boolean.class;
+ case "char":
+ return char.class;
+ default:
+ throw new ASTException("Unrecognized literal type: " + binding.getName());
+ }
+ }
+
+ private AnnotationClassReference processAnnotation(Annotation node)
+ {
+ ITypeBinding typeBinding = node.resolveTypeBinding();
+ AnnotationClassReference reference;
+ String qualifiedName;
+ if (typeBinding != null)
+ qualifiedName = typeBinding.getQualifiedName();
+ else
+ qualifiedName = resolveClassname(node.getTypeName().toString());
+
+ reference = new AnnotationClassReference(
+ qualifiedName,
+ compilationUnit.getLineNumber(node.getStartPosition()),
+ compilationUnit.getColumnNumber(node.getStartPosition()),
+ node.getLength(),
+ node.toString());
+
+ addAnnotationValues(reference, node);
+
+ return reference;
+ }
+
+ @Override
+ public boolean visit(NormalAnnotation node)
+ {
+ AnnotationClassReference reference = processAnnotation(node);
+ this.classReferences.add(reference);
+
+ // false to avoid recursively processing nested annotations (our code already handles that)
+ return false;
+ }
+
+ @Override
+ public boolean visit(SingleMemberAnnotation node)
+ {
+ AnnotationClassReference reference = processAnnotation(node);
+ this.classReferences.add(reference);
+ return false;
+ }
+
+ @Override
+ public boolean visit(MarkerAnnotation node)
+ {
+ AnnotationClassReference reference = processAnnotation(node);
+ this.classReferences.add(reference);
+ return false;
+ }
+
+ public boolean visit(TypeDeclaration node)
+ {
+ Object clzInterfaces = node.getStructuralProperty(TypeDeclaration.SUPER_INTERFACE_TYPES_PROPERTY);
+ Object clzSuperClasses = node.getStructuralProperty(TypeDeclaration.SUPERCLASS_TYPE_PROPERTY);
+
+ if (clzInterfaces != null)
+ {
+ if (List.class.isAssignableFrom(clzInterfaces.getClass()))
+ {
+ List> clzInterfacesList = (List>) clzInterfaces;
+ for (Object clzInterface : clzInterfacesList)
+ {
+ if (clzInterface instanceof SimpleType)
+ {
+ ITypeBinding resolvedSuperInterface = ((SimpleType) clzInterface).resolveBinding();
+ Stack stack = new Stack();
+ stack.push(resolvedSuperInterface);
+ // register all the implemented interfaces (even superinterfaces)
+ while (!stack.isEmpty())
+ {
+ resolvedSuperInterface = stack.pop();
+ processTypeBinding(resolvedSuperInterface, TypeReferenceLocation.IMPLEMENTS_TYPE,
+ compilationUnit.getLineNumber(node.getStartPosition()),
+ compilationUnit.getColumnNumber(node.getStartPosition()), node.getLength(),
+ extractDefinitionLine(node.toString()));
+ if (resolvedSuperInterface != null)
+ {
+ ITypeBinding[] interfaces = resolvedSuperInterface.getInterfaces();
+ for (ITypeBinding oneInterface : interfaces)
+ {
+ stack.push(oneInterface);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (clzSuperClasses != null)
+ {
+ if (clzSuperClasses instanceof SimpleType)
+ {
+ ITypeBinding resolvedSuperClass = ((SimpleType) clzSuperClasses).resolveBinding();
+ // register all the superClasses up to Object
+ while (resolvedSuperClass != null && !resolvedSuperClass.getQualifiedName().equals("java.lang.Object"))
+ {
+ processTypeBinding(resolvedSuperClass, TypeReferenceLocation.INHERITANCE, compilationUnit.getLineNumber(node.getStartPosition()),
+ compilationUnit.getColumnNumber(node.getStartPosition()), node.getLength(), extractDefinitionLine(node.toString()));
+ resolvedSuperClass = resolvedSuperClass.getSuperclass();
+ }
+ }
+ }
+
+ return super.visit(node);
+ }
+
+ /**
+ * Declaration of the variable within a block
+ */
+ @Override
+ public boolean visit(VariableDeclarationStatement node)
+ {
+ for (int i = 0; i < node.fragments().size(); ++i)
+ {
+ String nodeType = node.getType().toString();
+ nodeType = resolveClassname(nodeType);
+ VariableDeclarationFragment frag = (VariableDeclarationFragment) node.fragments().get(i);
+ this.names.add(frag.getName().getIdentifier());
+ this.nameInstance.put(frag.getName().toString(), nodeType.toString());
+ }
+ processType(node.getType(), TypeReferenceLocation.VARIABLE_DECLARATION,
+ compilationUnit.getLineNumber(node.getStartPosition()),
+ compilationUnit.getColumnNumber(node.getStartPosition()), node.getLength(), node.toString());
+ return super.visit(node);
+ }
+
+ @Override
+ public boolean visit(ImportDeclaration node)
+ {
+ String name = node.getName().toString();
+ if (node.isOnDemand())
+ {
+ wildcardImports.add(name);
+
+ String[] resolvedNames = this.wildcardImportResolver.resolve(name);
+ for (String resolvedName : resolvedNames)
+ {
+ processImport(resolvedName, compilationUnit.getLineNumber(node.getName().getStartPosition()),
+ compilationUnit.getColumnNumber(node.getName().getStartPosition()), node.getName().getLength(), node.toString());
+ }
+ }
+ else
+ {
+ String clzName = StringUtils.substringAfterLast(name, ".");
+ classNameLookedUp.add(clzName);
+ classNameToFQCN.put(clzName, name);
+ processImport(name, compilationUnit.getLineNumber(node.getName().getStartPosition()),
+ compilationUnit.getColumnNumber(node.getName().getStartPosition()), node.getName().getLength(), node.toString());
+ }
+
+ return super.visit(node);
+ }
+
+ /***
+ * Takes the MethodInvocation, and attempts to resolve the types of objects passed into the method invocation.
+ */
+ public boolean visit(MethodInvocation node)
+ {
+ if (!StringUtils.contains(node.toString(), "."))
+ {
+ // it must be a local method. ignore.
+ return true;
+ }
+ List qualifiedInstances = new ArrayList();
+ List argumentsQualified = new ArrayList();
+ // get qualified arguments of the method
+ IMethodBinding resolveTypeBinding = node.resolveMethodBinding();
+ if (resolveTypeBinding != null)
+ {
+ ITypeBinding[] arguments = resolveTypeBinding.getParameterTypes();
+
+ for (ITypeBinding type : arguments)
+ {
+ argumentsQualified.add(type.getQualifiedName());
+ }
+
+ // find the interface declaring the method
+
+ if (resolveTypeBinding != null && resolveTypeBinding.getDeclaringClass() != null)
+ {
+ ITypeBinding declaringClass = resolveTypeBinding.getDeclaringClass();
+ qualifiedInstances.add(declaringClass.getQualifiedName());
+ ITypeBinding[] interfaces = declaringClass.getInterfaces();
+ // Now find all the implemented interfaces having the method called.
+ for (ITypeBinding possibleInterface : interfaces)
+ {
+ IMethodBinding[] declaredMethods = possibleInterface.getDeclaredMethods();
+ if (declaredMethods.length != 0)
+ {
+ for (IMethodBinding interfaceMethod : declaredMethods)
+ {
+ if (interfaceMethod.getName().equals(node.getName().toString()))
+ {
+
+ List interfaceMethodArguments = new ArrayList();
+ for (ITypeBinding type : interfaceMethod.getParameterTypes())
+ {
+ interfaceMethodArguments.add(type.getQualifiedName());
+ }
+ if (interfaceMethodArguments.equals(argumentsQualified))
+ {
+ qualifiedInstances.add(possibleInterface.getQualifiedName());
+ }
+ }
+ }
+ }
+
+ }
+ }
+
+ }
+ else
+ {
+ String nodeName = StringUtils.removeStart(node.toString(), "this.");
+ String objRef = StringUtils.substringBefore(nodeName, "." + node.getName().toString());
+ if (nameInstance.containsKey(objRef))
+ {
+ objRef = nameInstance.get(objRef);
+ }
+ objRef = resolveClassname(objRef);
+
+ // not resolved binding
+ List arguments = node.arguments();
+ for (Expression expression : arguments)
+ {
+ ITypeBinding argumentBinding = expression.resolveTypeBinding();
+ if (argumentBinding != null)
+ {
+ argumentsQualified.add(argumentBinding.getQualifiedName());
+ }
+ else
+ {
+ // TODO: Is toString good option? Just a name of the argument will be saved
+ argumentsQualified.add(expression.toString());
+ }
+ }
+ qualifiedInstances.add(objRef);
+ }
+
+ // register all found qualified names for this method invocation
+ for (String qualifiedInstance : qualifiedInstances)
+ {
+ MethodType methodCall = new MethodType(qualifiedInstance, node.getName().toString(), argumentsQualified);
+ processMethod(methodCall, TypeReferenceLocation.METHOD_CALL, compilationUnit.getLineNumber(node.getName().getStartPosition()),
+ compilationUnit.getColumnNumber(node.getName().getStartPosition()), node.getName().getLength(), node.toString());
+ }
+
+ return super.visit(node);
+ }
+
+ @Override
+ public boolean visit(PackageDeclaration node)
+ {
+ return super.visit(node);
+ }
+
+ @Override
+ public boolean visit(ClassInstanceCreation node)
+ {
+ IMethodBinding constructorBinding = node.resolveConstructorBinding();
+ // ITypeBinding resolveTypeBinding = node.resolveTypeBinding();
+ String qualifiedClass = "";
+ List constructorMethodQualifiedArguments = new ArrayList();
+ if (constructorBinding != null && constructorBinding.getDeclaringClass() != null)
+ {
+ ITypeBinding declaringClass = constructorBinding.getDeclaringClass();
+ qualifiedClass = declaringClass.getQualifiedName();
+ for (ITypeBinding type : constructorBinding.getParameterTypes())
+ {
+ constructorMethodQualifiedArguments.add(type.getQualifiedName());
+ }
+ }
+
+ if (constructorMethodQualifiedArguments.isEmpty() && !node.arguments().isEmpty())
+ {
+ List arguments = node.arguments();
+ arguments.get(0).resolveTypeBinding();
+ for (Expression type : arguments)
+ {
+ ITypeBinding argumentBinding = type.resolveTypeBinding();
+ if (argumentBinding != null)
+ {
+ constructorMethodQualifiedArguments.add(argumentBinding.getQualifiedName());
+ }
+ else
+ {
+ List guessedParam = methodParameterGuesser(Collections.singletonList(type));
+ constructorMethodQualifiedArguments.addAll(guessedParam);
+ }
+
+ }
+ }
+
+ // qualified class may not be resolved in case of anonymous classes
+ if (qualifiedClass == null || qualifiedClass.equals(""))
+ {
+ qualifiedClass = node.getType().toString();
+ qualifiedClass = resolveClassname(qualifiedClass);
+ }
+
+ ConstructorType resolvedConstructor = new ConstructorType(qualifiedClass, constructorMethodQualifiedArguments);
+ processConstructor(resolvedConstructor, compilationUnit.getLineNumber(node.getType().getStartPosition()),
+ compilationUnit.getColumnNumber(node.getType().getStartPosition()), node.getType().getLength(), node.toString());
+
+ return super.visit(node);
+ }
+
+ public static class MethodType
+ {
+ private final String qualifiedName;
+ private final String methodName;
+ private final List qualifiedParameters;
+
+ public MethodType(String qualifiedName, String methodName, List qualifiedParameters)
+ {
+ this.qualifiedName = qualifiedName;
+ this.methodName = methodName;
+
+ if (qualifiedParameters != null)
+ {
+ this.qualifiedParameters = qualifiedParameters;
+ }
+ else
+ {
+ this.qualifiedParameters = new LinkedList();
+ }
+ }
+
+ public String getMethodName()
+ {
+ return methodName;
+ }
+
+ public String getQualifiedName()
+ {
+ return qualifiedName;
+ }
+
+ public List getQualifiedParameters()
+ {
+ return qualifiedParameters;
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder builder = new StringBuilder();
+ builder.append(qualifiedName + "." + methodName);
+ builder.append("(");
+
+ for (int i = 0, j = qualifiedParameters.size(); i < j; i++)
+ {
+ if (i > 0)
+ {
+ builder.append(", ");
+ }
+ String param = qualifiedParameters.get(i);
+ builder.append(param);
+ }
+ builder.append(")");
+
+ return builder.toString();
+ }
+ }
+
+ public static class ConstructorType
+ {
+ private final String qualifiedName;
+ private final List qualifiedParameters;
+
+ public ConstructorType(String qualifiedName, List qualifiedParameters)
+ {
+ this.qualifiedName = qualifiedName;
+ if (qualifiedParameters != null)
+ {
+ this.qualifiedParameters = qualifiedParameters;
+ }
+ else
+ {
+ this.qualifiedParameters = new LinkedList();
+ }
+
+ }
+
+ public String getQualifiedName()
+ {
+ return qualifiedName;
+ }
+
+ public List getQualifiedParameters()
+ {
+ return qualifiedParameters;
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder builder = new StringBuilder();
+ builder.append(qualifiedName);
+ builder.append("(");
+
+ for (int i = 0, j = qualifiedParameters.size(); i < j; i++)
+ {
+ if (i > 0)
+ {
+ builder.append(", ");
+ }
+ String param = qualifiedParameters.get(i);
+ builder.append(param);
+ }
+ builder.append(")");
+
+ return builder.toString();
+ }
+ }
+
+ private List methodParameterGuesser(List> arguements)
+ {
+ List resolvedParams = new ArrayList(arguements.size());
+ for (Object o : arguements)
+ {
+ if (o instanceof SimpleName)
+ {
+ String name = nameInstance.get(o.toString());
+ if (name != null)
+ {
+ resolvedParams.add(name);
+ }
+ else
+ {
+ resolvedParams.add("Undefined");
+ }
+ }
+ else if (o instanceof StringLiteral)
+ {
+ resolvedParams.add("java.lang.String");
+ }
+ else if (o instanceof FieldAccess)
+ {
+ String field = ((FieldAccess) o).getName().toString();
+ if (names.contains(field))
+ {
+ resolvedParams.add(nameInstance.get(field));
+ }
+ else
+ {
+ resolvedParams.add("Undefined");
+ }
+ }
+ else if (o instanceof CastExpression)
+ {
+ String type = ((CastExpression) o).getType().toString();
+ type = qualifyType(type);
+ resolvedParams.add(type);
+ }
+ else if (o instanceof MethodInvocation)
+ {
+ String on = ((MethodInvocation) o).getName().toString();
+ if (StringUtils.equals(on, "toString"))
+ {
+ if (((MethodInvocation) o).arguments().size() == 0)
+ {
+ resolvedParams.add("java.lang.String");
+ }
+ }
+ else
+ {
+ resolvedParams.add("Undefined");
+ }
+ }
+ else if (o instanceof NumberLiteral)
+ {
+ if (StringUtils.endsWith(o.toString(), "L"))
+ {
+ resolvedParams.add("long");
+ }
+ else if (StringUtils.endsWith(o.toString(), "f"))
+ {
+ resolvedParams.add("float");
+ }
+ else if (StringUtils.endsWith(o.toString(), "d"))
+ {
+ resolvedParams.add("double");
+ }
+ else
+ {
+ resolvedParams.add("int");
+ }
+ }
+ else if (o instanceof BooleanLiteral)
+ {
+ resolvedParams.add("boolean");
+ }
+ else if (o instanceof ClassInstanceCreation)
+ {
+ String nodeType = ((ClassInstanceCreation) o).getType().toString();
+ nodeType = resolveClassname(nodeType);
+ resolvedParams.add(nodeType);
+ }
+ else if (o instanceof org.eclipse.jdt.core.dom.CharacterLiteral)
+ {
+ resolvedParams.add("char");
+ }
+ else if (o instanceof InfixExpression)
+ {
+ String expression = o.toString();
+ if (StringUtils.contains(expression, "\""))
+ {
+ resolvedParams.add("java.lang.String");
+ }
+ else
+ {
+ resolvedParams.add("Undefined");
+ }
+ }
+ else
+ {
+ resolvedParams.add("Undefined");
+ }
+ }
+ return resolvedParams;
+ }
+
+ private String resolveClassname(String sourceClassname)
+ {
+ // If the type contains a "." assume that it is fully qualified.
+ // FIXME - This is a carryover from the original Windup code, and I don't think
+ // that this assumption is valid.
+ if (!StringUtils.contains(sourceClassname, "."))
+ {
+ // Check if we have already looked this one up
+ if (classNameLookedUp.contains(sourceClassname))
+ {
+ // if yes, then just use the looked up name from the map
+ String qualifiedName = classNameToFQCN.get(sourceClassname);
+ if (qualifiedName != null)
+ {
+ return qualifiedName;
+ }
+ else
+ {
+ // otherwise, just return the provided name (unchanged)
+ return sourceClassname;
+ }
+ }
+ else
+ {
+ classNameLookedUp.add(sourceClassname);
+ String resolvedClassName = this.wildcardImportResolver.resolve(this.wildcardImports, sourceClassname);
+ if (resolvedClassName != null)
+ {
+ classNameToFQCN.put(sourceClassname, resolvedClassName);
+ return resolvedClassName;
+ }
+ // nothing was found, so just return the original value
+ return sourceClassname;
+ }
+ }
+ else
+ {
+ return sourceClassname;
+ }
+ }
+
+ private String qualifyType(String objRef)
+ {
+ // temporarily remove to resolve arrays
+ objRef = StringUtils.removeEnd(objRef, "[]");
+ if (nameInstance.containsKey(objRef))
+ {
+ objRef = nameInstance.get(objRef);
+ }
+ objRef = resolveClassname(objRef);
+ return objRef;
+ }
+
+}
diff --git a/java-ast/addon/src/main/java/org/jboss/windup/ast/java/BatchASTListener.java b/java-ast/addon/src/main/java/org/jboss/windup/ast/java/BatchASTListener.java
new file mode 100644
index 0000000000..7edde611ab
--- /dev/null
+++ b/java-ast/addon/src/main/java/org/jboss/windup/ast/java/BatchASTListener.java
@@ -0,0 +1,24 @@
+package org.jboss.windup.ast.java;
+
+import java.nio.file.Path;
+import java.util.List;
+
+import org.jboss.windup.ast.java.data.ClassReference;
+
+/**
+ * Provides a callback to indicate that processing has completed for a particular file.
+ *
+ * @author Jesse Sightler
+ */
+public interface BatchASTListener
+{
+ /**
+ * Called to indicate that processing has completed on the specified file.
+ */
+ void processed(Path filePath, List classReferences);
+
+ /**
+ * Called on parse failures. Note that some failures will not trigger this method, due to limitations of JDT's batch API.
+ */
+ void failed(Path filePath, Throwable cause);
+}
diff --git a/java-ast/addon/src/main/java/org/jboss/windup/ast/java/BatchASTProcessor.java b/java-ast/addon/src/main/java/org/jboss/windup/ast/java/BatchASTProcessor.java
new file mode 100644
index 0000000000..87138feb78
--- /dev/null
+++ b/java-ast/addon/src/main/java/org/jboss/windup/ast/java/BatchASTProcessor.java
@@ -0,0 +1,91 @@
+package org.jboss.windup.ast.java;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTParser;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.FileASTRequestor;
+import org.jboss.windup.ast.java.data.ClassReference;
+
+/**
+ * Processes multiple files at a time in order to improve performance.
+ *
+ * @author Jesse Sightler
+ */
+public class BatchASTProcessor
+{
+ private static final int BATCH_SIZE = 1000;
+
+ /**
+ * Process the given batch of files and pass the results back to the listener as each file is processed.
+ */
+ public static void analyze(final BatchASTListener listener, WildcardImportResolver importResolver, Set libraryPaths,
+ Set sourcePaths, Iterable sourceFiles)
+ {
+ ASTParser parser = ASTParser.newParser(AST.JLS8);
+
+ String[] encodings = null;
+ String[] bindingKeys = new String[0];
+
+ final ASTReferenceResolver referenceResolver = new ASTReferenceResolver(importResolver);
+
+ FileASTRequestor requestor = new FileASTRequestor()
+ {
+ @Override
+ public void acceptAST(String sourcePath, CompilationUnit ast)
+ {
+ try
+ {
+ super.acceptAST(sourcePath, ast);
+ List references = referenceResolver.analyze(sourcePath, ast);
+ listener.processed(Paths.get(sourcePath), references);
+ }
+ catch (Throwable t)
+ {
+ listener.failed(Paths.get(sourcePath), t);
+ }
+ }
+ };
+
+ Iterator pathIterator = sourceFiles.iterator();
+ List batch = new ArrayList<>(BATCH_SIZE);
+ while (pathIterator.hasNext())
+ {
+ batch.add(pathIterator.next().toAbsolutePath().toString());
+
+ if (batch.size() == BATCH_SIZE || !pathIterator.hasNext())
+ {
+ parser.setEnvironment(libraryPaths.toArray(new String[libraryPaths.size()]), sourcePaths.toArray(new String[sourcePaths.size()]),
+ null, true);
+ parser.setBindingsRecovery(false);
+ parser.setResolveBindings(true);
+
+ Map options = JavaCore.getOptions();
+ JavaCore.setComplianceOptions(JavaCore.VERSION_1_8, options);
+
+ // these options seem to slightly reduce the number of times that JDT aborts on compilation errors
+ options.put(JavaCore.CORE_INCOMPLETE_CLASSPATH, "warning");
+ options.put(JavaCore.COMPILER_PB_ENUM_IDENTIFIER, "warning");
+ options.put(JavaCore.COMPILER_PB_FORBIDDEN_REFERENCE, "warning");
+ options.put(JavaCore.CORE_CIRCULAR_CLASSPATH, "warning");
+ options.put(JavaCore.COMPILER_PB_ASSERT_IDENTIFIER, "warning");
+ options.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_VIOLATION, "warning");
+ options.put(JavaCore.CORE_JAVA_BUILD_INVALID_CLASSPATH, "ignore");
+ options.put(JavaCore.COMPILER_PB_NULL_ANNOTATION_INFERENCE_CONFLICT, "warning");
+ options.put(JavaCore.CORE_OUTPUT_LOCATION_OVERLAPPING_ANOTHER_SOURCE, "warning");
+
+ parser.setCompilerOptions(options);
+ parser.createASTs(batch.toArray(new String[batch.size()]), encodings, bindingKeys, requestor, null);
+ batch.clear();
+ }
+ }
+ }
+}
diff --git a/java-ast/addon/src/main/java/org/jboss/windup/ast/java/WildcardImportResolver.java b/java-ast/addon/src/main/java/org/jboss/windup/ast/java/WildcardImportResolver.java
index 1d3a094def..246528175a 100644
--- a/java-ast/addon/src/main/java/org/jboss/windup/ast/java/WildcardImportResolver.java
+++ b/java-ast/addon/src/main/java/org/jboss/windup/ast/java/WildcardImportResolver.java
@@ -2,9 +2,18 @@
import java.util.List;
+/**
+ * Provides a pluggable lookup mechanism for resolving wildcard imports.
+ */
public interface WildcardImportResolver
{
+ /**
+ * Resolve the given name based upon the provided wildcard imports.
+ */
String resolve(List wildcardImports, String name);
+ /**
+ * Find all potential imports for this wildcard package name.
+ */
String[] resolve(String wildcardImportPackageName);
}
diff --git a/java-ast/addon/src/main/java/org/jboss/windup/ast/java/data/ClassReference.java b/java-ast/addon/src/main/java/org/jboss/windup/ast/java/data/ClassReference.java
index e8fb7e42bc..2fac9abc89 100644
--- a/java-ast/addon/src/main/java/org/jboss/windup/ast/java/data/ClassReference.java
+++ b/java-ast/addon/src/main/java/org/jboss/windup/ast/java/data/ClassReference.java
@@ -30,11 +30,17 @@ public ClassReference(String qualifiedName, TypeReferenceLocation location, int
this.line= line;
}
+ /**
+ * Contains the raw text represented by this reference (class names are not resolved).
+ */
public String getLine()
{
return line;
}
+ /**
+ * Contains the raw text represented by this reference (class names are not resolved).
+ */
public void setLine(String line)
{
this.line = line;
diff --git a/java-ast/addon/src/main/java/org/jboss/windup/ast/java/data/ClassReferences.java b/java-ast/addon/src/main/java/org/jboss/windup/ast/java/data/ClassReferences.java
deleted file mode 100644
index 880542a2bf..0000000000
--- a/java-ast/addon/src/main/java/org/jboss/windup/ast/java/data/ClassReferences.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package org.jboss.windup.ast.java.data;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Contains a list of {@link ClassReference}s, which themselves describe the contents of the Java source file, and all of the things that it
- * directly references.
- *
- * @author Jesse Sightler
- *
- */
-public class ClassReferences
-{
- private List references = new ArrayList<>();
-
- /**
- * Adds a {@link ClassReference}
- */
- public void addReference(ClassReference reference)
- {
- this.references.add(reference);
- }
-
- /**
- * Gets the list of all {@link ClassReference}s
- */
- public List getReferences()
- {
- return Collections.unmodifiableList(this.references);
- }
-}
diff --git a/java-ast/tests/src/test/java/org/jboss/windup/ast/java/test/JavaASTProcessorTest.java b/java-ast/tests/src/test/java/org/jboss/windup/ast/java/test/JavaASTReferenceResolverTest.java
similarity index 63%
rename from java-ast/tests/src/test/java/org/jboss/windup/ast/java/test/JavaASTProcessorTest.java
rename to java-ast/tests/src/test/java/org/jboss/windup/ast/java/test/JavaASTReferenceResolverTest.java
index 8f43e5f2f3..25ff6515ad 100644
--- a/java-ast/tests/src/test/java/org/jboss/windup/ast/java/test/JavaASTProcessorTest.java
+++ b/java-ast/tests/src/test/java/org/jboss/windup/ast/java/test/JavaASTReferenceResolverTest.java
@@ -1,71 +1,71 @@
package org.jboss.windup.ast.java.test;
import java.nio.file.Paths;
+import java.util.List;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.windup.ast.java.ASTProcessor;
import org.jboss.windup.ast.java.data.ClassReference;
-import org.jboss.windup.ast.java.data.ClassReferences;
import org.jboss.windup.ast.java.data.TypeReferenceLocation;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(Arquillian.class)
-public class JavaASTProcessorTest extends AbstractJavaASTTest
+public class JavaASTReferenceResolverTest extends AbstractJavaASTTest
{
@Test
public void testHelloWorld()
{
- ClassReferences references = ASTProcessor.analyzeJavaFile(getLibraryPaths(), getSourcePaths(),
- Paths.get("src/test/resources/testclasses/helloworld/HelloWorld.java"));
+ List references = ASTProcessor.analyze(getLibraryPaths(), getSourcePaths(),
+ Paths.get("src/test/resources/testclasses/helloworld/HelloWorld.java"));
- for (ClassReference reference : references.getReferences())
+ for (ClassReference reference : references)
{
System.out.println("Reference: " + reference);
}
- Assert.assertTrue(references.getReferences().contains(
+ Assert.assertTrue(references.contains(
new ClassReference("testclasses.helloworld.HelloWorld", TypeReferenceLocation.TYPE, 3, 0, 174, "public class HelloWorld {")));
- Assert.assertTrue(references.getReferences().contains(
+ Assert.assertTrue(references.contains(
new ClassReference("testclasses.helloworld.HelloWorld.main(String[])", TypeReferenceLocation.METHOD, 5, 23, 4,
"public static void main(String[] argv) throws Exception {")));
- Assert.assertTrue(references.getReferences().contains(
+ Assert.assertTrue(references.contains(
new ClassReference("void", TypeReferenceLocation.RETURN_TYPE, 5, 4, 108,
"public static void main(String[] argv) throws Exception {")));
- Assert.assertTrue(references.getReferences().contains(
+ Assert.assertTrue(references.contains(
new ClassReference("java.lang.String[]", TypeReferenceLocation.METHOD_PARAMETER, 5, 4, 108,
"public static void main(String[] argv) throws Exception {")));
- Assert.assertTrue(references.getReferences().contains(
+ Assert.assertTrue(references.contains(
new ClassReference("java.lang.Exception", TypeReferenceLocation.THROWS_METHOD_DECLARATION, 5, 51, 9,
"public static void main(String[] argv) throws Exception {")));
- Assert.assertTrue(references.getReferences().contains(
+ Assert.assertTrue(references.contains(
new ClassReference("java.io.PrintStream.println(java.lang.String)", TypeReferenceLocation.METHOD_CALL, 6, 19, 7,
"System.out.println(\"Hello world!\")")));
- Assert.assertEquals(6, references.getReferences().size());
+ Assert.assertEquals(6, references.size());
}
@Test
public void testSimpleMain()
{
- ClassReferences references = ASTProcessor.analyzeJavaFile(getLibraryPaths(), getSourcePaths(),
- Paths.get("src/test/resources/testclasses/simple/Main.java"));
+ List references = ASTProcessor.analyze(getLibraryPaths(), getSourcePaths(),
+ Paths.get("src/test/resources/testclasses/simple/Main.java"));
- for (ClassReference reference : references.getReferences())
+ for (ClassReference reference : references)
{
System.out.println("Reference: " + reference);
}
- Assert.assertTrue(references.getReferences().contains(
+ Assert.assertTrue(references.contains(
new ClassReference("testclasses.simple.MyBClass", TypeReferenceLocation.VARIABLE_DECLARATION, 9, 8, 28,
"MyBClass b=new MyBClass();")));
- Assert.assertTrue(references.getReferences().contains(
+ Assert.assertTrue(references.contains(
new ClassReference("testclasses.simple.MyAClass.interfaceMethod()", TypeReferenceLocation.METHOD_CALL, 12, 26, 15,
"c.returnAnother().interfaceMethod()")));
- Assert.assertTrue(references.getReferences().contains(
+ Assert.assertTrue(references.contains(
new ClassReference("testclasses.simple.SomeInterface.interfaceMethod()", TypeReferenceLocation.METHOD_CALL, 12, 26, 15,
"c.returnAnother().interfaceMethod()")));
- Assert.assertTrue(references.getReferences().contains(
+ Assert.assertTrue(references.contains(
new ClassReference("testclasses.simple.ClassReturningAnother.returnAnother()", TypeReferenceLocation.METHOD_CALL, 12, 10, 13,
"c.returnAnother()")));
}
@@ -73,18 +73,18 @@ public void testSimpleMain()
@Test
public void testMyBClass()
{
- ClassReferences references = ASTProcessor.analyzeJavaFile(getLibraryPaths(), getSourcePaths(),
- Paths.get("src/test/resources/testclasses/simple/MyBClass.java"));
+ List references = ASTProcessor.analyze(getLibraryPaths(), getSourcePaths(),
+ Paths.get("src/test/resources/testclasses/simple/MyBClass.java"));
- for (ClassReference reference : references.getReferences())
+ for (ClassReference reference : references)
{
System.out.println("Reference: " + reference);
}
- Assert.assertTrue(references.getReferences().contains(
+ Assert.assertTrue(references.contains(
new ClassReference("testclasses.simple.MyBClass", TypeReferenceLocation.TYPE, 4, 0, 161,
"public class MyBClass extends MyAClass {")));
- Assert.assertTrue(references.getReferences().contains(
+ Assert.assertTrue(references.contains(
new ClassReference("testclasses.simple.MyAClass", TypeReferenceLocation.TYPE, 4, 0, 161,
"public class MyBClass extends MyAClass {")));
}
@@ -92,41 +92,42 @@ public void testMyBClass()
@Test
public void testMyAClass()
{
- ClassReferences references = ASTProcessor.analyzeJavaFile(getLibraryPaths(), getSourcePaths(),
- Paths.get("src/test/resources/testclasses/simple/MyAClass.java"));
+ List references = ASTProcessor.analyze(getLibraryPaths(), getSourcePaths(),
+ Paths.get("src/test/resources/testclasses/simple/MyAClass.java"));
- for (ClassReference reference : references.getReferences())
+ for (ClassReference reference : references)
{
System.out.println("Reference: " + reference);
}
- Assert.assertTrue(references.getReferences().contains(
+ Assert.assertTrue(references.contains(
new ClassReference("testclasses.simple.MyAClass", TypeReferenceLocation.TYPE, 3, 0, 128,
"public class MyAClass implements SomeInterface {")));
- Assert.assertTrue(references.getReferences().contains(
+ Assert.assertTrue(references.contains(
new ClassReference("testclasses.simple.SomeInterface", TypeReferenceLocation.IMPLEMENTS_TYPE, 3, 0, 99,
"public class MyAClass implements SomeInterface {")));
}
+
@Test
public void testJavaLangReferences()
{
- ClassReferences references = ASTProcessor.analyzeJavaFile(getLibraryPaths(), getSourcePaths(),
- Paths.get("src/test/resources/testclasses/javalang/JavaLangReferences.java"));
+ List references = ASTProcessor.analyze(getLibraryPaths(), getSourcePaths(),
+ Paths.get("src/test/resources/testclasses/javalang/JavaLangReferences.java"));
- for (ClassReference reference : references.getReferences())
+ for (ClassReference reference : references)
{
System.out.println("Reference: " + reference);
}
- Assert.assertTrue(references.getReferences().contains(
+ Assert.assertTrue(references.contains(
new ClassReference("testclasses.javalang.JavaLangReferences", TypeReferenceLocation.TYPE, 3, 0, 191,
"public class JavaLangReferences {")));
- Assert.assertTrue(references.getReferences().contains(
+ Assert.assertTrue(references.contains(
new ClassReference("void", TypeReferenceLocation.RETURN_TYPE, 5, 4, 119, "public void someMethod(){")));
- Assert.assertTrue(references.getReferences().contains(
+ Assert.assertTrue(references.contains(
new ClassReference("java.lang.String", TypeReferenceLocation.VARIABLE_DECLARATION, 7, 8, 39,
"String a=\"This is an example String\";")));
- Assert.assertTrue(references.getReferences().contains(
+ Assert.assertTrue(references.contains(
new ClassReference("java.lang.String", TypeReferenceLocation.VARIABLE_DECLARATION, 8, 8, 26, "String b=a.substring(1);")));
- Assert.assertTrue(references.getReferences().contains(
+ Assert.assertTrue(references.contains(
new ClassReference("java.lang.String.substring(int)", TypeReferenceLocation.METHOD_CALL, 8, 21, 9, "a.substring(1)")));
}
diff --git a/java-ast/tests/src/test/java/org/jboss/windup/ast/java/test/JavaAnnotationScanningTest.java b/java-ast/tests/src/test/java/org/jboss/windup/ast/java/test/JavaAnnotationScanningTest.java
index 274dbed3ac..de342e7713 100644
--- a/java-ast/tests/src/test/java/org/jboss/windup/ast/java/test/JavaAnnotationScanningTest.java
+++ b/java-ast/tests/src/test/java/org/jboss/windup/ast/java/test/JavaAnnotationScanningTest.java
@@ -1,11 +1,11 @@
package org.jboss.windup.ast.java.test;
import java.nio.file.Paths;
+import java.util.List;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.windup.ast.java.ASTProcessor;
import org.jboss.windup.ast.java.data.ClassReference;
-import org.jboss.windup.ast.java.data.ClassReferences;
import org.jboss.windup.ast.java.data.annotations.AnnotationArrayValue;
import org.jboss.windup.ast.java.data.annotations.AnnotationClassReference;
import org.jboss.windup.ast.java.data.annotations.AnnotationLiteralValue;
@@ -21,12 +21,12 @@ public class JavaAnnotationScanningTest extends AbstractJavaASTTest
@Test
public void testSimpleAnnotatedClass()
{
- ClassReferences references = ASTProcessor.analyzeJavaFile(getLibraryPaths(), getSourcePaths(),
+ List references = ASTProcessor.analyze(getLibraryPaths(), getSourcePaths(),
Paths.get("src/test/resources/testclasses/annotations/basic/SimpleAnnotatedClass.java"));
boolean foundSimpleAnnotation = false;
boolean foundSingleMemberAnnotation = false;
- for (ClassReference reference : references.getReferences())
+ for (ClassReference reference : references)
{
System.out.println("Reference: " + reference);
if (reference instanceof AnnotationClassReference)
@@ -55,11 +55,11 @@ else if (reference.getQualifiedName().equals("testclasses.annotations.basic.Simp
@Test
public void testComplexAnnotatedClass()
{
- ClassReferences references = ASTProcessor.analyzeJavaFile(getLibraryPaths(), getSourcePaths(),
+ List references = ASTProcessor.analyze(getLibraryPaths(), getSourcePaths(),
Paths.get("src/test/resources/testclasses/annotations/complex/ComplexAnnotatedClass.java"));
boolean foundAnnotation = false;
- for (ClassReference reference : references.getReferences())
+ for (ClassReference reference : references)
{
System.out.println("Reference: " + reference);
if (reference instanceof AnnotationClassReference)
diff --git a/rules-java/api/src/main/java/org/jboss/windup/rules/apps/java/scan/provider/AnalyzeJavaFilesRuleProvider.java b/rules-java/api/src/main/java/org/jboss/windup/rules/apps/java/scan/provider/AnalyzeJavaFilesRuleProvider.java
index e26f244281..2e6eb2881b 100644
--- a/rules-java/api/src/main/java/org/jboss/windup/rules/apps/java/scan/provider/AnalyzeJavaFilesRuleProvider.java
+++ b/rules-java/api/src/main/java/org/jboss/windup/rules/apps/java/scan/provider/AnalyzeJavaFilesRuleProvider.java
@@ -1,18 +1,24 @@
package org.jboss.windup.rules.apps.java.scan.provider;
-import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.inject.Inject;
import org.jboss.windup.ast.java.ASTProcessor;
+import org.jboss.windup.ast.java.BatchASTListener;
+import org.jboss.windup.ast.java.BatchASTProcessor;
import org.jboss.windup.ast.java.data.ClassReference;
-import org.jboss.windup.ast.java.data.ClassReferences;
import org.jboss.windup.ast.java.data.TypeReferenceLocation;
import org.jboss.windup.ast.java.data.annotations.AnnotationArrayValue;
import org.jboss.windup.ast.java.data.annotations.AnnotationClassReference;
@@ -21,13 +27,8 @@
import org.jboss.windup.config.AbstractRuleProvider;
import org.jboss.windup.config.GraphRewrite;
import org.jboss.windup.config.metadata.MetadataBuilder;
-import org.jboss.windup.config.operation.Commit;
import org.jboss.windup.config.operation.GraphOperation;
-import org.jboss.windup.config.operation.Iteration;
-import org.jboss.windup.config.operation.IterationProgress;
-import org.jboss.windup.config.operation.iteration.AbstractIterationOperation;
import org.jboss.windup.config.phase.InitialAnalysisPhase;
-import org.jboss.windup.config.query.Query;
import org.jboss.windup.graph.GraphContext;
import org.jboss.windup.graph.model.resource.FileModel;
import org.jboss.windup.graph.service.GraphService;
@@ -56,9 +57,10 @@
*/
public class AnalyzeJavaFilesRuleProvider extends AbstractRuleProvider
{
+ public static final int COMMIT_INTERVAL = 750;
+ public static final int LOG_INTERVAL = 250;
private static Logger LOG = Logging.get(AnalyzeJavaFilesRuleProvider.class);
- private ASTProcessor processor;
@Inject
private WindupWildcardImportResolver importResolver;
@@ -75,111 +77,155 @@ public Configuration getConfiguration(GraphContext context)
{
return ConfigurationBuilder.begin()
.addRule()
- .when(Query.fromType(JavaSourceFileModel.class))
- .perform(
- Iteration.over().perform(
- new ParseSourceOperation()
- .and(IterationProgress.monitoring("Analyzed Java File: ", 250))
- .and(Commit.every(10))
- ).endIteration()
- .and(new ClearClasspathCache())
- );
- }
- // @formatter:on
+ .perform(new ParseSourceOperation());
- private final class ClearClasspathCache extends GraphOperation
- {
- @Override
- public void perform(GraphRewrite event, EvaluationContext context)
- {
- processor = null;
- }
+
+ //.and(IterationProgress.monitoring("Analyzed Java File: ", 250))
+ //.and(Commit.every(10))
}
+ // @formatter:on
- private final class ParseSourceOperation extends AbstractIterationOperation
+ private final class ParseSourceOperation extends GraphOperation
{
- public void perform(GraphRewrite event, EvaluationContext context, JavaSourceFileModel payload)
+ public void perform(final GraphRewrite event, EvaluationContext context)
{
ExecutionStatistics.get().begin("AnalyzeJavaFilesRuleProvider.analyzeFile");
try
{
WindupJavaConfigurationService windupJavaConfigurationService = new WindupJavaConfigurationService(
event.getGraphContext());
- if (!windupJavaConfigurationService.shouldScanPackage(payload.getPackageName()))
+
+ GraphService service = new GraphService<>(event.getGraphContext(), JavaSourceFileModel.class);
+ Iterable allJavaSourceModels = service.findAll();
+
+ final Set allSourceFiles = new TreeSet<>();
+ final Map sourcePathToFileModel = new TreeMap<>();
+
+ Set sourcePaths = new HashSet<>();
+ for (JavaSourceFileModel javaFile : allJavaSourceModels)
{
- // should not analyze this one, skip it
- return;
+ FileModel rootSourceFolder = javaFile.getRootSourceFolder();
+ if (rootSourceFolder != null)
+ {
+ sourcePaths.add(rootSourceFolder.getFilePath());
+ }
+
+ if (windupJavaConfigurationService.shouldScanPackage(javaFile.getPackageName()))
+ {
+ Path path = Paths.get(javaFile.getFilePath());
+ allSourceFiles.add(path);
+ sourcePathToFileModel.put(path, javaFile);
+ }
}
- GraphService service = new GraphService(event.getGraphContext(), JavaSourceFileModel.class);
- if (processor == null)
+ GraphService libraryService = new GraphService(event.getGraphContext(), JarArchiveModel.class);
+
+ Iterable libraries = libraryService.findAll();
+ Set libraryPaths = new HashSet<>();
+ for (JarArchiveModel library : libraries)
{
- Iterable allJavaSourceModels = service.findAll();
- Set sourcePaths = new HashSet<>();
- for (JavaSourceFileModel javaFile : allJavaSourceModels)
+ if (library.getUnzippedDirectory() != null)
{
- FileModel rootSourceFolder = javaFile.getRootSourceFolder();
- if (rootSourceFolder != null)
- {
- sourcePaths.add(rootSourceFolder.getFilePath());
- }
+ libraryPaths.add(library.getUnzippedDirectory().getFilePath());
}
+ else
+ {
+ libraryPaths.add(library.getFilePath());
+ }
+ }
+ ExecutionStatistics.get().begin("AnalyzeJavaFilesRuleProvider.parseFiles");
+ try
+ {
+ WindupWildcardImportResolver.setGraphContext(event.getGraphContext());
+ final int totalToProcess = allSourceFiles.size();
+ final AtomicInteger numberProcessed = new AtomicInteger(0);
- GraphService libraryService = new GraphService(event.getGraphContext(), JarArchiveModel.class);
-
- Iterable libraries = libraryService.findAll();
- Set libraryPaths = new HashSet<>();
- for (JarArchiveModel library : libraries)
+ final Set processedPaths = new HashSet<>(allSourceFiles.size());
+ BatchASTListener listener = new BatchASTListener()
{
- if (library.getUnzippedDirectory() != null)
+ @Override
+ public void processed(Path filePath, List references)
{
- libraryPaths.add(library.getUnzippedDirectory().getFilePath());
+ processedPaths.add(filePath);
+ processReferences(event.getGraphContext(), sourcePathToFileModel, filePath, references);
+
+ numberProcessed.incrementAndGet();
+ if (numberProcessed.get() % LOG_INTERVAL == 0)
+ {
+ LOG.info("Analyzed Java File: " + numberProcessed.get() + " / " + totalToProcess);
+ }
+
+ if (numberProcessed.get() % COMMIT_INTERVAL == 0)
+ {
+ event.getGraphContext().getGraph().getBaseGraph().commit();
+ }
}
- else
+
+ @Override
+ public void failed(Path filePath, Throwable cause)
{
- libraryPaths.add(library.getFilePath());
+ LOG.log(Level.WARNING, "Failed to process: " + filePath + " due to: " + cause.getMessage(), cause);
+ ClassificationService classificationService = new ClassificationService(event.getGraphContext());
+ JavaSourceFileModel sourceFileModel = sourcePathToFileModel.get(filePath);
+ classificationService.attachClassification(sourceFileModel, JavaSourceFileModel.UNPARSEABLE_JAVA_CLASSIFICATION,
+ JavaSourceFileModel.UNPARSEABLE_JAVA_DESCRIPTION);
}
- }
- processor = new ASTProcessor(importResolver, libraryPaths, sourcePaths);
- }
- File sourceFile = payload.asFile();
+ };
- ExecutionStatistics.get().begin("AnalyzeJavaFilesRuleProvider.parseFile");
- try
- {
- WindupWildcardImportResolver.setGraphContext(event.getGraphContext());
- ClassReferences references = processor.analyzeFile(sourceFile.toPath());
- TypeReferenceService typeReferenceService = new TypeReferenceService(event.getGraphContext());
- for (ClassReference reference : references.getReferences())
+ Set filesToProcess = new TreeSet<>(allSourceFiles);
+ BatchASTProcessor.analyze(listener, importResolver, libraryPaths, sourcePaths, filesToProcess);
+ filesToProcess.removeAll(processedPaths);
+ processedPaths.clear();
+
+ if (!filesToProcess.isEmpty())
{
- // we are always interested in types + anything that the TypeInterestFactory has registered
- if (reference.getLocation() == TypeReferenceLocation.TYPE
- || TypeInterestFactory.matchesAny(reference.getQualifiedName(), reference.getLocation()))
+ // try these one file at a time
+ for (Path unprocessed : filesToProcess)
{
- JavaTypeReferenceModel typeReference = typeReferenceService.createTypeReference(payload, reference.getLocation(),
- reference.getLineNumber(), reference.getColumn(), reference.getLength(), reference.getQualifiedName(),
- reference.getLine());
- if (reference instanceof AnnotationClassReference)
+ try
+ {
+ List references = ASTProcessor.analyze(importResolver, libraryPaths, sourcePaths, unprocessed);
+ processReferences(event.getGraphContext(), sourcePathToFileModel, unprocessed, references);
+ processedPaths.add(unprocessed);
+ }
+ catch (Exception e)
{
- Map annotationValues = ((AnnotationClassReference) reference).getAnnotationValues();
- addAnnotationValues(event.getGraphContext(), typeReference, annotationValues);
+ LOG.log(Level.WARNING, "Failed to process: " + unprocessed + " due to: " + e.getMessage(), e);
+ ClassificationService classificationService = new ClassificationService(event.getGraphContext());
+ JavaSourceFileModel sourceFileModel = sourcePathToFileModel.get(unprocessed);
+ classificationService.attachClassification(sourceFileModel, JavaSourceFileModel.UNPARSEABLE_JAVA_CLASSIFICATION,
+ JavaSourceFileModel.UNPARSEABLE_JAVA_DESCRIPTION);
}
}
}
- ExecutionStatistics.get().end("AnalyzeJavaFilesRuleProvider.parseFile");
+ filesToProcess.removeAll(processedPaths);
+
+ if (!filesToProcess.isEmpty())
+ {
+ ClassificationService classificationService = new ClassificationService(event.getGraphContext());
+
+ StringBuilder message = new StringBuilder();
+ message.append("Failed to process " + filesToProcess.size() + " files:\n");
+ for (Path unprocessed : filesToProcess)
+ {
+ JavaSourceFileModel sourceFileModel = sourcePathToFileModel.get(unprocessed);
+ message.append("\tFailed to process: " + unprocessed + "\n");
+ classificationService.attachClassification(sourceFileModel, JavaSourceFileModel.UNPARSEABLE_JAVA_CLASSIFICATION,
+ JavaSourceFileModel.UNPARSEABLE_JAVA_DESCRIPTION);
+ }
+ LOG.warning(message.toString());
+ }
+
+ ExecutionStatistics.get().end("AnalyzeJavaFilesRuleProvider.parseFiles");
}
catch (Exception e)
{
- LOG.log(Level.WARNING, "Could not analyze java file: " + payload.getFilePath() + " due to: " + e.getMessage(), e);
- ClassificationService classificationService = new ClassificationService(event.getGraphContext());
- classificationService.attachClassification(payload, JavaSourceFileModel.UNPARSEABLE_JAVA_CLASSIFICATION,
- JavaSourceFileModel.UNPARSEABLE_JAVA_DESCRIPTION);
+ LOG.log(Level.SEVERE, "Could not analyze java files: " + e.getMessage(), e);
}
finally
{
WindupWildcardImportResolver.setGraphContext(null);
}
-
}
finally
{
@@ -187,6 +233,31 @@ public void perform(GraphRewrite event, EvaluationContext context, JavaSourceFil
}
}
+ private void processReferences(GraphContext context, Map sourcePathToFileModel, Path filePath,
+ List references)
+ {
+ TypeReferenceService typeReferenceService = new TypeReferenceService(context);
+ for (ClassReference reference : references)
+ {
+ // we are always interested in types + anything that the TypeInterestFactory has registered
+ if (reference.getLocation() == TypeReferenceLocation.TYPE
+ || TypeInterestFactory.matchesAny(reference.getQualifiedName(), reference.getLocation()))
+ {
+ JavaSourceFileModel javaSourceModel = sourcePathToFileModel.get(filePath);
+ JavaTypeReferenceModel typeReference = typeReferenceService.createTypeReference(javaSourceModel,
+ reference.getLocation(),
+ reference.getLineNumber(), reference.getColumn(), reference.getLength(),
+ reference.getQualifiedName(),
+ reference.getLine());
+ if (reference instanceof AnnotationClassReference)
+ {
+ Map annotationValues = ((AnnotationClassReference) reference).getAnnotationValues();
+ addAnnotationValues(context, typeReference, annotationValues);
+ }
+ }
+ }
+ }
+
/**
* Adds parameters contained in the annotation into the annotation type reference
*/
diff --git a/rules-xml/addon/src/main/java/org/jboss/windup/rules/apps/xml/service/XmlFileService.java b/rules-xml/addon/src/main/java/org/jboss/windup/rules/apps/xml/service/XmlFileService.java
index b746712176..39ea30e196 100644
--- a/rules-xml/addon/src/main/java/org/jboss/windup/rules/apps/xml/service/XmlFileService.java
+++ b/rules-xml/addon/src/main/java/org/jboss/windup/rules/apps/xml/service/XmlFileService.java
@@ -46,20 +46,23 @@ public Document loadDocumentQuiet(XmlFileModel model)
}
XMLDocumentCache.Result cacheResult = XMLDocumentCache.get(model);
- Document document = null;
+ Document document;
if (cacheResult.isParseFailure())
{
- LOG.log(Level.WARNING, "Not loading entity: " + model.getFilePath() + ", due to previous parse failures");
+ LOG.log(Level.FINE, "Not loading entity: " + model.getFilePath() + ", due to previous parse failures");
+ document = null;
}
else if (cacheResult.getDocument() == null)
{
try (InputStream is = model.asInputStream())
{
document = LocationAwareXmlReader.readXML(is);
+ XMLDocumentCache.cache(model, document);
}
catch (SAXException e)
{
XMLDocumentCache.cacheParseFailure(model);
+ document = null;
LOG.log(Level.WARNING,
"Failed to parse xml entity: " + model.getFilePath() + ", due to: " + e.getMessage());
classificationService.attachClassification(model, XmlFileModel.UNPARSEABLE_XML_CLASSIFICATION,
@@ -68,14 +71,12 @@ else if (cacheResult.getDocument() == null)
catch (IOException e)
{
XMLDocumentCache.cacheParseFailure(model);
+ document = null;
LOG.log(Level.WARNING,
"Failed to parse xml entity: " + model.getFilePath() + ", due to: " + e.getMessage());
classificationService.attachClassification(model, XmlFileModel.UNPARSEABLE_XML_CLASSIFICATION,
XmlFileModel.UNPARSEABLE_XML_DESCRIPTION);
}
-
- if (document != null)
- XMLDocumentCache.cache(model, document);
}
else
{
diff --git a/tests/src/test/java/org/jboss/windup/tests/application/WindupArchitectureSmallBinaryMode2Test.java b/tests/src/test/java/org/jboss/windup/tests/application/WindupArchitectureSmallBinaryMode2Test.java
index 0284a07136..34bd9928c8 100644
--- a/tests/src/test/java/org/jboss/windup/tests/application/WindupArchitectureSmallBinaryMode2Test.java
+++ b/tests/src/test/java/org/jboss/windup/tests/application/WindupArchitectureSmallBinaryMode2Test.java
@@ -27,6 +27,7 @@ public class WindupArchitectureSmallBinaryMode2Test extends WindupArchitectureTe
@AddonDependency(name = "org.jboss.windup.reporting:windup-reporting"),
@AddonDependency(name = "org.jboss.windup.exec:windup-exec"),
@AddonDependency(name = "org.jboss.windup.rules.apps:windup-rules-java"),
+ @AddonDependency(name = "org.jboss.windup.rules.apps:windup-rules-java-ee"),
@AddonDependency(name = "org.jboss.windup.config:windup-config-groovy"),
@AddonDependency(name = "org.jboss.forge.furnace.container:cdi"),
})