package org.springframework.roo.classpath;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Service;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.service.component.ComponentContext;
import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetails;
import org.springframework.roo.classpath.details.MemberHoldingTypeDetails;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadata;
import org.springframework.roo.file.monitor.FileMonitorService;
import org.springframework.roo.file.monitor.event.FileDetails;
import org.springframework.roo.metadata.MetadataIdentificationUtils;
import org.springframework.roo.metadata.MetadataService;
import org.springframework.roo.model.JavaPackage;
import org.springframework.roo.model.JavaSymbolName;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.process.manager.FileManager;
import org.springframework.roo.project.Dependency;
import org.springframework.roo.project.LogicalPath;
import org.springframework.roo.project.PhysicalPath;
import org.springframework.roo.project.ProjectOperations;
import org.springframework.roo.project.maven.Pom;
import org.springframework.roo.shell.NaturalOrderComparator;
import org.springframework.roo.support.logging.HandlerUtils;
import org.springframework.roo.support.util.FileUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Logger;
/**
* Implementation of {@link TypeLocationService}.
* <p>
* For performance reasons automatically caches the queries. The cache is
* invalidated on changes to the file system.
*
* @author Alan Stewart
* @author Ben Alex
* @author Stefan Schmidt
* @author James Tyrrell
* @author Paula Navarro
* @author Sergio Clares
* @since 1.1
*/
@Component
@Service
public class TypeLocationServiceImpl implements TypeLocationService {
private static final Logger LOGGER = HandlerUtils.getLogger(TypeLocationServiceImpl.class);
// ------------ OSGi component attributes ----------------
private BundleContext context;
private Map<ModuleFeatureName, ModuleFeature> moduleFeatures =
new HashMap<ModuleFeatureName, ModuleFeature>();
protected void activate(final ComponentContext cContext) {
context = cContext.getBundleContext();
}
private static final Comparator<String> LENGTH_COMPARATOR = new Comparator<String>() {
public int compare(final String key1, final String key2) {
return Integer.valueOf(key1.length()).compareTo(key2.length());
}
};
private static final String JAVA_FILES_ANT_PATH = "**" + File.separatorChar + "*.java";
/**
* Returns all packages leading up to the given package, e.g. if the given
* package is "com.foo.bar", returns ["com", "com.foo", "com.foo.bar"].
*
* @param leafPackage the fully-qualified package to parse (required)
* @return a non-<code>null</code> iterable
*/
static Set<String> getAllPackages(final String leafPackage) {
final Set<String> discoveredPackages = new HashSet<String>();
final String[] typeSegments = leafPackage.split("\\.");
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < typeSegments.length; i++) {
final String typeSegment = typeSegments[i];
if (i > 0) {
sb.append(".");
}
sb.append(typeSegment);
discoveredPackages.add(sb.toString());
}
return discoveredPackages;
}
/**
* Returns the lowest-level package that contains the given number of types.
*
* @param typeCount the number of types to look for
* @param typesByPackage maps package names to type names; a given type will
* appear under all of its parent packages (required)
* @return <code>null</code> if no package contains the given number of
* types
*/
static String getLowestCommonPackage(final int typeCount,
final Map<String, Collection<String>> typesByPackage) {
final Map<String, Collection<String>> typesBySortedPackage = sortByKeyLength(typesByPackage);
int longestPackage = 0;
String topLevelPackage = null;
for (final Entry<String, Collection<String>> entry : typesBySortedPackage.entrySet()) {
final String thisPackage = entry.getKey();
final Collection<String> typesInPackage = entry.getValue();
if (typesInPackage.size() == typeCount && thisPackage.length() > longestPackage) {
longestPackage = thisPackage.length();
topLevelPackage = thisPackage;
}
}
return topLevelPackage;
}
static String getPackageFromType(final String typeName) {
return typeName.substring(0, typeName.lastIndexOf('.'));
}
/**
* Sorts the given string-keyed map by the length of its keys.
*
* @param <V> the type of the map's values
* @param mapToSort the map to sort (not changed)
* @return the sorted version of the map
*/
static <V> Map<String, V> sortByKeyLength(final Map<String, V> mapToSort) {
final List<String> keys = new ArrayList<String>(mapToSort.keySet());
Collections.sort(keys, LENGTH_COMPARATOR);
final Map<String, V> sortedMap = new LinkedHashMap<String, V>();
for (final String key : keys) {
sortedMap.put(key, mapToSort.get(key));
}
return sortedMap;
}
private FileManager fileManager;
private FileMonitorService fileMonitorService;
private MetadataService metadataService;
private ProjectOperations projectOperations;
private TypeCache typeCache;
private TypeResolutionService typeResolutionService;
private final Map<JavaType, Set<String>> annotationToMidMap =
new HashMap<JavaType, Set<String>>();
private final Map<String, Set<String>> changeMap = new HashMap<String, Set<String>>();
private final Set<String> dirtyFiles = new HashSet<String>();
private final Set<String> discoveredTypes = new HashSet<String>();
private final Map<String, Set<Object>> typeCustomDataMap = new HashMap<String, Set<Object>>();
private final Map<Object, Set<String>> tagToMidMap = new HashMap<Object, Set<String>>();
private final Map<String, Set<JavaType>> typeAnnotationMap = new HashMap<String, Set<JavaType>>();
private void cacheType(final String fileCanonicalPath) {
Validate.notBlank(fileCanonicalPath, "File canonical path required");
if (doesPathIndicateJavaType(fileCanonicalPath)) {
final String id = getPhysicalTypeIdentifier(fileCanonicalPath);
if (id != null && PhysicalTypeIdentifier.isValid(id)) {
// Change to Java, so drop the cache
final ClassOrInterfaceTypeDetails cid = lookupClassOrInterfaceTypeDetails(id);
if (cid == null) {
if (!getFileManager().exists(fileCanonicalPath)) {
getTypeCache().removeType(id);
final JavaType type = getTypeCache().getTypeDetails(id).getName();
updateChanges(type.getFullyQualifiedTypeName(), true);
}
return;
}
getTypeCache().cacheType(fileCanonicalPath, cid);
updateAttributeCache(cid);
updateChanges(cid.getName().getFullyQualifiedTypeName(), false);
}
}
}
private Set<String> discoverTypes() {
// Retrieve a list of paths that have been discovered or modified since
// the last invocation by this class
for (final String change : getFileMonitorService().getDirtyFiles(
TypeLocationServiceImpl.class.getName())) {
if (doesPathIndicateJavaType(change)) {
discoveredTypes.add(change);
dirtyFiles.add(change);
}
}
return discoveredTypes;
}
private boolean doesPathIndicateJavaType(final String fileCanonicalPath) {
Validate.notBlank(fileCanonicalPath, "File canonical path required");
return fileCanonicalPath.endsWith(".java") && !fileCanonicalPath.endsWith("package-info.java")
&& JavaSymbolName.isLegalJavaName(getProposedJavaType(fileCanonicalPath));
}
public Set<ClassOrInterfaceTypeDetails> findClassesOrInterfaceDetailsWithAnnotation(
final JavaType... annotationsToDetect) {
final List<ClassOrInterfaceTypeDetails> types = new ArrayList<ClassOrInterfaceTypeDetails>();
processTypesWithAnnotation(Arrays.asList(annotationsToDetect), new LocatedTypeCallback() {
public void process(final ClassOrInterfaceTypeDetails located) {
if (located != null) {
types.add(located);
}
}
});
Collections.sort(types, new NaturalOrderComparator<ClassOrInterfaceTypeDetails>() {
@Override
protected String stringify(final ClassOrInterfaceTypeDetails object) {
return object.getName().getSimpleTypeName();
}
});
return Collections.unmodifiableSet(new LinkedHashSet<ClassOrInterfaceTypeDetails>(types));
}
public Set<ClassOrInterfaceTypeDetails> findClassesOrInterfaceDetailsWithTag(final Object tag) {
Validate.notNull(tag, "Tag required");
final Set<ClassOrInterfaceTypeDetails> types = new LinkedHashSet<ClassOrInterfaceTypeDetails>();
processTypesWithTag(tag, new LocatedTypeCallback() {
public void process(final ClassOrInterfaceTypeDetails located) {
if (located != null) {
types.add(located);
}
}
});
return Collections.unmodifiableSet(types);
}
public Set<JavaType> findTypesWithAnnotation(final JavaType... annotationsToDetect) {
return findTypesWithAnnotation(Arrays.asList(annotationsToDetect));
}
public Set<JavaType> findTypesWithAnnotation(final List<JavaType> annotationsToDetect) {
Validate.notNull(annotationsToDetect, "Annotations to detect required");
final Set<JavaType> types = new LinkedHashSet<JavaType>();
processTypesWithAnnotation(annotationsToDetect, new LocatedTypeCallback() {
public void process(final ClassOrInterfaceTypeDetails located) {
if (located != null) {
types.add(located.getName());
}
}
});
return Collections.unmodifiableSet(types);
}
public List<JavaPackage> getPackagesForModule(Pom module) {
List<JavaPackage> packages = new ArrayList<JavaPackage>();
for (JavaType type : getTypesForModule(module)) {
if (!packages.contains(type.getPackage())) {
packages.add(type.getPackage());
}
}
return packages;
}
public List<JavaPackage> getPackagesForModule(String moduleName) {
return this.getPackagesForModule(getProjectOperations().getPomFromModuleName(moduleName));
}
private String getParentPath(final JavaType javaType) {
final String relativePath = javaType.getRelativeFileName();
for (final String typePath : discoverTypes()) {
if (typePath.endsWith(relativePath)) {
return StringUtils.removeEnd(typePath, relativePath);
}
}
return null;
}
private PhysicalPath getPhysicalPath(final JavaType javaType) {
Validate.notNull(javaType, "Java type required");
final String parentPath = getParentPath(javaType);
if (parentPath == null) {
return null;
}
for (final Pom pom : getProjectOperations().getPoms()) {
for (final PhysicalPath physicalPath : pom.getPhysicalPaths()) {
if (physicalPath.isSource()) {
final String pathLocation =
FileUtils.ensureTrailingSeparator(physicalPath.getLocationPath());
if (pathLocation.startsWith(parentPath)) {
getTypeCache().cacheTypeAgainstModule(pom, javaType);
return physicalPath;
}
}
}
}
return null;
}
public String getPhysicalTypeCanonicalPath(final JavaType javaType, final LogicalPath path) {
return getPhysicalTypeCanonicalPath(PhysicalTypeIdentifier.createIdentifier(javaType, path));
}
public String getPhysicalTypeCanonicalPath(final String physicalTypeId) {
final LogicalPath logicalPath = PhysicalTypeIdentifier.getPath(physicalTypeId);
final JavaType javaType = PhysicalTypeIdentifier.getJavaType(physicalTypeId);
final Pom pom = getProjectOperations().getPomFromModuleName(logicalPath.getModule());
final String canonicalFilePath =
pom.getPathLocation(logicalPath.getPath()) + javaType.getRelativeFileName();
if (getFileManager().exists(canonicalFilePath)) {
getTypeCache().cacheTypeAgainstModule(pom, javaType);
getTypeCache().cacheFilePathAgainstTypeIdentifier(canonicalFilePath, physicalTypeId);
}
return canonicalFilePath;
}
public String getPhysicalTypeIdentifier(final JavaType type) {
final PhysicalPath containingPhysicalPath = getPhysicalPath(type);
if (containingPhysicalPath == null) {
return null;
}
final LogicalPath logicalPath = containingPhysicalPath.getLogicalPath();
return PhysicalTypeIdentifier.createIdentifier(type, logicalPath);
}
public String getPhysicalTypeIdentifier(final String fileCanonicalPath) {
Validate.notBlank(fileCanonicalPath, "File canonical path required");
if (!doesPathIndicateJavaType(fileCanonicalPath)) {
return null;
}
String physicalTypeIdentifier = getTypeCache().getTypeIdFromTypeFilePath(fileCanonicalPath);
if (physicalTypeIdentifier != null) {
return physicalTypeIdentifier;
}
final String typeDirectory = FileUtils.getFirstDirectory(fileCanonicalPath);
final String simpleTypeName =
StringUtils.replace(fileCanonicalPath, typeDirectory + File.separator, "", 1).replace(
".java", "");
final JavaPackage javaPackage = getTypeResolutionService().getPackage(fileCanonicalPath);
if (javaPackage == null) {
return null;
}
final Pom module = getProjectOperations().getModuleForFileIdentifier(fileCanonicalPath);
Validate.notNull(module, "The module for the file '" + fileCanonicalPath
+ "' could not be located");
final JavaType javaType =
new JavaType(javaPackage.getFullyQualifiedPackageName() + "." + simpleTypeName,
module.getModuleName());
getTypeCache().cacheTypeAgainstModule(module, javaType);
String reducedPath = fileCanonicalPath.replace(javaType.getRelativeFileName(), "");
reducedPath = StringUtils.stripEnd(reducedPath, File.separator);
for (final PhysicalPath physicalPath : module.getPhysicalPaths()) {
if (physicalPath.getLocationPath().startsWith(reducedPath)) {
final LogicalPath path = physicalPath.getLogicalPath();
physicalTypeIdentifier =
MetadataIdentificationUtils.create(PhysicalTypeIdentifier.class.getName(),
path.getName() + "?" + javaType.getFullyQualifiedTypeName());
break;
}
}
getTypeCache().cacheFilePathAgainstTypeIdentifier(fileCanonicalPath, physicalTypeIdentifier);
return physicalTypeIdentifier;
}
public List<String> getPotentialTopLevelPackagesForModule(final Pom module) {
Validate.notNull(module, "Module required");
final Map<String, Set<String>> packageMap = new HashMap<String, Set<String>>();
final Set<String> moduleTypes = getTypesForModule(module.getPath());
final List<String> topLevelPackages = new ArrayList<String>();
if (moduleTypes.isEmpty()) {
topLevelPackages.add(module.getGroupId());
return topLevelPackages;
}
for (final String typeName : moduleTypes) {
final StringBuilder sb = new StringBuilder();
final String type = getPackageFromType(typeName);
final String[] typeSegments = type.split("\\.");
final Set<String> discoveredPackages = new HashSet<String>();
for (int i = 0; i < typeSegments.length; i++) {
final String typeSegment = typeSegments[i];
if (i > 0) {
sb.append(".");
}
sb.append(typeSegment);
discoveredPackages.add(sb.toString());
}
for (final String discoveredPackage : discoveredPackages) {
if (!packageMap.containsKey(discoveredPackage)) {
packageMap.put(discoveredPackage, new HashSet<String>());
}
packageMap.get(discoveredPackage).add(typeName);
}
}
int longestPackage = 0;
for (final Map.Entry<String, Set<String>> entry : packageMap.entrySet()) {
if (entry.getValue().size() == moduleTypes.size()) {
topLevelPackages.add(entry.getKey());
if (entry.getKey().length() > longestPackage) {
longestPackage = entry.getKey().length();
}
}
}
return topLevelPackages;
}
private String getProposedJavaType(final String fileCanonicalPath) {
Validate.notBlank(fileCanonicalPath, "File canonical path required");
// Determine the JavaType for this file
String relativePath = "";
final Pom moduleForFileIdentifier =
getProjectOperations().getModuleForFileIdentifier(fileCanonicalPath);
if (moduleForFileIdentifier == null) {
return relativePath;
}
for (final PhysicalPath physicalPath : moduleForFileIdentifier.getPhysicalPaths()) {
final String moduleCanonicalPath =
FileUtils.ensureTrailingSeparator(FileUtils.getCanonicalPath(physicalPath.getLocation()));
if (fileCanonicalPath.startsWith(moduleCanonicalPath)) {
relativePath =
File.separator + StringUtils.replace(fileCanonicalPath, moduleCanonicalPath, "", 1);
break;
}
}
Validate.notBlank(relativePath, "Could not determine compilation unit name for file '%s'",
fileCanonicalPath);
Validate.isTrue(relativePath.startsWith(File.separator),
"Relative path unexpectedly dropped the '%s' prefix (received '%s' from '%s')",
File.separator, relativePath, fileCanonicalPath);
relativePath = relativePath.substring(1);
Validate.isTrue(relativePath.endsWith(".java"),
"The relative path unexpectedly dropped the .java extension for file '%s'",
fileCanonicalPath);
relativePath = relativePath.substring(0, relativePath.lastIndexOf(".java"));
return relativePath.replace(File.separatorChar, '.');
}
public String getTopLevelPackageForModule(final Pom module) {
if (StringUtils.isBlank(module.getModuleName())) {
// Parent module or project package
return module.getGroupId();
} else {
//Module package
return module.getGroupId().concat(".").concat(module.getArtifactId());
}
}
/**
* Builds a map of a module's packages and all types anywhere within them,
* e.g. if there's two types com.foo.bar.A and com.foo.baz.B, the map will
* look like this: "com": {"com.foo.bar.A", "com.foo.baz.B"} "com.foo":
* {"com.foo.bar.A", "com.foo.baz.B"}, "com.foo.bar": {"com.foo.bar.A"},
* "com.foo.baz": {"com.foo.baz.B"} All packages that directly contain a
* type are added to the given set.
*
* @param typesInModule the Java types within the module in question
* @param typePackages the set to which to add each type's package
* @return a non-<code>null</code> map laid out as above
*/
@SuppressWarnings("unused")
private Map<String, Collection<String>> getTypesByPackage(final Iterable<String> typesInModule,
final Set<String> typePackages) {
final Map<String, Collection<String>> typesByPackage =
new HashMap<String, Collection<String>>();
for (final String typeName : typesInModule) {
final String typePackage = getPackageFromType(typeName);
typePackages.add(typePackage);
for (final String discoveredPackage : getAllPackages(typePackage)) {
if (typesByPackage.get(discoveredPackage) == null) {
typesByPackage.put(discoveredPackage, new HashSet<String>());
}
typesByPackage.get(discoveredPackage).add(typeName);
}
}
return typesByPackage;
}
public ClassOrInterfaceTypeDetails getTypeDetails(final JavaType type) {
return getTypeDetails(getPhysicalTypeIdentifier(type));
}
public ClassOrInterfaceTypeDetails getTypeDetails(final String physicalTypeId) {
if (StringUtils.isBlank(physicalTypeId)) {
return null;
}
Validate.isTrue(PhysicalTypeIdentifier.isValid(physicalTypeId),
"Metadata id '%s' is not a valid physical type id", physicalTypeId);
updateTypeCache();
final ClassOrInterfaceTypeDetails cachedDetails = getTypeCache().getTypeDetails(physicalTypeId);
if (cachedDetails != null) {
return cachedDetails;
}
final PhysicalTypeMetadata physicalTypeMetadata =
(PhysicalTypeMetadata) getMetadataService().get(physicalTypeId);
if (physicalTypeMetadata == null) {
return null;
}
return physicalTypeMetadata.getMemberHoldingTypeDetails();
}
public LogicalPath getTypePath(final JavaType javaType) {
final String physicalTypeId = getPhysicalTypeIdentifier(javaType);
if (StringUtils.isBlank(physicalTypeId)) {
return null;
}
return PhysicalTypeIdentifier.getPath(physicalTypeId);
}
public Collection<JavaType> getTypesForModule(final Pom module) {
if ("pom".equals(module.getPackaging())) {
return Collections.emptySet();
}
final Set<String> typeNames = getTypesForModule(module.getPath());
final Collection<JavaType> javaTypes = new ArrayList<JavaType>();
for (final String typeName : typeNames) {
javaTypes.add(new JavaType(typeName, module.getModuleName()));
}
return javaTypes;
}
public Set<String> getTypesForModule(final String modulePath) {
Validate.notNull(modulePath, "Module path required");
return getTypeCache().getTypeNamesForModuleFilePath(modulePath);
}
public boolean hasTypeChanged(final String requestingClass, final JavaType javaType) {
Validate.notNull(requestingClass, "Requesting class required");
Validate.notNull(javaType, "Java type required");
updateTypeCache();
Set<String> changesSinceLastRequest = changeMap.get(requestingClass);
if (changesSinceLastRequest == null) {
changesSinceLastRequest = new LinkedHashSet<String>();
for (final String typeIdentifier : getTypeCache().getAllTypeIdentifiers()) {
changesSinceLastRequest.add(getTypeCache().getTypeDetails(typeIdentifier).getName()
.getFullyQualifiedTypeName());
}
changeMap.put(requestingClass, changesSinceLastRequest);
}
for (final String changedId : changesSinceLastRequest) {
if (changedId.equals(javaType.getFullyQualifiedTypeName())) {
changesSinceLastRequest.remove(changedId);
return true;
}
}
return false;
}
private void initTypeMap() {
for (final Pom pom : getProjectOperations().getPoms()) {
for (final PhysicalPath path : pom.getPhysicalPaths()) {
if (path.isSource()) {
final String allJavaFiles =
FileUtils.ensureTrailingSeparator(path.getLocationPath()) + JAVA_FILES_ANT_PATH;
for (final FileDetails file : getFileManager().findMatchingAntPath(allJavaFiles)) {
cacheType(file.getCanonicalPath());
}
}
}
}
}
public boolean isInProject(final JavaType javaType) {
return javaType != null && !javaType.isCoreType() && getPhysicalPath(javaType) != null;
}
/**
* Obtains the a fresh copy of the {@link ClassOrInterfaceTypeDetails} for
* the given physical type.
*
* @param physicalTypeIdentifier to lookup (required)
* @return the requested details (or <code>null</code> if unavailable)
*/
private ClassOrInterfaceTypeDetails lookupClassOrInterfaceTypeDetails(
final String physicalTypeIdentifier) {
final PhysicalTypeMetadata physicalTypeMetadata =
(PhysicalTypeMetadata) getMetadataService().evictAndGet(physicalTypeIdentifier);
if (physicalTypeMetadata == null) {
return null;
}
return physicalTypeMetadata.getMemberHoldingTypeDetails();
}
public void processTypesWithAnnotation(final List<JavaType> annotationsToDetect,
final LocatedTypeCallback callback) {
Validate.notNull(annotationsToDetect, "Annotations to detect required");
Validate.notNull(callback, "Callback required");
// If the cache doesn't yet contain the annotation to be found it should
// be added
for (final JavaType annotationType : annotationsToDetect) {
if (!annotationToMidMap.containsKey(annotationType)) {
annotationToMidMap.put(annotationType, new HashSet<String>());
}
}
// Before processing the call any changes to the project should be
// processed and the cache updated accordingly
updateTypeCache();
for (final JavaType annotationType : annotationsToDetect) {
for (final String locatedMid : annotationToMidMap.get(annotationType)) {
final ClassOrInterfaceTypeDetails located = getTypeCache().getTypeDetails(locatedMid);
callback.process(located);
}
}
}
private void processTypesWithTag(final Object tag, final LocatedTypeCallback callback) {
Validate.notNull(tag, "Tag required");
Validate.notNull(callback, "Callback required");
// If the cache doesn't yet contain the tag it should be added
if (!tagToMidMap.containsKey(tag)) {
tagToMidMap.put(tag, new HashSet<String>());
}
// Before processing the call any changes to the project should be
// processed and the cache updated accordingly
updateTypeCache();
for (final String locatedMid : tagToMidMap.get(tag)) {
final ClassOrInterfaceTypeDetails located = getTypeCache().getTypeDetails(locatedMid);
callback.process(located);
}
}
private void updateAttributeCache(final MemberHoldingTypeDetails cid) {
Validate.notNull(cid, "Member holding type details required");
if (!typeAnnotationMap.containsKey(cid.getDeclaredByMetadataId())) {
typeAnnotationMap.put(cid.getDeclaredByMetadataId(), new HashSet<JavaType>());
}
if (!typeCustomDataMap.containsKey(cid.getDeclaredByMetadataId())) {
typeCustomDataMap.put(cid.getDeclaredByMetadataId(), new HashSet<Object>());
}
final Set<JavaType> previousAnnotations = typeAnnotationMap.get(cid.getDeclaredByMetadataId());
for (final JavaType previousAnnotation : previousAnnotations) {
final Set<String> midSet = annotationToMidMap.get(previousAnnotation);
if (midSet != null) {
midSet.remove(cid.getDeclaredByMetadataId());
}
}
previousAnnotations.clear();
for (final AnnotationMetadata annotationMetadata : cid.getAnnotations()) {
if (!annotationToMidMap.containsKey(annotationMetadata.getAnnotationType())) {
annotationToMidMap.put(annotationMetadata.getAnnotationType(), new HashSet<String>());
}
previousAnnotations.add(annotationMetadata.getAnnotationType());
annotationToMidMap.get(annotationMetadata.getAnnotationType()).add(
cid.getDeclaredByMetadataId());
}
final Set<Object> previousCustomDataSet = typeCustomDataMap.get(cid.getDeclaredByMetadataId());
for (final Object previousCustomData : previousCustomDataSet) {
final Set<String> midSet = tagToMidMap.get(previousCustomData);
if (midSet != null) {
midSet.remove(cid.getDeclaredByMetadataId());
}
}
previousCustomDataSet.clear();
for (final Object customData : cid.getCustomData().keySet()) {
if (!tagToMidMap.containsKey(customData)) {
tagToMidMap.put(customData, new HashSet<String>());
}
previousCustomDataSet.add(customData);
tagToMidMap.get(customData).add(cid.getDeclaredByMetadataId());
}
}
private void updateChanges(final String typeName, final boolean remove) {
Validate.notNull(typeName, "Type name required");
for (final String requestingClass : changeMap.keySet()) {
if (remove) {
changeMap.get(requestingClass).remove(typeName);
} else {
changeMap.get(requestingClass).add(typeName);
}
}
}
private void updateTypeCache() {
if (getTypeCache().getAllTypeIdentifiers().isEmpty()) {
initTypeMap();
}
discoverTypes();
// Update the type cache
for (final String change : dirtyFiles) {
cacheType(change);
}
dirtyFiles.clear();
}
public void addDependencies(ModuleFeatureName moduleFeatureName,
final Collection<? extends Dependency> newDependencies) {
for (String moduleName : getModuleNames(moduleFeatureName)) {
getProjectOperations().addDependencies(moduleName, newDependencies);
}
}
public void addModuleDependency(final String moduleName, final JavaType moduleJavaTypeToDependUpon) {
if (moduleJavaTypeToDependUpon.getModule() != null) {
getProjectOperations()
.addModuleDependency(moduleName, moduleJavaTypeToDependUpon.getModule());
} else {
ClassOrInterfaceTypeDetails details = getTypeDetails(moduleJavaTypeToDependUpon);
if (details != null && details.getName().getModule() != null) {
getProjectOperations().addModuleDependency(moduleName, details.getName().getModule());
}
}
}
public void removeDependencies(ModuleFeatureName moduleFeatureName,
final Collection<? extends Dependency> newDependencies) {
for (String moduleName : getModuleNames(moduleFeatureName)) {
getProjectOperations().removeDependencies(moduleName, newDependencies);
}
}
@Override
public Collection<Pom> getModules(ModuleFeatureName moduleFeatureName) {
Validate.notNull(moduleFeatureName, "Module featured required");
List<Pom> modules = new ArrayList<Pom>();
ModuleFeature moduleFeature = getModuleFeature(moduleFeatureName);
if (moduleFeature != null) {
modules = moduleFeature.getModules();
}
return modules;
}
@Override
public Collection<String> getModuleNames(ModuleFeatureName moduleFeatureName) {
Validate.notNull(moduleFeatureName, "Module featured required");
List<String> moduleNames = new ArrayList<String>();
ModuleFeature moduleFeature = getModuleFeature(moduleFeatureName);
if (moduleFeature != null) {
moduleNames = moduleFeature.getModuleNames();
}
return moduleNames;
}
public boolean hasModuleFeature(Pom module, ModuleFeatureName moduleFeatureName) {
Validate.notNull(moduleFeatureName, "Module featured required");
Validate.notNull(module, "Module required");
ModuleFeature moduleFeature = getModuleFeature(moduleFeatureName);
if (moduleFeature != null) {
return moduleFeature.hasModuleFeature(module);
}
return false;
}
public FileManager getFileManager() {
if (fileManager == null) {
// Get all Services implement FileManager interface
try {
ServiceReference<?>[] references =
context.getAllServiceReferences(FileManager.class.getName(), null);
for (ServiceReference<?> ref : references) {
return (FileManager) context.getService(ref);
}
return null;
} catch (InvalidSyntaxException e) {
LOGGER.warning("Cannot load FileManager on TypeLocationServiceImpl.");
return null;
}
} else {
return fileManager;
}
}
public FileMonitorService getFileMonitorService() {
if (fileMonitorService == null) {
// Get all Services implement FileMonitorService interface
try {
ServiceReference<?>[] references =
context.getAllServiceReferences(FileMonitorService.class.getName(), null);
for (ServiceReference<?> ref : references) {
return (FileMonitorService) context.getService(ref);
}
return null;
} catch (InvalidSyntaxException e) {
LOGGER.warning("Cannot load FileMonitorService on TypeLocationServiceImpl.");
return null;
}
} else {
return fileMonitorService;
}
}
public MetadataService getMetadataService() {
if (metadataService == null) {
// Get all Services implement MetadataService interface
try {
ServiceReference<?>[] references =
context.getAllServiceReferences(MetadataService.class.getName(), null);
for (ServiceReference<?> ref : references) {
return (MetadataService) context.getService(ref);
}
return null;
} catch (InvalidSyntaxException e) {
LOGGER.warning("Cannot load MetadataService on TypeLocationServiceImpl.");
return null;
}
} else {
return metadataService;
}
}
public ProjectOperations getProjectOperations() {
if (projectOperations == null) {
// Get all Services implement ProjectOperations interface
try {
ServiceReference<?>[] references =
context.getAllServiceReferences(ProjectOperations.class.getName(), null);
for (ServiceReference<?> ref : references) {
return (ProjectOperations) context.getService(ref);
}
return null;
} catch (InvalidSyntaxException e) {
LOGGER.warning("Cannot load ProjectOperations on TypeLocationServiceImpl.");
return null;
}
} else {
return projectOperations;
}
}
public TypeCache getTypeCache() {
if (typeCache == null) {
// Get all Services implement TypeCache interface
try {
ServiceReference<?>[] references =
context.getAllServiceReferences(TypeCache.class.getName(), null);
for (ServiceReference<?> ref : references) {
return (TypeCache) context.getService(ref);
}
return null;
} catch (InvalidSyntaxException e) {
LOGGER.warning("Cannot load TypeCache on TypeLocationServiceImpl.");
return null;
}
} else {
return typeCache;
}
}
public TypeResolutionService getTypeResolutionService() {
if (typeResolutionService == null) {
// Get all Services implement TypeResolutionService interface
try {
ServiceReference<?>[] references =
context.getAllServiceReferences(TypeResolutionService.class.getName(), null);
for (ServiceReference<?> ref : references) {
return (TypeResolutionService) context.getService(ref);
}
return null;
} catch (InvalidSyntaxException e) {
LOGGER.warning("Cannot load TypeResolutionService on TypeLocationServiceImpl.");
return null;
}
} else {
return typeResolutionService;
}
}
private ModuleFeature getModuleFeature(ModuleFeatureName moduleFeatureName) {
if (moduleFeatures.containsKey(moduleFeatureName)) {
return moduleFeatures.get(moduleFeatureName);
}
try {
ServiceReference<?>[] references =
context.getAllServiceReferences(ModuleFeature.class.getName(), null);
for (ServiceReference<?> ref : references) {
ModuleFeature moduleFeature = (ModuleFeature) context.getService(ref);
if (moduleFeature.getName().equals(moduleFeatureName)) {
moduleFeatures.put(moduleFeatureName, moduleFeature);
return moduleFeature;
}
}
return null;
} catch (InvalidSyntaxException e) {
LOGGER.warning("Cannot load ModuleFeature on TypeLocationServiceImpl.");
return null;
}
}
}