summaryrefslogtreecommitdiff
path: root/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java
diff options
context:
space:
mode:
Diffstat (limited to 'jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java')
-rw-r--r--jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java283
1 files changed, 173 insertions, 110 deletions
diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java
index a397d4b87a55..3e3a4793cbb6 100644
--- a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java
+++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java
@@ -85,7 +85,7 @@ public class Mappings {
*/
private IntIntMultiMaplet myClassToClassDependency;
private ObjectObjectMultiMaplet<File, ClassRepr> mySourceFileToClasses;
- private IntObjectMaplet<File> myClassToSourceFile;
+ private IntObjectMultiMaplet<File> myClassToSourceFile;
/**
* [short className] -> list of FQ names
*/
@@ -138,12 +138,17 @@ public class Mappings {
myRemovedSuperClasses = myIsDelta ? new IntIntTransientMultiMaplet() : null;
myAddedSuperClasses = myIsDelta ? new IntIntTransientMultiMaplet() : null;
+ final CollectionFactory<File> fileCollectionFactory = new CollectionFactory<File>() {
+ public Collection<File> create() {
+ return new THashSet<File>(FileUtil.FILE_HASHING_STRATEGY); // todo: do we really need set and not a list here?
+ }
+ };
if (myIsDelta && myDeltaIsTransient) {
myClassToSubclasses = new IntIntTransientMultiMaplet();
myClassToClassDependency = new IntIntTransientMultiMaplet();
myShortClassNameIndex = null;
mySourceFileToClasses = new ObjectObjectTransientMultiMaplet<File, ClassRepr>(FileUtil.FILE_HASHING_STRATEGY, ourClassSetConstructor);
- myClassToSourceFile = new IntObjectTransientMaplet<File>();
+ myClassToSourceFile = new IntObjectTransientMultiMaplet<File>(fileCollectionFactory);
}
else {
if (myIsDelta) {
@@ -156,7 +161,7 @@ public class Mappings {
DependencyContext.getTableFile(myRootDir, SOURCE_TO_CLASS), new FileKeyDescriptor(), ClassRepr.externalizer(myContext),
ourClassSetConstructor
);
- myClassToSourceFile = new IntObjectPersistentMaplet<File>(DependencyContext.getTableFile(myRootDir, CLASS_TO_SOURCE), new FileKeyDescriptor());
+ myClassToSourceFile = new IntObjectPersistentMultiMaplet<File>(DependencyContext.getTableFile(myRootDir, CLASS_TO_SOURCE), INT_KEY_DESCRIPTOR, new FileKeyDescriptor(), fileCollectionFactory);
}
}
@@ -190,17 +195,17 @@ public class Mappings {
}
@Nullable
- private ClassRepr getReprByName(@Nullable File source, final int name) {
- if (source == null) {
- source = myClassToSourceFile.get(name);
- }
- if (source != null) {
- final Collection<ClassRepr> reprs = mySourceFileToClasses.get(source);
-
- if (reprs != null) {
- for (ClassRepr repr : reprs) {
- if (repr.name == name) {
- return repr;
+ private ClassRepr getReprByName(final @Nullable File source, final int qName) {
+ final Collection<File> sources = source != null? Collections.singleton(source) : myClassToSourceFile.get(qName);
+ if (sources != null) {
+ for (File src : sources) {
+ final Collection<ClassRepr> reprs = mySourceFileToClasses.get(src);
+
+ if (reprs != null) {
+ for (ClassRepr repr : reprs) {
+ if (repr.name == qName) {
+ return repr;
+ }
}
}
}
@@ -556,14 +561,19 @@ public class Mappings {
void affectSubclasses(final int className, final Collection<File> affectedFiles, final Collection<UsageRepr.Usage> affectedUsages, final TIntHashSet dependants, final boolean usages, final Collection<File> alreadyCompiledFiles) {
debug("Affecting subclasses of class: ", className);
- final File fileName = myClassToSourceFile.get(className);
- if (fileName == null) {
+ final Collection<File> allSources = myClassToSourceFile.get(className);
+ if (allSources == null || allSources.isEmpty()) {
debug("No source file detected for class ", className);
debug("End of affectSubclasses");
return;
}
- debug("Source file name: ", fileName);
+ for (File fName : allSources) {
+ debug("Source file name: ", fName);
+ if (!alreadyCompiledFiles.contains(fName)) {
+ affectedFiles.add(fName);
+ }
+ }
if (usages) {
debug("Class usages affection requested");
@@ -579,9 +589,6 @@ public class Mappings {
if (depClasses != null) {
addAll(dependants, depClasses);
}
- if (!alreadyCompiledFiles.contains(fileName)) {
- affectedFiles.add(fileName);
- }
final TIntHashSet directSubclasses = myClassToSubclasses.get(className);
if (directSubclasses != null) {
@@ -701,10 +708,14 @@ public class Mappings {
dependants.forEach(new TIntProcedure() {
@Override
public boolean execute(int depClass) {
- final File depFile = myClassToSourceFile.get(depClass);
- if (depFile != null && !FileUtil.filesEqual(depFile, sourceFile)) {
- if (filter == null || filter.accept(depFile)) {
- affectedFiles.add(depFile);
+ final Collection<File> allSources = myClassToSourceFile.get(depClass);
+ if (allSources != null) {
+ for (File depFile : allSources) {
+ if (!FileUtil.filesEqual(depFile, sourceFile)) {
+ if (filter == null || filter.accept(depFile)) {
+ affectedFiles.add(depFile);
+ }
+ }
}
}
return true;
@@ -757,6 +768,8 @@ public class Mappings {
return false;
}
+ final THashSet<File> toRecompile = new THashSet<File>(FileUtil.FILE_HASHING_STRATEGY);
+
// Protected branch
if (member.isProtected()) {
debug("Protected access, softening non-incremental decision: adding all relevant subclasses for a recompilation");
@@ -766,10 +779,12 @@ public class Mappings {
propagated.forEach(new TIntProcedure() {
@Override
public boolean execute(int className) {
- final File fileName = myClassToSourceFile.get(className);
- if (fileName != null && !currentlyCompiled.contains(fileName)) {
- debug("Adding ", fileName);
- affectedFiles.add(fileName);
+ final Collection<File> fileNames = myClassToSourceFile.get(className);
+ if (fileNames != null) {
+ for (File fileName : fileNames) {
+ debug("Adding ", fileName);
+ }
+ toRecompile.addAll(fileNames);
}
return true;
}
@@ -782,19 +797,32 @@ public class Mappings {
debug("Package name: ", packageName);
// Package-local branch
- myClassToSourceFile.forEachEntry(new TIntObjectProcedure<File>() {
+ myClassToSourceFile.forEachEntry(new TIntObjectProcedure<Collection<File>>() {
@Override
- public boolean execute(int className, File fileName) {
+ public boolean execute(int className, Collection<File> fileNames) {
if (ClassRepr.getPackageName(myContext.getValue(className)).equals(packageName)) {
- if ((filter == null || filter.accept(fileName)) && !currentlyCompiled.contains(fileName)) {
- debug("Adding: ", fileName);
- affectedFiles.add(fileName);
+ for (File fileName : fileNames) {
+ if (filter == null || filter.accept(fileName)) {
+ debug("Adding: ", fileName);
+ toRecompile.add(fileName);
+ }
}
}
return true;
}
});
+ // filtering already compiled and non-existing paths
+ toRecompile.removeAll(currentlyCompiled);
+ for (Iterator<File> it = toRecompile.iterator(); it.hasNext(); ) {
+ final File file = it.next();
+ if (!file.exists()) {
+ it.remove();
+ }
+ }
+
+ affectedFiles.addAll(toRecompile);
+
return true;
}
@@ -1094,12 +1122,14 @@ public class Mappings {
if (overrides.satisfy(method) && isInheritor) {
debug("Current method overrides that found");
- final File file = myClassToSourceFile.get(methodClass.name);
-
- if (file != null) {
- myAffectedFiles.add(file);
- debug("Affecting file ", file);
+ final Collection<File> files = myClassToSourceFile.get(methodClass.name);
+ if (files != null) {
+ myAffectedFiles.addAll(files);
+ for (File file : files) {
+ debug("Affecting file ", file);
+ }
}
+
}
else {
debug("Current method does not override that found");
@@ -1128,12 +1158,15 @@ public class Mappings {
@Override
public boolean execute(int subClass) {
final ClassRepr r = myFuture.reprByName(subClass);
- if (r != null) {
- final File sourceFileName = myClassToSourceFile.get(subClass);
- if (sourceFileName != null && !myCompiledFiles.contains(sourceFileName)) {
- final int outerClass = r.getOuterClassName();
- if (!isEmpty(outerClass) && myFuture.isMethodVisible(outerClass, m)) {
- myAffectedFiles.add(sourceFileName);
+ if (r == null) {
+ return true;
+ }
+ final Collection<File> sourceFileNames = myClassToSourceFile.get(subClass);
+ if (sourceFileNames != null && !myCompiledFiles.containsAll(sourceFileNames)) {
+ final int outerClass = r.getOuterClassName();
+ if (!isEmpty(outerClass) && myFuture.isMethodVisible(outerClass, m)) {
+ myAffectedFiles.addAll(sourceFileNames);
+ for (File sourceFileName : sourceFileNames) {
debug("Affecting file due to local overriding: ", sourceFileName);
}
}
@@ -1186,10 +1219,12 @@ public class Mappings {
myFuture.addOverridingMethods(m, it, MethodRepr.equalByJavaRules(m), overridingMethods);
for (final Pair<MethodRepr, ClassRepr> p : overridingMethods) {
- final File fName = myClassToSourceFile.get(p.second.name);
- if (fName != null) {
- myAffectedFiles.add(fName);
- debug("Affecting file by overriding: ", fName);
+ final Collection<File> fNames = myClassToSourceFile.get(p.second.name);
+ if (fNames != null) {
+ myAffectedFiles.addAll(fNames);
+ for (File fName : fNames) {
+ debug("Affecting file by overriding: ", fName);
+ }
}
}
@@ -1229,12 +1264,14 @@ public class Mappings {
}
if (allAbstract && visited) {
- final File source = myClassToSourceFile.get(p);
+ final Collection<File> sources = myClassToSourceFile.get(p);
- if (source != null && !myCompiledFiles.contains(source)) {
- myAffectedFiles.add(source);
+ if (sources != null && !myCompiledFiles.containsAll(sources)) {
+ myAffectedFiles.addAll(sources);
debug("Removed method is not abstract & overrides some abstract method which is not then over-overridden in subclass ", p);
- debug("Affecting subclass source file ", source);
+ for (File source : sources) {
+ debug("Affecting subclass source file ", source);
+ }
}
}
}
@@ -1304,9 +1341,9 @@ public class Mappings {
final ClassRepr aClass = p.getSecond();
if (aClass != MOCK_CLASS) {
- final File fileName = myClassToSourceFile.get(aClass.name);
- if (fileName != null) {
- myAffectedFiles.add(fileName);
+ final Collection<File> fileNames = myClassToSourceFile.get(aClass.name);
+ if (fileNames != null) {
+ myAffectedFiles.addAll(fileNames);
}
}
}
@@ -1374,17 +1411,21 @@ public class Mappings {
public boolean execute(int subClass) {
final ClassRepr r = myFuture.reprByName(subClass);
if (r != null) {
- final File sourceFileName = myClassToSourceFile.get(subClass);
- if (sourceFileName != null && !myCompiledFiles.contains(sourceFileName)) {
+ final Collection<File> sourceFileNames = myClassToSourceFile.get(subClass);
+ if (sourceFileNames != null && !myCompiledFiles.containsAll(sourceFileNames)) {
if (r.isLocal()) {
- debug("Affecting local subclass (introduced field can potentially hide surrounding method parameters/local variables): ", sourceFileName);
- myAffectedFiles.add(sourceFileName);
+ for (File sourceFileName : sourceFileNames) {
+ debug("Affecting local subclass (introduced field can potentially hide surrounding method parameters/local variables): ", sourceFileName);
+ }
+ myAffectedFiles.addAll(sourceFileNames);
}
else {
final int outerClass = r.getOuterClassName();
if (!isEmpty(outerClass) && myFuture.isFieldVisible(outerClass, f)) {
- debug("Affecting inner subclass (introduced field can potentially hide surrounding class fields): ", sourceFileName);
- myAffectedFiles.add(sourceFileName);
+ for (File sourceFileName : sourceFileNames) {
+ debug("Affecting inner subclass (introduced field can potentially hide surrounding class fields): ", sourceFileName);
+ }
+ myAffectedFiles.addAll(sourceFileNames);
}
}
}
@@ -1783,7 +1824,9 @@ public class Mappings {
// checking if this newly added class duplicates already existing one
for (ClassRepr c : addedClasses) {
if (!c.isLocal() && !c.isAnonymous() && isEmpty(c.getOuterClassName())) {
- final File currentlyMappedTo = myClassToSourceFile.get(c.name);
+ final Collection<File> currentSources = myClassToSourceFile.get(c.name);
+ final File currentlyMappedTo = currentSources != null && currentSources.size() == 1? currentSources.iterator().next() : null;
+ // only check, if exactly one file is mapped
if (currentlyMappedTo != null && !FileUtil.filesEqual(currentlyMappedTo, srcFile) && currentlyMappedTo.exists() && myFilter.belongsToCurrentTargetChunk(currentlyMappedTo)) {
// Same classes from different source files.
// Schedule for recompilation both to make possible 'duplicate sources' error evident
@@ -1832,11 +1875,13 @@ public class Mappings {
toAffect.forEach(new TIntProcedure() {
@Override
public boolean execute(int depClass) {
- final File fName = myClassToSourceFile.get(depClass);
- if (fName != null) {
- if (myFilter == null || myFilter.accept(fName)) {
- debug("Adding dependent file ", fName);
- myAffectedFiles.add(fName);
+ final Collection<File> fNames = myClassToSourceFile.get(depClass);
+ if (fNames != null) {
+ for (File fName : fNames) {
+ if (myFilter == null || myFilter.accept(fName)) {
+ debug("Adding dependent file ", fName);
+ myAffectedFiles.add(fName);
+ }
}
}
return true;
@@ -1850,24 +1895,30 @@ public class Mappings {
state.myDependants.forEach(new TIntProcedure() {
@Override
public boolean execute(final int depClass) {
- final File depFile = myClassToSourceFile.get(depClass);
+ final Collection<File> depFiles = myClassToSourceFile.get(depClass);
+ if (depFiles != null) {
+ for (File depFile : depFiles) {
+ processDependentFile(depClass, depFile);
+ }
+ }
+ return true;
+ }
- if (depFile == null || myAffectedFiles.contains(depFile) || myCompiledFiles.contains(depFile)) {
- return true;
+ private void processDependentFile(int depClass, @NotNull File depFile) {
+ if (myAffectedFiles.contains(depFile) || myCompiledFiles.contains(depFile)) {
+ return;
}
debug("Dependent class: ", depClass);
final ClassRepr classRepr = getReprByName(depFile, depClass);
-
if (classRepr == null) {
- return true;
+ return;
}
final Set<UsageRepr.Usage> depUsages = classRepr.getUsages();
-
if (depUsages == null || depUsages.isEmpty()) {
- return true;
+ return;
}
for (UsageRepr.Usage usage : depUsages) {
@@ -1876,32 +1927,24 @@ public class Mappings {
if (query.satisfies(usage)) {
debug("Added file due to annotation query");
myAffectedFiles.add(depFile);
-
- return true;
+ return;
}
}
}
else if (state.myAffectedUsages.contains(usage)) {
final Util.UsageConstraint constraint = state.myUsageConstraints.get(usage);
-
if (constraint == null) {
debug("Added file with no constraints");
myAffectedFiles.add(depFile);
-
- return true;
+ return;
}
- else {
- if (constraint.checkResidence(depClass)) {
- debug("Added file with satisfied constraint");
- myAffectedFiles.add(depFile);
-
- return true;
- }
+ if (constraint.checkResidence(depClass)) {
+ debug("Added file with satisfied constraint");
+ myAffectedFiles.add(depFile);
+ return;
}
}
}
-
- return true;
}
});
}
@@ -2007,11 +2050,27 @@ public class Mappings {
private void cleanupRemovedClass(final Mappings delta, @NotNull final ClassRepr cr, File sourceFile, final Set<UsageRepr.Usage> usages, final IntIntMultiMaplet dependenciesTrashBin) {
final int className = cr.name;
- if (!FileUtil.filesEqual(sourceFile, myClassToSourceFile.get(className))) {
- // if classname is already mapped to a different source, the class with such FQ name exists elsewhere, so
- // we cannot destroy all these links
+
+ // it is safe to cleanup class information if it is mapped to non-existing files only
+ final Collection<File> currentlyMapped = myClassToSourceFile.get(className);
+ if (currentlyMapped == null || currentlyMapped.isEmpty()) {
return;
}
+ if (currentlyMapped.size() == 1) {
+ if (!FileUtil.filesEqual(sourceFile, currentlyMapped.iterator().next())) {
+ // if classname is already mapped to a different source, the class with such FQ name exists elsewhere, so
+ // we cannot destroy all these links
+ return;
+ }
+ }
+ else {
+ // many files
+ for (File file : currentlyMapped) {
+ if (!FileUtil.filesEqual(sourceFile, file) && file.exists()) {
+ return;
+ }
+ }
+ }
for (final int superSomething : cr.getSupers()) {
delta.registerRemovedSuperClass(className, superSomething);
@@ -2113,13 +2172,8 @@ public class Mappings {
delta.getChangedClasses().forEach(new TIntProcedure() {
@Override
public boolean execute(final int className) {
- final File sourceFile = delta.myClassToSourceFile.get(className);
- if (sourceFile != null) {
- myClassToSourceFile.put(className, sourceFile);
- }
- else {
- myClassToSourceFile.remove(className);
- }
+ final Collection<File> sourceFiles = delta.myClassToSourceFile.get(className);
+ myClassToSourceFile.replace(className, sourceFiles);
cleanupBackDependency(className, null, dependenciesTrashBin);
@@ -2138,7 +2192,7 @@ public class Mappings {
}
else {
myClassToSubclasses.putAll(delta.myClassToSubclasses);
- myClassToSourceFile.putAll(delta.myClassToSourceFile);
+ myClassToSourceFile.replaceAll(delta.myClassToSourceFile);
mySourceFileToClasses.replaceAll(delta.mySourceFileToClasses);
delta.mySourceFileToClasses.forEachEntry(new TObjectObjectProcedure<File, Collection<ClassRepr>>() {
public boolean execute(File src, Collection<ClassRepr> classes) {
@@ -2203,18 +2257,21 @@ public class Mappings {
public Callbacks.Backend getCallback() {
return new Callbacks.Backend() {
- public void associate(final String classFileName, final String sourceFileName, final ClassReader cr) {
+
+ public void associate(String classFileName, Collection<String> sources, ClassReader cr) {
synchronized (myLock) {
final int classFileNameS = myContext.get(classFileName);
final Pair<ClassRepr, Set<UsageRepr.Usage>> result = new ClassfileAnalyzer(myContext).analyze(classFileNameS, cr);
final ClassRepr repr = result.first;
if (repr != null) {
final Set<UsageRepr.Usage> localUsages = result.second;
- final File sourceFile = new File(sourceFileName);
final int className = repr.name;
- myClassToSourceFile.put(className, sourceFile);
- mySourceFileToClasses.put(sourceFile, repr);
+ for (String sourceFileName : sources) {
+ final File sourceFile = new File(sourceFileName);
+ myClassToSourceFile.put(className, sourceFile);
+ mySourceFileToClasses.put(sourceFile, repr);
+ }
for (final int s : repr.getSupers()) {
myClassToSubclasses.put(s, className);
@@ -2231,6 +2288,10 @@ public class Mappings {
}
}
+ public void associate(final String classFileName, final String sourceFileName, final ClassReader cr) {
+ associate(classFileName, Collections.singleton(sourceFileName), cr);
+ }
+
@Override
public void registerImports(final String className, final Collection<String> imports, Collection<String> staticImports) {
final List<String> allImports = new ArrayList<String>();
@@ -2252,14 +2313,16 @@ public class Mappings {
myPostPasses.offer(new Runnable() {
public void run() {
final int rootClassName = myContext.get(className.replace(".", "/"));
- final File fileName = myClassToSourceFile.get(rootClassName);
- final ClassRepr repr = fileName != null? getReprByName(fileName, rootClassName) : null;
+ final Collection<File> fileNames = myClassToSourceFile.get(rootClassName);
+ final ClassRepr repr = fileNames != null && !fileNames.isEmpty()? getReprByName(fileNames.iterator().next(), rootClassName) : null;
for (final String i : allImports) {
- final int iname = myContext.get(i.replace(".", "/"));
+ final int iname = myContext.get(i.replace('.', '/'));
myClassToClassDependency.put(iname, rootClassName);
if (repr != null && repr.addUsage(UsageRepr.createClassUsage(myContext, iname))) {
- mySourceFileToClasses.put(fileName, repr);
+ for (File fileName : fileNames) {
+ mySourceFileToClasses.put(fileName, repr);
+ }
}
}
}
@@ -2386,10 +2449,10 @@ public class Mappings {
assert (myChangedClasses != null && myChangedFiles != null);
myChangedClasses.add(it);
- final File file = myClassToSourceFile.get(it);
+ final Collection<File> files = myClassToSourceFile.get(it);
- if (file != null) {
- myChangedFiles.add(file);
+ if (files != null) {
+ myChangedFiles.addAll(files);
}
}