package org.jboss.windup.rules.apps.java.scan.ast; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.jboss.forge.roaster.model.JavaType; import org.jboss.forge.roaster.model.source.Import; import org.jboss.forge.roaster.model.source.Importer; import org.jboss.forge.roaster.spi.WildcardImportResolver; import org.jboss.windup.graph.GraphContext; import org.jboss.windup.rules.apps.java.model.JavaClassModel; import org.jboss.windup.rules.apps.java.service.JavaClassService; /** * Provides a wildcard resolver for imports that attempts to search the graph for related types * */ public class WindupWildcardImportResolver implements WildcardImportResolver, org.jboss.windup.ast.java.WildcardImportResolver { private static GraphContext context; /** * Contains a map of class short names (eg, MyClass) to qualified names (eg, com.example.MyClass) */ private final Map<String, String> classNameToFQCN = new ConcurrentHashMap<>(); /** * 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<String> classNameLookedUp = Collections.synchronizedSet(new HashSet<String>()); @Override public String resolve(JavaType<?> source, String type) { GraphContext graphContext = getContext(); if (graphContext == null) { return type; } Importer<?> importer = (Importer<?>) source; List<String> wildcardImports = new ArrayList<>(); for (Import importDeclaration : importer.getImports()) { if (importDeclaration.isWildcard()) { wildcardImports.add(importDeclaration.getQualifiedName()); } } return resolve(wildcardImports, type); } @Override public String resolve(List<String> wildcardImports, String type) { GraphContext graphContext = getContext(); // 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. // Check if we have already looked this one up if (classNameLookedUp.contains(type)) { // if yes, then just use the looked up name from the map String qualifiedName = classNameToFQCN.get(type); if (qualifiedName != null) { return qualifiedName; } else { // otherwise, just return the provided name (unchanged) return type; } } else { // if this name has not been resolved before, go ahead and resolve it from the graph (if possible) classNameLookedUp.add(type); // search every wildcard import for this name for (String wildcardImport : wildcardImports) { String candidateQualifiedName = wildcardImport + "." + type; JavaClassService javaClassService = new JavaClassService(graphContext); Iterable<JavaClassModel> models = javaClassService.findAllByProperty(JavaClassModel.QUALIFIED_NAME, candidateQualifiedName); if (models.iterator().hasNext()) { classNameToFQCN.put(type, candidateQualifiedName); return candidateQualifiedName; } } // nothing was found, so just return the original value return type; } } @Override public String[] resolve(String wildcardImportPackageName) { JavaClassService javaClassService = new JavaClassService(getContext()); Iterable<JavaClassModel> classModels = javaClassService.findByJavaPackage(wildcardImportPackageName); List<String> results = new ArrayList<>(); for (JavaClassModel classModel : classModels) { results.add(classModel.getQualifiedName()); } return results.toArray(new String[results.size()]); } private GraphContext getContext() { return WindupWildcardImportResolver.context; } public static void setContext(GraphContext context) { WindupWildcardImportResolver.context = context; } }