package husacct.analyse.domain.famix; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import javax.naming.directory.InvalidAttributesException; import org.apache.log4j.Logger; import husacct.analyse.domain.IModelCreationService; import husacct.common.dto.SoftwareUnitDTO; import husacct.common.enums.DependencySubTypes; import husacct.common.enums.DependencyTypes; public class FamixCreationServiceImpl implements IModelCreationService { private FamixModel model; private FamixCreationPostProcessor creationPostProcessor; private final Logger logger = Logger.getLogger(FamixCreationServiceImpl.class); int numberOfInternalClassesAddedBasedOnImports = 0; public FamixCreationServiceImpl() { model = FamixModel.getInstance(); creationPostProcessor = new FamixCreationPostProcessor(); } @Override public void createPackage(String uniqueName, String belongsToPackage, String name) { if (!model.packages.containsKey(uniqueName)){ FamixPackage fPackage = new FamixPackage(); fPackage.uniqueName = uniqueName; fPackage.belongsToPackage = belongsToPackage; fPackage.name = name; if ((!belongsToPackage.equals("") && (!model.packages.containsKey(belongsToPackage)))){ createPackageParent(belongsToPackage); } addToModel(fPackage); } } private void createPackageParent(String uniquePackageName){ String belongsToPackage = ""; String name = ""; if (uniquePackageName.contains(".")) { // Determine parentPackageName String[] allPackages = uniquePackageName.split("\\."); for (int i = 0; i < allPackages.length - 1; i++) { if (belongsToPackage.equals("")) { belongsToPackage += allPackages[i]; } else { belongsToPackage += "." + allPackages[i]; } } name = allPackages[allPackages.length - 1]; } else { name = uniquePackageName; } createPackage(uniquePackageName, belongsToPackage, name); } @Override public void createClass(String sourceFilePath, int linesOfCode, String uniqueName, String name, String belongsToPackage, boolean isAbstract, boolean isInnerClass, String belongsToClass, String visibility, boolean isInterface, boolean isEnumeration) { if ((uniqueName != null) && !uniqueName.equals("") && (name != null) && !name.equals("") && (belongsToPackage != null) && !belongsToPackage.equals("") && (belongsToClass != null)) { FamixClass fClass = new FamixClass(); fClass.sourceFilePath = sourceFilePath; fClass.linesOfCode = linesOfCode; fClass.uniqueName = uniqueName.trim(); fClass.isAbstract = isAbstract; fClass.belongsToPackage = belongsToPackage.trim(); fClass.isInnerClass = isInnerClass; fClass.name = name.trim(); fClass.belongsToClass = belongsToClass; if (visibility.equals("")) { fClass.visibility = "default"; } else { fClass.visibility = visibility; } fClass.isInterface = isInterface; fClass.isEnumeration = isEnumeration; addToModel(fClass); } else { // String breakpoint = "true"; // Test helper } } @Override public void createImport(String importingClass, String importedModule, int lineNumber, String completeImportString, boolean importsCompletePackage) { FamixImport fImport = new FamixImport(); fImport.from = importingClass; fImport.to = importedModule; fImport.lineNumber = lineNumber; fImport.importingClass = importingClass; fImport.completeImportString = completeImportString; fImport.importedModule = completeImportString; fImport.importsCompletePackage = importsCompletePackage; addToModel(fImport); } @Override public void createMethodOnly(String name, String uniqueName, String visibility, String signature, String declaredReturnType, String belongsToClass, boolean isConstructor, boolean isAbstract, boolean hasClassScope, int lineNumber) { FamixMethod famixMethod = new FamixMethod(); famixMethod.name = name; famixMethod.uniqueName = uniqueName; famixMethod.visibility = visibility; if (signature.equals("")) { signature = "()"; } famixMethod.signature = signature; famixMethod.declaredReturnType = declaredReturnType; famixMethod.belongsToClass = belongsToClass; famixMethod.isConstructor = isConstructor; famixMethod.isAbstract = isAbstract; famixMethod.hasClassScope = hasClassScope; addToModel(famixMethod); } @Override public void createMethod(String name, String uniqueName, String visibility, String signature, String declaredReturnType, String belongsToClass, boolean isConstructor, boolean isAbstract, boolean hasClassScope, int lineNumber) { createMethodOnly(name, uniqueName, visibility, signature, declaredReturnType, belongsToClass, isConstructor, isAbstract, hasClassScope, lineNumber); if ((declaredReturnType != null) && (!declaredReturnType.equals(""))) { FamixAssociation fAssocation = new FamixAssociation(); fAssocation.from = belongsToClass; fAssocation.to = declaredReturnType; fAssocation.type = DependencyTypes.DECLARATION.toString(); fAssocation.subType = DependencySubTypes.DECL_RETURN_TYPE.toString(); fAssocation.lineNumber = lineNumber; model.waitingAssociations.add(fAssocation); } } @Override public void createAttributeOnly(boolean classScope, boolean isFinal, String accesControlQualifier, String belongsToClass, String declareType, String name, String uniqueName, int line, String typeInClassDiagram, boolean isComposite) { FamixAttribute famixAttribute = new FamixAttribute(); famixAttribute.hasClassScope = classScope; famixAttribute.isFinal = isFinal; famixAttribute.accessControlQualifier = accesControlQualifier; famixAttribute.belongsToClass = belongsToClass; famixAttribute.declareType = declareType; famixAttribute.name = name; famixAttribute.uniqueName = uniqueName; famixAttribute.lineNumber = line; famixAttribute.typeInClassDiagram = typeInClassDiagram; famixAttribute.isComposite = isComposite; model.waitingStructuralEntities.add(famixAttribute); } @Override public void createAttribute(boolean classScope, boolean isFinal, String accesControlQualifier, String belongsToClass, String declareType, String name, String uniqueName, int line, String typeInClassDiagram, boolean isComposite) { createAttributeOnly(classScope, isFinal, accesControlQualifier, belongsToClass, declareType, name, uniqueName, line, typeInClassDiagram, isComposite); FamixAssociation fAssocation = new FamixAssociation(); fAssocation.from = belongsToClass; fAssocation.to = declareType; fAssocation.type = "Declaration"; if (classScope) { fAssocation.subType = "Class Variable"; } else { fAssocation.subType = "Instance Variable"; } fAssocation.lineNumber = line; model.waitingAssociations.add(fAssocation); } @Override public void createLocalVariable(String belongsToClass, String declareType, String name, String uniqueName, int lineNumber, String belongsToMethodString) { createLocalVariableOnly(belongsToClass, declareType, name, uniqueName, lineNumber, belongsToMethodString); FamixAssociation fAssocation = new FamixAssociation(); fAssocation.from = belongsToClass; fAssocation.to = declareType; fAssocation.type = "Declaration"; fAssocation.subType = "Local Variable"; fAssocation.lineNumber = lineNumber; model.waitingAssociations.add(fAssocation); } @Override public void createLocalVariableOnly(String belongsToClass, String declareType, String name, String uniqueName, int lineNumber, String belongsToMethodString) { FamixLocalVariable famixLocalVariable = new FamixLocalVariable(); famixLocalVariable.belongsToMethod = belongsToMethodString; famixLocalVariable.belongsToClass = belongsToClass; famixLocalVariable.declareType = declareType; famixLocalVariable.name = name; famixLocalVariable.uniqueName = uniqueName; famixLocalVariable.lineNumber = lineNumber; model.waitingStructuralEntities.add(famixLocalVariable); } @Override public void createParameterOnly(String name, String uniqueName, String declareType, String belongsToClass, int lineNumber, String belongsToMethod) { FamixFormalParameter famixParameter = new FamixFormalParameter(); famixParameter.belongsToClass = belongsToClass; famixParameter.belongsToMethod = belongsToMethod; famixParameter.declareType = declareType; famixParameter.lineNumber = lineNumber; famixParameter.name = name; famixParameter.uniqueName = uniqueName; model.waitingStructuralEntities.add(famixParameter); } @Override public void createParameter(String name, String uniqueName, String declareType, String belongsToClass, int lineNumber, String belongsToMethod) { this.createParameterOnly(name, uniqueName, declareType, belongsToClass, lineNumber, belongsToMethod); FamixAssociation fAssocation = new FamixAssociation(); fAssocation.from = belongsToClass; fAssocation.to = declareType; fAssocation.type = "Declaration"; fAssocation.subType = "Parameter"; fAssocation.lineNumber = lineNumber; model.waitingAssociations.add(fAssocation); } @Override public void createTypeParameter(String belongsToClass, int lineNumber, String parameterType) { // Currently, the parameter type (e.g. in case of HashSet<PT1>, or HashMap<PT2, PT3>) is not created as FamixObject. // Only the association is created, necessary to report the dependency on the declared type. FamixAssociation fAssocation = new FamixAssociation(); fAssocation.from = belongsToClass; fAssocation.to = parameterType; fAssocation.type = DependencyTypes.REFERENCE.toString(); fAssocation.subType = DependencySubTypes.REF_TYPE.toString(); fAssocation.lineNumber = lineNumber; model.waitingAssociations.add(fAssocation); } @Override public void createAnnotation(String belongsToClass, String declareType, String name, String uniqueName, int linenumber, String annotatedElement) { FamixAnnotation famixAnnotation = new FamixAnnotation(); famixAnnotation.belongsToClass = belongsToClass; famixAnnotation.declareType = declareType; famixAnnotation.name = name; famixAnnotation.uniqueName = uniqueName; famixAnnotation.annotatedElement = "class"; model.waitingStructuralEntities.add(famixAnnotation); FamixAssociation fAssocation = new FamixAssociation(); fAssocation.from = belongsToClass; fAssocation.to = declareType; fAssocation.type = "Annotation"; fAssocation.lineNumber = linenumber; model.waitingAssociations.add(fAssocation); } @Override public void createException(String fromClass, String exceptionClass, int lineNumber) { FamixException exception = new FamixException(); exception.from = fromClass; exception.to = exceptionClass; exception.lineNumber = lineNumber; model.waitingAssociations.add(exception); } @Override public void createInheritanceDefinition(String from, String to, int lineNumber) { FamixInheritanceDefinition famixInheritanceDefinition = new FamixInheritanceDefinition(); famixInheritanceDefinition.from = from; famixInheritanceDefinition.to = to; famixInheritanceDefinition.subType = "Extends Class"; famixInheritanceDefinition.lineNumber = lineNumber; model.waitingAssociations.add(famixInheritanceDefinition); } @Override public void createImplementsDefinition(String from, String to, int lineNumber) { FamixInheritanceDefinition fImplements = new FamixInheritanceDefinition(); fImplements.from = from; fImplements.to = to; fImplements.subType = "Implements Interface"; fImplements.lineNumber = lineNumber; model.waitingAssociations.add(fImplements); } @Override public void createDeclarationTypeCast(String from, String to, int lineNumber) { FamixAssociation fAssocation = new FamixAssociation(); fAssocation.from = from; fAssocation.to = to; fAssocation.type = "Declaration"; // Is changed afterwards in Reference. Don't change it here, cause it influences the postprocessing. fAssocation.subType = "Type Cast"; fAssocation.lineNumber = lineNumber; model.waitingAssociations.add(fAssocation); } @Override public void createMethodInvocation(String from, String to, int lineNumber, String belongsToMethod, String type) { FamixInvocation famixInvocation = new FamixInvocation(); famixInvocation.type = type; // "InvocMethod" or "InvocConstructor" famixInvocation.from = from; famixInvocation.lineNumber = lineNumber; famixInvocation.to = to; famixInvocation.belongsToMethod = belongsToMethod; model.waitingAssociations.add(famixInvocation); } @Override public void createVariableInvocation(String from, String to, int lineNumber, String belongsToMethod) { FamixInvocation famixInvocation = new FamixInvocation(); famixInvocation.type = "AccessVariable"; famixInvocation.from = from; famixInvocation.lineNumber = lineNumber; famixInvocation.to = to; famixInvocation.belongsToMethod = belongsToMethod; model.waitingAssociations.add(famixInvocation); } @Override public void executePostProcesses() { creationPostProcessor.processImports(); createClassesAndLibrariesBasedOnImports(); this.logger.info(new Date().toString() + " Finished: distinguisAndCreateLibraries(), Nr of Libraries = " + model.libraries.size()); int associationsNumber = model.associations.size(); this.logger.info(new Date().toString() + " Starting: processWaitingStructuralEntities(), Model.entities = " + model.structuralEntities.size() + ", WaitingStructuralEntities = " + model.waitingStructuralEntities.size()); creationPostProcessor.processWaitingStructuralEntities(); this.logger.info(new Date().toString() + " Finished: processWaitingStructuralEntities(), Model.entities = " + model.structuralEntities.size() + ", Model.associations = " + model.associations.size() + ", WaitingAssociations = " + model.waitingAssociations.size()); creationPostProcessor.processBehaviouralEntities(); creationPostProcessor.processInheritanceAssociations(); creationPostProcessor.processWaitingAssociations(); creationPostProcessor.processWaitingDerivedAssociations(); associationsNumber = model.associations.size(); model.clearAfterPostProcessing(); this.logger.info(new Date().toString() + " Finished: processWaitingAssociations(), Model.associations = " + associationsNumber + ", Not connected associations = " + creationPostProcessor.getNumberOfRejectedWaitingAssociations() + ", NumberOfInternalClassesAddedBasedOnImports = " + numberOfInternalClassesAddedBasedOnImports); } private void createClassesAndLibrariesBasedOnImports() { // Create a root package "ExternalLibraries" String rootLibraryPackage = "xLibraries"; createPackage(rootLibraryPackage, "", rootLibraryPackage); FamixPackage externalRoot = model.packages.get(rootLibraryPackage); externalRoot.external = true; // Select all imported types. Note: key of imports is combined from.to. HashMap<String, Boolean> completeImportStrings = new HashMap<String, Boolean>(); for(String importKey : model.imports.keySet()){ FamixImport foundImport = model.imports.get(importKey); completeImportStrings.put(foundImport.completeImportString, foundImport.importsCompletePackage); } // Get a list of rootPackagesWithClasses: the first packages (starting from the root) that contain one or more classes. // These rootPackagesWithClasses identify the paths to the systems internal classes. ArrayList<String> rootPackagesWithClassList = new ArrayList<String>(); FamixModuleFinder fmf = new FamixModuleFinder(model); List<SoftwareUnitDTO> rootModules = fmf.getRootModules(); for (SoftwareUnitDTO rootModule : rootModules) { rootPackagesWithClassList.addAll(fmf.getRootPackagesWithClass(rootModule.uniqueName)); } // Check for each completeImportString if it is an internal class. If not, create a FamixLibrary or package if it refers to a complete package or namespace. for(String completeImportString : completeImportStrings.keySet()){ boolean importsCompletePackage = completeImportStrings.get(completeImportString); if (!completeImportString.contains("*")) { //May be extended, eg: if((!completeImportString.startsWith("java.")) && (!completeImportString.startsWith("javax."))) // Determine if the complete import string starts with a root module and refers to an internal type. boolean isExternal = true; String rootModuleUniqueName = ""; for (String rootName : rootPackagesWithClassList){ if (completeImportString.startsWith(rootName)){ isExternal = false; rootModuleUniqueName = rootName; break; } } if (!isExternal) { // completeImportString refers to an internal package or type. If it's not a package, it must be a class (assuming that all packages are created). If the type is not registered yet, create it. if (!importsCompletePackage && !model.packages.containsKey(completeImportString)){ if(!model.classes.containsKey(completeImportString)){ createClassWithParentsBasedOnImport(completeImportString, rootModuleUniqueName); numberOfInternalClassesAddedBasedOnImports ++; } else { // Do nothing: class exists already } } else { // Do nothing: package exists already } } else { // completeImportString refers to an external package or type. Create a library with parent Library objects, if not registered already. String uniqueExternalName = rootLibraryPackage + "." + completeImportString; if(!model.libraries.containsKey(uniqueExternalName)){ createLibraryPackagewithParents(completeImportString, rootLibraryPackage, importsCompletePackage); } else { // Do nothing: library exists already } } } } } private void createClassWithParentsBasedOnImport(String completeImportString, String rootModuleUniqueName) { // 1) Determine parent; 2) if parent is package ...; 3) if parent is class ...; 4) if no parent is found // Create package for each substring, except for the last substring String packageName = ""; String packageUniqueName = ""; String packageParent = ""; String className = ""; // short name if (completeImportString.contains(".")) { if (completeImportString.lastIndexOf('.') != completeImportString.length() - 1) { String[] names = completeImportString.split("\\."); className = names[names.length -1]; packageParent = names[0]; for (int i = 1 ; i < (names.length -1); i++){ packageName = names[i]; packageUniqueName = packageParent + "." + names[i]; // Create a package if it is not registered already if (!model.packages.containsKey(packageUniqueName) && !model.classes.containsKey(packageUniqueName)) { // Needed in case the import refers to an inner class createPackage(packageUniqueName, packageParent, packageName); } packageParent = packageUniqueName; } } else { // Do nothing: Type name which finishes with a dot } } else { packageUniqueName = packageParent; className = completeImportString; } //Add as FamixClass FamixClass newClass = new FamixClass(); newClass.uniqueName = completeImportString; // Determine if the new class is an inner class if (model.classes.containsKey(packageUniqueName)) { // The class is an inner class newClass.belongsToPackage = packageUniqueName.substring(0, packageUniqueName.lastIndexOf(".")); //String containingClass = packageUniqueName.substring(packageUniqueName.lastIndexOf(".") + 1, packageUniqueName.length()); newClass.name = className; newClass.isInnerClass = true; newClass.belongsToClass = packageUniqueName; } else { newClass.belongsToPackage = packageUniqueName; newClass.name = className; newClass.isInnerClass = false; newClass.belongsToClass = ""; } newClass.isAbstract = false; newClass.isInterface = false; newClass.visibility = "public"; addToModel(newClass); } private void createLibraryPackagewithParents(String completeImportString, String rootLibraryPackage, boolean importsCompletePackage) { // Create a FamixLibrary for each substring. String libraryName = ""; String libraryUniqueName = ""; // physicalPath prefixed by rootLibraryPackage String libraryPhysicalPath = ""; // original path in the source code String libraryParentUniqueName = rootLibraryPackage; if (completeImportString.lastIndexOf('.') != completeImportString.length() - 1) { // If completeImportString doesn't end with a ".". String[] names = completeImportString.split("\\."); for (int i = 0 ; i < (names.length); i++){ libraryName = names[i]; libraryUniqueName = libraryParentUniqueName + "." + libraryName; if (i == 0) { libraryPhysicalPath = libraryName; } else { libraryPhysicalPath = libraryPhysicalPath + "." + libraryName; } // Create a library if it is not registered already if (!model.libraries.containsKey(libraryUniqueName)) { //Add completeImportString as FamixLibrary FamixLibrary fLibrary = new FamixLibrary(); fLibrary.name = libraryName; fLibrary.uniqueName = libraryUniqueName; fLibrary.physicalPath = libraryPhysicalPath; fLibrary.isPackage = importsCompletePackage; fLibrary.belongsToPackage = libraryParentUniqueName; fLibrary.visibility = "public"; addToModel(fLibrary); } libraryParentUniqueName = libraryUniqueName; } } else { // Do nothing: Type name which finishes with a dot } } private boolean addToModel(FamixObject newObject) { try { model.addObject(newObject); return true; } catch (InvalidAttributesException e) { this.logger.debug(new Date().toString() + e.getMessage()); return false; } } }