package husacct.analyse.domain.famix; import husacct.common.dto.DependencyDTO; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Set; import java.util.TreeMap; import org.apache.log4j.Logger; class FamixDependencyFinder extends FamixFinder { private static enum FinderFunction { FROM, TO, BOTH, ALL }; private List<DependencyDTO> dependencyCache; private final Logger logger = Logger.getLogger(FamixDependencyFinder.class); protected int numberOfDuplicateAssociations; // HashMap dependenciesMapFromTo has as first key classPathFrom, as second key classPathTo, and as value a list of dependencies. private HashMap<String, HashMap<String, ArrayList<DependencyDTO>>> dependenciesMapFromTo; // HashMap dependenciesMapTo has as key classPathTo and as value a list of all dependencies to this class (from all classes to the selected class). private HashMap<String, ArrayList<DependencyDTO>> dependenciesMapTo; public FamixDependencyFinder(FamixModel model) { super(model); this.dependencyCache = null; } public void buildCache(){ int numberOfDependencies = getAllDependencies().size(); this.logger.info(new Date().toString() + " Dependencies added: " + numberOfDependencies + ", Removed duplicates: " + numberOfDuplicateAssociations); initializeDependencyHashMap(); return; } public void importDependencies(List<DependencyDTO> dependencies){ dependencyCache = dependencies; } public List<DependencyDTO> getAllDependencies(){ if(dependencyCache == null) dependencyCache = findDependencies(FinderFunction.ALL, "", ""); return dependencyCache; } // Deprecated public List<DependencyDTO> getDependencies(String from, String to, String[] dependencyFilter){ return findDependencies(FinderFunction.BOTH, from, to, dependencyFilter); } // Deprecated public List<DependencyDTO> getDependenciesFrom(String from, String[] dependencyFilter){ return findDependencies(FinderFunction.FROM, from, "", dependencyFilter); } // Returns all dependencies for the exact match from classPathFrom and classPathTo // Either classPathTFrom or classPathTo should have a value other than "", otherwise an empty array is returned. // If classPathTFrom = "", then all dependencies to classPathTo are returned, which refer to existing classPathFrom's. // If classPathTo = "", then all dependencies from classPathFrom are returned, which refer to existing classPathTo's. public ArrayList<DependencyDTO> getDependenciesFromTo(String classPathFrom, String classPathTo){ ArrayList<DependencyDTO> foundDependencies = new ArrayList<DependencyDTO>(); if((classPathFrom == null || classPathFrom.equals("")) && (classPathTo == null || classPathTo.equals(""))){ this.logger.warn(" Incomplete calls: ClassPathFrom = " + classPathFrom + ", ClassPathTo = " + classPathTo); return foundDependencies; } try{ if ((dependenciesMapFromTo != null) && (dependenciesMapTo != null)){ if(!classPathFrom.equals("")){ // Select all dependencies within dependenciesMapFromTo whose pathFrom equals classPathFrom HashMap<String, ArrayList<DependencyDTO>> fromMap = dependenciesMapFromTo.get(classPathFrom); // Select all dependencies within fromMap whose pathTo starts with classPathTo if(fromMap != null ){ if(!classPathTo.equals("")){ ArrayList<DependencyDTO> dependencyList = fromMap.get(classPathTo); if(dependencyList != null){ foundDependencies.addAll(dependencyList); } } else{ // If classPathTo = "", then find all dependencies from classPathFrom. Add each, when it refers to an existing classPathTo Set<String> keyset = fromMap.keySet(); for(String keyTo : keyset){ ArrayList<DependencyDTO> dependencyList = fromMap.get(keyTo); if(dependencyList != null){ for(DependencyDTO dependency : dependencyList){ if((theModel.classes.containsKey(dependency.to)) || (theModel.libraries.containsKey(dependency.to))){ foundDependencies.add(dependency); } } } } } } } else{ //classPathFrom = "" // Select the list of dependencies within dependenciesMapTo,where the key equals classPathTo. ArrayList<DependencyDTO> toDependenciesList = dependenciesMapTo.get(classPathTo); if(toDependenciesList != null ){ foundDependencies = toDependenciesList; } } } } catch (Exception e) { this.logger.error(" Exception: " + e); //e.printStackTrace(); } return foundDependencies; } // Deprecated public List<DependencyDTO> getDependenciesTo(String to, String[] dependencyFilter){ return findDependencies(FinderFunction.TO, "", to, dependencyFilter); } private List<DependencyDTO> findDependencies(FinderFunction findFunction, String from, String to){ return findDependencies(findFunction, from, to, null); } private List<DependencyDTO> findDependencies(FinderFunction findFunction, String from, String to, String[] applyFilter){ if(dependencyCache == null) return findDependenciesRaw(findFunction, from, to, applyFilter); else return queryCache(findFunction, from, to, applyFilter); } // Returns a list of filtered and sorted DependencyDTOs. private List<DependencyDTO> findDependenciesRaw(FinderFunction findFunction, String from, String to, String[] applyFilter){ List<DependencyDTO> foundDependenciesReturnList = new ArrayList<DependencyDTO>(); TreeMap<String, DependencyDTO> foundDependenciesTreeMap = new TreeMap<String, DependencyDTO>(); numberOfDuplicateAssociations = 0; try { for(FamixAssociation association : theModel.associations){ if(compliesWithFunction(association, findFunction, from, to) && compliesWithFilter(association, applyFilter)){ if (association.from == null || association.from.equals("") || association.to == null || association.to.equals("") ||association.lineNumber == 0 || association.type == null){ // Do not add the association as a dependency, since it is incomplete. } else{ // Filter out dependencies if from and to do not refer to types String libraryRoot = "xLibraries."; if(!(theModel.classes.containsKey(association.from) && (theModel.classes.containsKey(association.to) || theModel.libraries.containsKey((libraryRoot + association.to))))){ // Do not add the association as a dependency, since it is inconsistent with theModel. } else { if (theModel.libraries.containsKey((libraryRoot + association.to))) { association.to = libraryRoot + association.to; // Prefix it with the libraryRoot to present external systems everywhere the same to the tool users. } // Filter-out duplicate associations. String uniqueName = (association.from + association.to + association.lineNumber + association.type + association.subType + Boolean.toString(association.isIndirect)); if (!foundDependenciesTreeMap.containsKey(uniqueName)){ // Create Dependency and add to result DependencyDTO foundDependency = new DependencyDTO(association.from, association.to, association.type, association.subType ,association.lineNumber, association.isIndirect, association.isInheritanceRelated); if (association instanceof FamixInvocation) { FamixInvocation invocation = (FamixInvocation) association; foundDependency.usedEntity = invocation.usedEntity; foundDependency.belongsToMethod = invocation.belongsToMethod; foundDependency.statement = invocation.statement; } if (theModel.classes.get(association.from).isInnerClass || (!theModel.libraries.containsKey(association.to) && theModel.classes.get(association.to).isInnerClass)) { foundDependency.isInnerClassRelated = true; } foundDependenciesTreeMap.put(uniqueName, foundDependency); } else { numberOfDuplicateAssociations ++; } } } } else{ // Do not add the association as a dependency, since it does not comply. } } } catch (Exception e) { this.logger.error(" Exception: " + e); //e.printStackTrace(); } foundDependenciesReturnList.addAll(foundDependenciesTreeMap.values()); return foundDependenciesReturnList; } private List<DependencyDTO> queryCache(FinderFunction findFunction, String from, String to, String[] applyFilter){ List<DependencyDTO> foundDependencies = new ArrayList<DependencyDTO>(); for(DependencyDTO dependency : dependencyCache){ switch(findFunction){ case FROM: if(isFrom(dependency, from)){ foundDependencies.add(dependency); } break; case TO: if(isTo(dependency, to)){ foundDependencies.add(dependency); } break; case BOTH: if(isFrom(dependency, from) && isTo(dependency, to)){ foundDependencies.add(dependency); } break; case ALL: return dependencyCache; default: break; } } return foundDependencies; } private boolean compliesWithFilter(FamixAssociation association, String[] filter){ if(filter == null || filter.length == 0) return true; for(String loopingFilter : filter) if(association.type.equals(loopingFilter)) return true; return false; } private boolean compliesWithFunction(FamixAssociation association, FinderFunction findFunction, String from, String to){ switch(findFunction){ case BOTH: return isFrom(association, from) && isTo(association, to); case FROM: return isFrom(association, from); case TO: return isTo(association, to); case ALL: return true; } return false; } private boolean isFrom(FamixAssociation association, String from){ boolean result = from.equals("") || association.from.equals(from); result = result || association.from.startsWith(from + "."); result = result && !association.from.equals(association.to); return result; } private boolean isFrom(DependencyDTO dependency, String from){ boolean result = from.equals("") || dependency.from.equals(from); result = result || dependency.from.startsWith(from + "."); result = result && !dependency.from.equals(dependency.to); return result; } private boolean isTo(FamixAssociation association, String to){ boolean result = to.equals("") || association.to.equals(to); result = result || association.to.startsWith(to + "."); result = result && !association.to.equals(association.from); return result; } private boolean isTo(DependencyDTO dependency, String to){ boolean result = to.equals("") || dependency.to.equals(to); result = result || dependency.to.startsWith(to + "."); result = result && !dependency.to.equals(dependency.from); return result; } // Fill the HashMaps dependenciesMapFromTo and dependenciesMapTo public void initializeDependencyHashMap(){ this.dependenciesMapFromTo = new HashMap<String, HashMap<String, ArrayList<DependencyDTO>>>(); this.dependenciesMapTo = new HashMap<String, ArrayList<DependencyDTO>>(); HashMap<String, ArrayList<DependencyDTO>> toMap; try{ for(DependencyDTO dependency : getAllDependencies()) { String uniqueNameFrom = dependency.from; String uniqueNameTo = dependency.to; // Fill dependenciesMapFromTo if(dependenciesMapFromTo.containsKey(uniqueNameFrom)){ toMap = dependenciesMapFromTo.get(uniqueNameFrom); if(toMap.containsKey(uniqueNameTo)){ ArrayList<DependencyDTO> matchingDependencies = toMap.get(uniqueNameTo); matchingDependencies.add(dependency); } else{ // No toMap exists for the to-key, so create it. ArrayList<DependencyDTO> newList = new ArrayList<DependencyDTO>(); newList.add(dependency); toMap.put(uniqueNameTo, newList); } } else{ // No map exists for the from-key, so add it. ArrayList<DependencyDTO> newList = new ArrayList<DependencyDTO>(); newList.add(dependency); toMap = new HashMap<String, ArrayList<DependencyDTO>>(); toMap.put(uniqueNameTo, newList); dependenciesMapFromTo.put(uniqueNameFrom, toMap); } // Fill dependenciesMapTo if(dependenciesMapTo.containsKey(uniqueNameTo)){ ArrayList<DependencyDTO> matchingDependencies = dependenciesMapTo.get(uniqueNameTo); matchingDependencies.add(dependency); } else{ // No list exists for uniqueNameTo, so add it. ArrayList<DependencyDTO> newList = new ArrayList<DependencyDTO>(); newList.add(dependency); dependenciesMapTo.put(uniqueNameTo, newList); } } } catch(Exception e) { this.logger.warn("Exception may result in incomplete dependency list. Exception: " + e); //e.printStackTrace(); } } }