package com.redhat.ceylon.eclipse.core.builder; import static com.redhat.ceylon.model.typechecker.model.Module.LANGUAGE_MODULE_NAME; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.ITypeRoot; import org.eclipse.jdt.core.JavaModelException; import com.redhat.ceylon.compiler.java.loader.TypeFactory; import com.redhat.ceylon.compiler.typechecker.context.PhasedUnit; import com.redhat.ceylon.compiler.typechecker.tree.Tree; import com.redhat.ceylon.compiler.typechecker.tree.Visitor; import com.redhat.ceylon.ide.common.model.CeylonBinaryUnit; import com.redhat.ceylon.ide.common.model.ExternalSourceFile; import com.redhat.ceylon.ide.common.model.ICrossProjectReference; import com.redhat.ceylon.ide.common.model.IdeUnit; import com.redhat.ceylon.ide.common.model.JavaClassFile; import com.redhat.ceylon.ide.common.model.JavaCompilationUnit; import com.redhat.ceylon.ide.common.model.ProjectSourceFile; import com.redhat.ceylon.model.cmr.JDKUtils; import com.redhat.ceylon.model.typechecker.model.Declaration; import com.redhat.ceylon.model.typechecker.model.ModelUtil; import com.redhat.ceylon.model.typechecker.model.Parameter; import com.redhat.ceylon.model.typechecker.model.Type; import com.redhat.ceylon.model.typechecker.model.TypeDeclaration; import com.redhat.ceylon.model.typechecker.model.TypedDeclaration; import com.redhat.ceylon.model.typechecker.model.Unit; public class UnitDependencyVisitor extends Visitor { private final PhasedUnit phasedUnit; private Set<Declaration> alreadyDone; public UnitDependencyVisitor(PhasedUnit phasedUnit) { this.phasedUnit = phasedUnit; alreadyDone = new HashSet<Declaration>(); } private void storeDependency(Type type) { if (type!=null) { if (type.isClassOrInterface() || type.isTypeAlias()) { if (!createDependency(type.getDeclaration())) { return; } } Type et = type.getExtendedType(); storeDependency(et); List<Type> satisfiedTypes = type.getSatisfiedTypes(); for (Type st: satisfiedTypes) { storeDependency(st); } List<Type> caseTypes = type.getCaseTypes(); if (caseTypes!=null) { for (Type ct: caseTypes) { storeDependency(ct); } } } } private void storeDependency(Declaration dec) { if (dec instanceof TypeDeclaration) { storeDependency((TypeDeclaration) dec); } else if (dec instanceof TypedDeclaration) { storeDependency((TypedDeclaration) dec); } } private void storeDependency(TypedDeclaration dec) { storeDependency(dec.getType()); //TODO: parameters! Declaration rd = dec.getRefinedDeclaration(); if (rd!=dec && rd instanceof TypedDeclaration) { storeDependency((TypedDeclaration) rd); //this one is needed for default arguments, I think } createDependency(dec); } private void storeDependency(TypeDeclaration dec) { TypeDeclaration typeDeclaration = (TypeDeclaration) dec; storeDependency(typeDeclaration.getType()); Declaration rd = dec.getRefinedDeclaration(); if (rd!=dec && rd instanceof TypeDeclaration) { storeDependency((TypeDeclaration) rd); //this one is needed for default arguments, I think } createDependency(dec); } boolean createDependency(Declaration dec) { if (dec!=null && !alreadyDone.contains(dec)) { alreadyDone.add(dec); Unit declarationUnit = dec.getUnit(); if (declarationUnit != null && !(declarationUnit instanceof TypeFactory)) { String moduleName = declarationUnit.getPackage() .getModule() .getNameAsString(); if (!moduleName.equals(LANGUAGE_MODULE_NAME) && !JDKUtils.isJDKModule(moduleName) && !JDKUtils.isOracleJDKModule(moduleName)) { Unit currentUnit = phasedUnit.getUnit(); String currentUnitPath = phasedUnit.getUnitFile() .getPath(); addDependentsOf(declarationUnit, currentUnit, currentUnitPath); } } return true; } else { return false; } } protected static void addDependentsOf(Unit declarationUnit, Unit currentUnit, String currentUnitPath) { String currentUnitName = currentUnit.getFilename(); String dependedOnUnitName = declarationUnit.getFilename(); String currentUnitPackage = currentUnit.getPackage() .getNameAsString(); String dependedOnPackage = declarationUnit.getPackage() .getNameAsString(); if (!dependedOnUnitName.equals(currentUnitName) || !dependedOnPackage.equals(currentUnitPackage)) { // WOW : Ceylon Abstract Data types and swith case would be cool here ;) if (declarationUnit instanceof ProjectSourceFile) { declarationUnit.getDependentsOf() .add(currentUnitPath); } else if (declarationUnit instanceof ICrossProjectReference) { ICrossProjectReference crossProjectReference = (ICrossProjectReference) declarationUnit; IdeUnit originalProjectSourceFile = crossProjectReference.getOriginalSourceFile(); if (originalProjectSourceFile != null) { originalProjectSourceFile.getDependentsOf() .add(currentUnitPath); } } else if (declarationUnit instanceof ExternalSourceFile) { // Don't manage them : they cannot change ... Well they might if we were using these dependencies to manage module // removal. But since module removal triggers a classpath container update and so a full build, it's not necessary. // Might change in the future } else if (declarationUnit instanceof CeylonBinaryUnit) { declarationUnit.getDependentsOf() .add(currentUnitPath); } else if (declarationUnit instanceof JavaCompilationUnit) { //TODO: this does not seem to work for cross-project deps // We should introduce a CrossProjectJavaUnit that can return // the original JavaCompilationUnit from the original project declarationUnit.getDependentsOf() .add(currentUnitPath); } else if (declarationUnit instanceof JavaClassFile) { //TODO: All the dependencies to class files are also added... It is really useful ? // I assume in the case of the classes in the classes or exploded dirs, it might be, // but not sure it is also used not in the case of jar-located classes declarationUnit.getDependentsOf() .add(currentUnitPath); } else { assert(false); } } } @Override public void visit(Tree.MemberOrTypeExpression that) { storeDependency(that.getDeclaration()); super.visit(that); } @Override public void visit(Tree.NamedArgument that) { //TODO: is this really necessary? storeDependency(that.getParameter()); super.visit(that); } @Override public void visit(Tree.SequencedArgument that) { //TODO: is this really necessary? storeDependency(that.getParameter()); super.visit(that); } @Override public void visit(Tree.PositionalArgument that) { //TODO: is this really necessary? storeDependency(that.getParameter()); super.visit(that); } void storeDependency(Parameter parameter) { if (parameter!=null) { storeDependency(parameter.getModel()); } } @Override public void visit(Tree.Type that) { storeDependency(that.getTypeModel()); super.visit(that); } @Override public void visit(Tree.ImportMemberOrType that) { storeDependency(that.getDeclarationModel()); super.visit(that); } @Override public void visit(Tree.TypeArguments that) { //TODO: is this really necessary? List<Type> types = that.getTypeModels(); if (types!=null) { for (Type type: types) { storeDependency(type); } } super.visit(that); } @Override public void visit(Tree.Term that) { //TODO: is this really necessary? storeDependency(that.getTypeModel()); super.visit(that); } public void visit(Tree.Declaration that) { Declaration decl = that.getDeclarationModel(); if (decl.isNative()) { Declaration headerDeclaration = ModelUtil.getNativeHeader(decl); if (headerDeclaration != null) { List<Declaration> declarationsDependingOn = new ArrayList<>(); if (headerDeclaration != decl) { declarationsDependingOn.add(headerDeclaration); } List<Declaration> overloads = headerDeclaration.getOverloads(); if (overloads != null) { for (Declaration overload : overloads) { if (overload != decl) { declarationsDependingOn.add(overload); } } } for (Declaration dependingOn : declarationsDependingOn) { createDependency(dependingOn); Unit u = dependingOn.getUnit(); if (u instanceof JavaCompilationUnit) { ITypeRoot compilationUnit = ((JavaCompilationUnit<IProject,IFolder,IFile,ITypeRoot,IJavaElement>) u).getTypeRoot(); if (compilationUnit != null) { IResource javaFile; try { javaFile = compilationUnit.getCorrespondingResource(); if (javaFile != null) { String path = javaFile.getProjectRelativePath().toString(); addDependentsOf(decl.getUnit(), u, path); } } catch (JavaModelException e) { e.printStackTrace(); } } } } } } super.visit(that); } }