package org.jboss.windup.ast.java;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
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.AnonymousClassDeclaration;
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.IBinding;
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.Modifier;
import org.eclipse.jdt.core.dom.Name;
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.ParameterizedType;
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.ResolutionStatus;
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 source file and return a {@link List} of {@link ClassReference} objects containing the fully qualified names
* of all of the contained references. <b>Note: A new instance of this visitor should be constructed for each {@link CompilationUnit}</b>
*
* @author <a href="mailto:jesse.sightler@gmail.com">Jesse Sightler</a>
* @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
*/
public class ReferenceResolvingVisitor extends ASTVisitor
{
private static final Logger LOG = Logger.getLogger(ReferenceResolvingVisitor.class.getName());
private final WildcardImportResolver wildcardImportResolver;
private final String path;
private final CompilationUnit compilationUnit;
private final List<ClassReference> classReferences = new ArrayList<>();
private final ReferenceResolvingVisitorState state;
private String packageName;
private String className;
public ReferenceResolvingVisitor(WildcardImportResolver importResolver, CompilationUnit compilationUnit, String path)
{
this.state = new ReferenceResolvingVisitorState();
this.wildcardImportResolver = importResolver;
this.compilationUnit = compilationUnit;
this.path = path;
resolveCurrentTypeNames(compilationUnit);
}
private void resolveCurrentTypeNames(CompilationUnit compilationUnit)
{
PackageDeclaration packageDeclaration = compilationUnit.getPackage();
this.packageName = packageDeclaration == null ? "" : packageDeclaration.getName().getFullyQualifiedName();
@SuppressWarnings("unchecked")
List<TypeDeclaration> types = compilationUnit.types();
@SuppressWarnings("unchecked")
List<ImportDeclaration> importDeclarations = (List<ImportDeclaration>) compilationUnit.imports();
for (ImportDeclaration importDeclaration : importDeclarations)
{
processImport(importDeclaration);
}
String fqcn = null;
if (!types.isEmpty())
{
AbstractTypeDeclaration typeDeclaration = (AbstractTypeDeclaration) types.get(0);
this.className = typeDeclaration.getName().getFullyQualifiedName();
if (packageName.isEmpty())
{
fqcn = className;
}
else
{
fqcn = packageName + "." + className;
}
String typeLine = typeDeclaration.toString();
if (typeDeclaration.getJavadoc() != null)
{
typeLine = typeLine.substring(typeDeclaration.getJavadoc().toString().length());
}
ClassReference mainTypeClassReference = new ClassReference(fqcn, packageName, className, null, ResolutionStatus.RESOLVED, TypeReferenceLocation.TYPE,
compilationUnit.getLineNumber(typeDeclaration.getStartPosition()),
compilationUnit.getColumnNumber(compilationUnit.getStartPosition()),
compilationUnit.getLength(), extractDefinitionLine(typeLine));
classReferences.add(mainTypeClassReference);
processModifiers(mainTypeClassReference, typeDeclaration.modifiers());
if (typeDeclaration instanceof TypeDeclaration)
{
Type superclassType = ((TypeDeclaration) typeDeclaration).getSuperclassType();
ITypeBinding resolveBinding = null;
if (superclassType != null)
{
resolveBinding = superclassType.resolveBinding();
}
if (resolveBinding == null && superclassType != null)
{
ResolveClassnameResult resolvedResult = resolveClassname(superclassType.toString());
PackageAndClassName packageAndClassName = PackageAndClassName.parseFromQualifiedName(resolvedResult.result);
String superPackageName = packageAndClassName.packageName;
String superClassName = packageAndClassName.className;
ResolutionStatus superResolutionStatus = resolvedResult.found ? ResolutionStatus.RECOVERED : ResolutionStatus.UNRESOLVED;
classReferences.add(new ClassReference(resolvedResult.result, superClassName, superPackageName, null,
superResolutionStatus,
TypeReferenceLocation.TYPE, compilationUnit.getLineNumber(typeDeclaration.getStartPosition()),
compilationUnit.getColumnNumber(compilationUnit.getStartPosition()),
compilationUnit.getLength(),
extractDefinitionLine(typeDeclaration.toString())));
}
while (resolveBinding != null)
{
if (superclassType.resolveBinding() != null)
{
String superPackageName = resolveBinding.getPackage().getName();
String superClassName = resolveBinding.getName();
classReferences.add(new ClassReference(resolveBinding.getQualifiedName(), superClassName, superPackageName, null,
ResolutionStatus.RESOLVED,
TypeReferenceLocation.TYPE, compilationUnit.getLineNumber(typeDeclaration.getStartPosition()),
compilationUnit.getColumnNumber(compilationUnit.getStartPosition()),
compilationUnit.getLength(),
extractDefinitionLine(typeDeclaration.toString())));
}
resolveBinding = resolveBinding.getSuperclass();
}
}
}
state.getNames().add("this");
state.getNameInstance().put("this", fqcn);
}
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<ClassReference> getJavaClassReferences()
{
return this.classReferences;
}
private ClassReference processConstructor(ConstructorType interest, ResolutionStatus resolutionStatus, int lineNumber,
int columnNumber, int length, String line)
{
String text = interest.toString();
ClassReference reference = new ClassReference(text, this.packageName, this.className, "<init>", resolutionStatus,
TypeReferenceLocation.CONSTRUCTOR_CALL, lineNumber, columnNumber, length,
line);
this.classReferences.add(reference);
return reference;
}
private ClassReference processMethod(MethodType interest, ResolutionStatus resolutionStatus, TypeReferenceLocation location, int lineNumber,
int columnNumber, int length, String line)
{
String text = interest.toString();
ClassReference reference = new ClassReference(text, interest.packageName, interest.className, interest.methodName, resolutionStatus, location,
lineNumber, columnNumber, length, line);
this.classReferences.add(reference);
return reference;
}
private void processImport(String interest, ResolutionStatus resolutionStatus, int lineNumber, int columnNumber, int length, String line)
{
final PackageAndClassName packageAndClass;
if (!interest.contains("."))
{
packageAndClass = new PackageAndClassName(interest, null);
}
else
{
packageAndClass = PackageAndClassName.parseFromQualifiedName(interest);
}
this.classReferences
.add(new ClassReference(interest, packageAndClass.packageName, packageAndClass.className, null, resolutionStatus,
TypeReferenceLocation.IMPORT, lineNumber,
columnNumber,
length, line));
}
private ClassReference processTypeBinding(ITypeBinding type, ResolutionStatus resolutionStatus,
TypeReferenceLocation referenceLocation, int lineNumber, int columnNumber,
int length, String line)
{
if (type == null)
return null;
final String sourceString = getQualifiedName(type);
final String packageName;
if (type.getPackage() != null)
packageName = type.getPackage().getName();
else
packageName = PackageAndClassName.parseFromQualifiedName(sourceString).packageName;
final String className = type.getName();
return processTypeAsString(sourceString, packageName, className, resolutionStatus, referenceLocation, lineNumber, columnNumber, length, line);
}
private ClassReference processTypeAsEnum(ITypeBinding typeBinding, Expression expression, ResolutionStatus resolutionStatus, int lineNumber, int columnNumber, int length, String line)
{
if (expression == null)
return null;
String fullExpression = null;
String enumPackage = null;
String enumClassName = null;
if (typeBinding != null)
{
fullExpression = typeBinding.getQualifiedName();
enumPackage = typeBinding.getPackage().getName();
enumClassName = typeBinding.getName();
}
if (expression instanceof Name)
{
if (StringUtils.isNotBlank(fullExpression))
fullExpression += ".";
if (expression instanceof QualifiedName)
fullExpression += ((QualifiedName) expression).getName();
else
fullExpression += ((Name) expression).getFullyQualifiedName();
}
ClassReference reference = new ClassReference(fullExpression, enumPackage, enumClassName, null, resolutionStatus,
TypeReferenceLocation.ENUM_CONSTANT,
lineNumber,
columnNumber,
length,
line);
this.classReferences.add(reference);
return reference;
}
/**
* 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)
{
ResolveClassnameResult resolvedResult = resolveClassname(type.toString());
ResolutionStatus status = resolvedResult.found ? ResolutionStatus.RECOVERED : ResolutionStatus.UNRESOLVED;
PackageAndClassName packageAndClassName = PackageAndClassName.parseFromQualifiedName(resolvedResult.result);
return processTypeAsString(resolvedResult.result, packageAndClassName.packageName, packageAndClassName.className, status,
typeReferenceLocation, lineNumber,
columnNumber, length, line);
}
else
{
return processTypeBinding(type.resolveBinding(), ResolutionStatus.RESOLVED, typeReferenceLocation, lineNumber,
columnNumber, length, line);
}
}
private ClassReference processTypeAsString(String sourceString, String packageName, String className, ResolutionStatus resolutionStatus,
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, packageName, className, null, resolutionStatus, referenceLocation, lineNumber,
columnNumber,
length,
line);
this.classReferences.add(typeRef);
return typeRef;
}
@Override
public boolean visit(MethodDeclaration node)
{
// register method return type
final ResolutionStatus resolutionStatus;
IMethodBinding resolveBinding = node.resolveBinding();
ITypeBinding returnType = null;
if (resolveBinding != null)
{
resolutionStatus = ResolutionStatus.RESOLVED;
returnType = node.resolveBinding().getReturnType();
}
else
{
resolutionStatus = ResolutionStatus.RECOVERED;
}
if (returnType != null)
{
processTypeBinding(returnType, ResolutionStatus.RESOLVED, 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<String> qualifiedArguments = new ArrayList<>();
@SuppressWarnings("unchecked")
List<SingleVariableDeclaration> parameters = node.parameters();
if (parameters != null)
{
for (SingleVariableDeclaration type : parameters)
{
state.getNames().add(type.getName().toString());
String typeName = type.getType().toString();
typeName = resolveClassname(typeName).result;
qualifiedArguments.add(typeName);
state.getNameInstance().put(type.getName().toString(), typeName);
ClassReference parameterClassReference = processType(type.getType(), TypeReferenceLocation.METHOD_PARAMETER, compilationUnit.getLineNumber(node.getStartPosition()),
compilationUnit.getColumnNumber(node.getStartPosition()), node.getLength(), extractDefinitionLine(node.toString()));
processModifiers(parameterClassReference, type.modifiers());
}
}
// register thow declarations
@SuppressWarnings("unchecked")
List<Type> 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(state.getNameInstance().get("this"), this.packageName, this.className, node.getName().toString(),
qualifiedArguments);
ClassReference methodReference = processMethod(methodCall, resolutionStatus, TypeReferenceLocation.METHOD, compilationUnit.getLineNumber(node.getName().getStartPosition()),
compilationUnit.getColumnNumber(node.getName().getStartPosition()), node.getName().getLength(),
extractDefinitionLine(node.toString()));
processModifiers(methodReference, node.modifiers());
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();
ITypeBinding typeBinding = cic.getType().resolveBinding();
if (typeBinding == null)
{
String qualifiedClass = cic.getType().toString();
ResolveClassnameResult result = resolveClassname(qualifiedClass);
qualifiedClass = result.result;
ResolutionStatus resolutionStatus = result.found ? ResolutionStatus.RECOVERED : ResolutionStatus.UNRESOLVED;
PackageAndClassName packageAndClassName = PackageAndClassName.parseFromQualifiedName(qualifiedClass);
processTypeAsString(qualifiedClass, packageAndClassName.packageName, packageAndClassName.className, resolutionStatus,
TypeReferenceLocation.CONSTRUCTOR_CALL,
compilationUnit.getLineNumber(node.getStartPosition()),
compilationUnit.getColumnNumber(cic.getStartPosition()), cic.getLength(), node.toString());
}
else
{
processTypeBinding(typeBinding, ResolutionStatus.RESOLVED, TypeReferenceLocation.CONSTRUCTOR_CALL,
compilationUnit.getLineNumber(node.getStartPosition()),
compilationUnit.getColumnNumber(cic.getStartPosition()), cic.getLength(), node.toString());
}
}
return super.visit(node);
}
@Override
public boolean visit(AnonymousClassDeclaration node)
{
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).result;
VariableDeclarationFragment frag = (VariableDeclarationFragment) node.fragments().get(i);
Expression expression = frag.getInitializer();
final int lineNumber = compilationUnit.getLineNumber(node.getStartPosition());
final int columnNumber = compilationUnit.getColumnNumber(node.getStartPosition());
final ITypeBinding resolveBinding = node.getType().resolveBinding();
if (expression instanceof Name)
{
Name expressionName = (Name) expression;
IBinding binding = expressionName.resolveBinding();
if (binding == null)
{
ResolveClassnameResult result = resolveClassname(expressionName.getFullyQualifiedName());
ResolutionStatus status = result.found ? ResolutionStatus.RECOVERED : ResolutionStatus.UNRESOLVED;
PackageAndClassName packageAndClassName = PackageAndClassName.parseFromQualifiedName(result.result);
processTypeAsString(result.result,
packageAndClassName.packageName,
packageAndClassName.className,
status,
TypeReferenceLocation.VARIABLE_INITIALIZER,
lineNumber,
columnNumber, node.getLength(), node.toString());
}
else
{
//additionally add Enum Constant type reference
if (resolveBinding.isEnum())
{
processTypeAsEnum(resolveBinding, expressionName, ResolutionStatus.RESOLVED, lineNumber, columnNumber, node.getLength(), extractDefinitionLine(node.toString()));
}
processTypeBinding(resolveBinding, ResolutionStatus.RESOLVED, TypeReferenceLocation.VARIABLE_INITIALIZER,
lineNumber,
columnNumber, node.getLength(), node.toString());
}
}
state.getNames().add(frag.getName().getIdentifier());
state.getNameInstance().put(frag.getName().toString(), nodeType.toString());
ITypeBinding resolvedTypeBinding = resolveBinding;
ClassReference reference;
if (resolvedTypeBinding != null)
{
reference = processTypeBinding(resolvedTypeBinding, ResolutionStatus.RESOLVED, TypeReferenceLocation.FIELD_DECLARATION,
lineNumber,
columnNumber, node.getLength(), node.toString());
}
else
{
ResolveClassnameResult result = resolveClassname(node.getType().toString());
ResolutionStatus status = result.found ? ResolutionStatus.RECOVERED : ResolutionStatus.UNRESOLVED;
PackageAndClassName packageAndClassName = PackageAndClassName.parseFromQualifiedName(result.result);
reference = processTypeAsString(result.result,
packageAndClassName.packageName,
packageAndClassName.className,
status,
TypeReferenceLocation.FIELD_DECLARATION,
lineNumber,
columnNumber, node.getLength(), node.toString());
}
processModifiers(reference, node.modifiers());
}
return true;
}
private AnnotationValue getAnnotationValueForExpression(ClassReference annotatedReference, 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(annotatedReference, (NormalAnnotation) expression);
else if (expression instanceof ArrayInitializer)
{
ArrayInitializer arrayInitializer = (ArrayInitializer) expression;
List<AnnotationValue> arrayValues = new ArrayList<>(arrayInitializer.expressions().size());
for (Object arrayExpression : arrayInitializer.expressions())
{
arrayValues.add(getAnnotationValueForExpression(annotatedReference, (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(annotatedReference, 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 type: " + 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(ClassReference annotatedReference, AnnotationClassReference typeRef, Annotation node)
{
Map<String, AnnotationValue> annotationValueMap = new HashMap<>();
if (node instanceof SingleMemberAnnotation)
{
SingleMemberAnnotation singleMemberAnnotation = (SingleMemberAnnotation) node;
AnnotationValue value = getAnnotationValueForExpression(annotatedReference, singleMemberAnnotation.getValue());
annotationValueMap.put("value", value);
}
else if (node instanceof NormalAnnotation)
{
@SuppressWarnings("unchecked")
List<MemberValuePair> annotationValues = ((NormalAnnotation) node).values();
for (MemberValuePair annotationValue : annotationValues)
{
String key = annotationValue.getName().toString();
Expression expression = annotationValue.getValue();
AnnotationValue value = getAnnotationValueForExpression(annotatedReference, 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(ClassReference annotatedReference, Annotation node)
{
final ITypeBinding typeBinding = node.resolveTypeBinding();
final AnnotationClassReference reference;
final String qualifiedName;
final String packageName;
final String className;
final ResolutionStatus status;
if (typeBinding != null)
{
status = ResolutionStatus.RESOLVED;
qualifiedName = typeBinding.getQualifiedName();
packageName = typeBinding.getPackage().getName();
className = typeBinding.getName();
}
else
{
ResolveClassnameResult result = resolveClassname(node.getTypeName().toString());
status = result.found ? ResolutionStatus.RECOVERED : ResolutionStatus.UNRESOLVED;
qualifiedName = result.result;
PackageAndClassName packageAndClassName = PackageAndClassName.parseFromQualifiedName(result.result);
packageName = packageAndClassName.packageName;
className = packageAndClassName.className;
}
reference = new AnnotationClassReference(
annotatedReference,
qualifiedName,
packageName,
className,
status,
compilationUnit.getLineNumber(node.getStartPosition()),
compilationUnit.getColumnNumber(node.getStartPosition()),
node.getLength(),
node.toString());
addAnnotationValues(annotatedReference, reference, node);
return reference;
}
private void processModifiers(ClassReference originalReference, List modifiers)
{
for (Object modifier : modifiers)
{
if (modifier instanceof NormalAnnotation)
{
AnnotationClassReference reference = processAnnotation(originalReference, (NormalAnnotation)modifier);
this.classReferences.add(reference);
}
else if (modifier instanceof SingleMemberAnnotation)
{
AnnotationClassReference reference = processAnnotation(originalReference, (SingleMemberAnnotation)modifier);
this.classReferences.add(reference);
}
else if (modifier instanceof MarkerAnnotation)
{
AnnotationClassReference reference = processAnnotation(originalReference, (MarkerAnnotation)modifier);
this.classReferences.add(reference);
} else if (modifier instanceof Modifier)
{
// throw if this is an annotation (as we should be processing all of those), ignore otherwise
if (((Modifier)modifier).isAnnotation())
throw new RuntimeException("Failed due to unexpected Modifier that is also an annotation: " + modifier.getClass().getCanonicalName());
}
else
{
throw new RuntimeException("Failed due to unexpected type: " + modifier.getClass().getCanonicalName());
}
}
}
// @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()))
{
TypeReferenceLocation typeReferenceLocation = node.isInterface() ? TypeReferenceLocation.INHERITANCE
: TypeReferenceLocation.IMPLEMENTS_TYPE;
List<?> clzInterfacesList = (List<?>) clzInterfaces;
for (Object clzInterface : clzInterfacesList)
{
ParameterizedType parameterizedType = null;
if (clzInterface instanceof ParameterizedType)
{
parameterizedType = (ParameterizedType) clzInterface;
clzInterface = parameterizedType.getType();
}
if (clzInterface instanceof SimpleType)
{
SimpleType simpleType = (SimpleType) clzInterface;
ITypeBinding resolvedSuperInterface = simpleType.resolveBinding();
if (resolvedSuperInterface == null)
{
ResolveClassnameResult result = resolveClassname(simpleType.getName().toString());
ResolutionStatus status = result.found ? ResolutionStatus.RECOVERED : ResolutionStatus.UNRESOLVED;
PackageAndClassName packageAndClassName = PackageAndClassName.parseFromQualifiedName(result.result);
processTypeAsString(result.result, packageAndClassName.packageName, packageAndClassName.className, status,
typeReferenceLocation,
compilationUnit.getLineNumber(node.getStartPosition()),
compilationUnit.getColumnNumber(node.getStartPosition()),
node.getLength(), extractDefinitionLine(node.toString()));
}
else
{
/*
* Register all the implemented interfaces (even super interfaces, if we are able to resolve them.)
*/
Stack<ITypeBinding> stack = new Stack<>();
stack.push(resolvedSuperInterface);
while (!stack.isEmpty())
{
resolvedSuperInterface = stack.pop();
processTypeBinding(resolvedSuperInterface, ResolutionStatus.RESOLVED, typeReferenceLocation,
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();
if (resolvedSuperClass == null)
{
ResolveClassnameResult result = resolveClassname(((SimpleType) clzSuperClasses).getName().toString());
ResolutionStatus status = result.found ? ResolutionStatus.RECOVERED : ResolutionStatus.UNRESOLVED;
PackageAndClassName packageAndClassName = PackageAndClassName.parseFromQualifiedName(result.result);
processTypeAsString(result.result, packageAndClassName.packageName, packageAndClassName.className, status,
TypeReferenceLocation.INHERITANCE,
compilationUnit.getLineNumber(node.getStartPosition()),
compilationUnit.getColumnNumber(node.getStartPosition()),
node.getLength(), extractDefinitionLine(node.toString()));
}
// register all the superClasses up to Object
while (resolvedSuperClass != null && !resolvedSuperClass.getQualifiedName().equals("java.lang.Object"))
{
processTypeBinding(resolvedSuperClass, ResolutionStatus.RESOLVED, TypeReferenceLocation.INHERITANCE,
compilationUnit.getLineNumber(node.getStartPosition()),
compilationUnit.getColumnNumber(node.getStartPosition()), node.getLength(), extractDefinitionLine(node.toString()));
for (ITypeBinding iface : resolvedSuperClass.getInterfaces())
{
processTypeBinding(iface, ResolutionStatus.RESOLVED, TypeReferenceLocation.IMPLEMENTS_TYPE,
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();
VariableDeclarationFragment frag = (VariableDeclarationFragment) node.fragments().get(i);
state.getNames().add(frag.getName().getIdentifier());
state.getNameInstance().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);
}
private void processImport(ImportDeclaration node)
{
String name = node.getName().toString();
if (node.isOnDemand())
{
state.getWildcardImports().add(name);
String[] resolvedNames = this.wildcardImportResolver.resolve(name);
for (String resolvedName : resolvedNames)
{
processImport(resolvedName, ResolutionStatus.RESOLVED, compilationUnit.getLineNumber(node.getName().getStartPosition()),
compilationUnit.getColumnNumber(node.getName().getStartPosition()), node.getName().getLength(), node.toString().trim());
}
/*
* Also, register the wildcard itself so that we can have rules that match against wildcard imports, event if we do not know what classes
* may be contained in that wildcard
*/
processImport(name + ".*", ResolutionStatus.RESOLVED, compilationUnit.getLineNumber(node.getName().getStartPosition()),
compilationUnit.getColumnNumber(node.getName().getStartPosition()), node.getName().getLength(), node.toString().trim());
}
else
{
String clzName = StringUtils.substringAfterLast(name, ".");
state.getClassNameLookedUp().add(clzName);
state.getClassNameToFQCN().put(clzName, name);
ResolutionStatus status = node.resolveBinding() != null ? ResolutionStatus.RESOLVED : ResolutionStatus.RECOVERED;
processImport(name, status, compilationUnit.getLineNumber(node.getName().getStartPosition()),
compilationUnit.getColumnNumber(node.getName().getStartPosition()), node.getName().getLength(), node.toString().trim());
}
}
/***
* 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<String> qualifiedInstances = new ArrayList<>();
List<String> argumentsQualified = new ArrayList<>();
// get qualified arguments of the method
IMethodBinding resolveTypeBinding = node.resolveMethodBinding();
final ResolutionStatus resolutionStatus;
int columnNumber = compilationUnit.getColumnNumber(node.getName().getStartPosition());
int length = node.getName().getLength();
if (resolveTypeBinding != null)
{
resolutionStatus = ResolutionStatus.RESOLVED;
ITypeBinding[] argumentTypeBindings = resolveTypeBinding.getParameterTypes();
List<Expression> arguments = node.arguments();
int index = 0;
for (ITypeBinding type : argumentTypeBindings)
{
argumentsQualified.add(type.getQualifiedName());
if (type.isEnum())
{
// there is different number of passed arguments and possible arguments from declaration
if (arguments.size() > index)
{
Expression expression = arguments.get(index);
processTypeAsEnum(type, expression, resolutionStatus,
compilationUnit.getLineNumber(node.getName().getStartPosition()),
columnNumber,
length, extractDefinitionLine(node.toString()));
}
index++;
}
}
// 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<String> interfaceMethodArguments = new ArrayList<>();
for (ITypeBinding type : interfaceMethod.getParameterTypes())
{
interfaceMethodArguments.add(type.getQualifiedName());
}
if (interfaceMethodArguments.equals(argumentsQualified))
{
qualifiedInstances.add(possibleInterface.getQualifiedName());
}
}
}
}
}
}
}
else
{
resolutionStatus = ResolutionStatus.RECOVERED;
String nodeName = StringUtils.removeStart(node.toString(), "this.");
String objRef = StringUtils.substringBefore(nodeName, "." + node.getName().toString());
if (state.getNameInstance().containsKey(objRef))
{
objRef = state.getNameInstance().get(objRef);
}
objRef = resolveClassname(objRef).result;
@SuppressWarnings("unchecked")
List<Expression> arguments = node.arguments();
for (Expression expression : arguments)
{
ITypeBinding argumentBinding = expression.resolveTypeBinding();
if (argumentBinding != null)
{
argumentsQualified.add(argumentBinding.getQualifiedName());
if (argumentBinding.isEnum())
{
// FIXME -- Test
String constantEnum = expression.toString();
if (constantEnum != null)
{
processTypeAsEnum(argumentBinding, expression, ResolutionStatus.RESOLVED, compilationUnit.getLineNumber(node.getName().getStartPosition()),
columnNumber, length, extractDefinitionLine(node.toString()));
}
}
}
else
{
PackageAndClassName argumentQualifiedGuess = PackageAndClassName.parseFromQualifiedName(expression.toString());
argumentsQualified.add(argumentQualifiedGuess.toString());
}
}
qualifiedInstances.add(objRef);
}
for (String qualifiedInstance : qualifiedInstances)
{
PackageAndClassName packageAndClassName = PackageAndClassName.parseFromQualifiedName(qualifiedInstance);
MethodType methodCall = new MethodType(qualifiedInstance, packageAndClassName.packageName, packageAndClassName.className,
node.getName().toString(), argumentsQualified);
processMethod(methodCall, resolutionStatus, TypeReferenceLocation.METHOD_CALL,
compilationUnit.getLineNumber(node.getName().getStartPosition()),
columnNumber, length, node.toString());
}
return super.visit(node);
}
@Override
public boolean visit(PackageDeclaration node)
{
return super.visit(node);
}
private String getQualifiedName(ITypeBinding typeBinding)
{
if (typeBinding == null)
return null;
String qualifiedName = typeBinding.getQualifiedName();
if (StringUtils.isEmpty(qualifiedName))
{
if (typeBinding.isAnonymous())
{
if (typeBinding.getSuperclass() != null)
qualifiedName = getQualifiedName(typeBinding.getSuperclass());
else if (typeBinding instanceof AnonymousClassDeclaration)
{
qualifiedName = ((AnonymousClassDeclaration) typeBinding).toString();
}
}
else if (StringUtils.isEmpty(qualifiedName) && typeBinding.isNested())
qualifiedName = typeBinding.getName();
}
return qualifiedName;
}
@Override
public boolean visit(ClassInstanceCreation node)
{
final int lineNumber = compilationUnit.getLineNumber(node.getType().getStartPosition());
final int columnNumber = compilationUnit.getColumnNumber(node.getType().getStartPosition());
final int length = node.getType().getLength();
IMethodBinding constructorBinding = node.resolveConstructorBinding();
String qualifiedClass = "";
List<String> constructorMethodQualifiedArguments = new ArrayList<>();
if (constructorBinding != null && constructorBinding.getDeclaringClass() != null)
{
ITypeBinding declaringClass = constructorBinding.getDeclaringClass();
qualifiedClass = getQualifiedName(declaringClass);
@SuppressWarnings("unchecked")
List<Expression> arguments = node.arguments();
int index = 0;
for (ITypeBinding type : constructorBinding.getParameterTypes())
{
if (type.isEnum())
{
// there is different number of passed arguments and possible arguments from declaration
if (arguments.size() > index)
{
Expression argument = arguments.get(index);
processTypeAsEnum(type, argument, ResolutionStatus.RESOLVED, lineNumber, columnNumber, length, extractDefinitionLine(node.toString()));
}
}
index++;
String qualifiedArgumentClass = type.getQualifiedName();
if (qualifiedArgumentClass != null)
{
constructorMethodQualifiedArguments.add(qualifiedArgumentClass);
}
}
}
if (constructorMethodQualifiedArguments.isEmpty() && !node.arguments().isEmpty())
{
@SuppressWarnings("unchecked")
List<Expression> arguments = node.arguments();
arguments.get(0).resolveTypeBinding();
for (Expression type : arguments)
{
ITypeBinding argumentBinding = type.resolveTypeBinding();
if (argumentBinding != null)
{
constructorMethodQualifiedArguments.add(argumentBinding.getQualifiedName());
}
else
{
List<String> guessedParam = methodParameterGuesser(Collections.singletonList(type));
constructorMethodQualifiedArguments.addAll(guessedParam);
}
}
}
final ResolutionStatus resolutionStatus;
/*
* Qualified class may not be resolved in case of anonymous classes
*/
if (qualifiedClass == null || qualifiedClass.isEmpty())
{
qualifiedClass = node.getType().toString();
ResolveClassnameResult result = resolveClassname(qualifiedClass);
qualifiedClass = result.result;
resolutionStatus = result.found ? ResolutionStatus.RECOVERED : ResolutionStatus.UNRESOLVED;
}
else
{
resolutionStatus = ResolutionStatus.RESOLVED;
}
ConstructorType resolvedConstructor = new ConstructorType(qualifiedClass, constructorMethodQualifiedArguments);
processConstructor(resolvedConstructor, resolutionStatus, lineNumber, columnNumber, length, node.toString());
return super.visit(node);
}
private List<String> methodParameterGuesser(List<?> arguments)
{
List<String> resolvedParams = new ArrayList<>(arguments.size());
for (Object o : arguments)
{
if (o instanceof SimpleName)
{
String name = state.getNameInstance().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 (state.getNames().contains(field))
{
resolvedParams.add(state.getNameInstance().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).result;
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 if (o instanceof QualifiedName)
{
String paramGuessed = ((QualifiedName) o).getFullyQualifiedName();
PackageAndClassName qualifiedParam = PackageAndClassName.parseFromQualifiedName(paramGuessed);
resolvedParams.add(qualifiedParam.toString());
}
else
{
resolvedParams.add("Undefined");
}
}
return resolvedParams;
}
private ResolveClassnameResult 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, "."))
{
if (state.getClassNameLookedUp().contains(sourceClassname))
{
String qualifiedName = state.getClassNameToFQCN().get(sourceClassname);
if (qualifiedName != null)
{
return new ResolveClassnameResult(true, qualifiedName);
}
else
{
return new ResolveClassnameResult(false, sourceClassname);
}
}
else
{
state.getClassNameLookedUp().add(sourceClassname);
String resolvedClassName = this.wildcardImportResolver.resolve(this.state.getWildcardImports(), sourceClassname);
if (resolvedClassName != null)
{
state.getClassNameToFQCN().put(sourceClassname, resolvedClassName);
return new ResolveClassnameResult(true, resolvedClassName);
}
return new ResolveClassnameResult(false, sourceClassname);
}
}
else
{
return new ResolveClassnameResult(true, sourceClassname);
}
}
private String qualifyType(String objRef)
{
/*
* Temporarily remove '[]' to resolve array types.
*/
objRef = StringUtils.removeEnd(objRef, "[]");
if (state.getNameInstance().containsKey(objRef))
{
objRef = state.getNameInstance().get(objRef);
}
objRef = resolveClassname(objRef).result;
return objRef;
}
private static class MethodType
{
private final String qualifiedName;
private final String packageName;
private final String className;
private final String methodName;
private final List<String> qualifiedParameters;
MethodType(String qualifiedName, String packageName, String className, String methodName, List<String> qualifiedParameters)
{
this.qualifiedName = qualifiedName;
this.packageName = packageName;
this.className = className;
this.methodName = methodName;
if (qualifiedParameters != null)
{
this.qualifiedParameters = qualifiedParameters;
}
else
{
this.qualifiedParameters = new LinkedList<>();
}
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append(qualifiedName).append(".").append(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();
}
}
private static class ConstructorType
{
private final String qualifiedName;
private final List<String> qualifiedParameters;
public ConstructorType(String qualifiedName, List<String> qualifiedParameters)
{
this.qualifiedName = qualifiedName;
if (qualifiedParameters != null)
{
this.qualifiedParameters = qualifiedParameters;
}
else
{
this.qualifiedParameters = new LinkedList<>();
}
}
@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 static class PackageAndClassName
{
private String packageName;
private String className;
public PackageAndClassName(String packageName, String className)
{
this.packageName = packageName;
this.className = className;
}
public String toString()
{
StringBuffer sb = new StringBuffer();
if (this.packageName != null)
{
sb.append(this.packageName).append(".");
}
if (this.className != null)
sb.append(this.className);
return sb.toString();
}
public static PackageAndClassName parseFromQualifiedName(String qualifiedName)
{
final String packageName;
final String className;
// remove the .* if this was a package import
if (qualifiedName.contains(".*"))
{
packageName = qualifiedName.replace("*", "");
className = null;
}
else
{
int lastDot = qualifiedName.lastIndexOf('.');
if (lastDot == -1)
{
packageName = null;
className = qualifiedName;
}
else
{
packageName = qualifiedName.substring(0, lastDot);
className = qualifiedName.substring(lastDot + 1);
}
}
return new PackageAndClassName(packageName, className);
}
}
private class ResolveClassnameResult
{
private boolean found;
private String result;
public ResolveClassnameResult(boolean found, String result)
{
this.found = found;
this.result = result;
}
}
}