From 83aad1ae28b4be04523cde3b4843a9e7bd389572 Mon Sep 17 00:00:00 2001 From: Dmitry Boulytchev Date: Tue, 18 Oct 2011 21:44:06 +0400 Subject: [PATCH] Privatized removed content compensation; bruched up interface methods signatures (jps/Mappings). --- .../org/jetbrains/ether/ProjectWrapper.java | 2697 +++++++++-------- .../ether/dependencyView/Mappings.java | 35 +- 2 files changed, 1393 insertions(+), 1339 deletions(-) diff --git a/jps/src/org/jetbrains/ether/ProjectWrapper.java b/jps/src/org/jetbrains/ether/ProjectWrapper.java index 0aa31dabe3fd..9a69d7f12c09 100644 --- a/jps/src/org/jetbrains/ether/ProjectWrapper.java +++ b/jps/src/org/jetbrains/ether/ProjectWrapper.java @@ -20,1451 +20,1472 @@ import java.util.*; */ public class ProjectWrapper { - public interface Flags { - boolean tests(); + public interface Flags { + boolean tests(); - boolean incremental(); + boolean incremental(); - boolean force(); + boolean force(); - PrintStream logStream(); + PrintStream logStream(); + } + + private final static Flags defaultFlags = new Flags() { + public boolean tests() { + return true; } - private final static Flags defaultFlags = new Flags() { - public boolean tests() { - return true; - } - - public boolean incremental() { - return false; - } - - public boolean force() { - return true; - } - - public PrintStream logStream() { - return null; - } - }; - - private abstract class Logger { - private final PrintStream stream; - - public Logger(final Flags flags) { - this.stream = flags.logStream(); - } - - public void logFilePaths(PrintStream stream, Collection paths) { - List strings = new ArrayList(paths.size()); - for (StringCache.S path : paths) { - strings.add(FileUtil.toSystemIndependentName(path.toString())); - } - logMany(stream, strings); - } - - public void logMany(final PrintStream stream, final Collection list) { - final String[] a = new String[list.size()]; - int i = 0; - - for (T e : list) { - a[i++] = e.toString(); - } - - Arrays.sort(a); - - for (String o : a) { - stream.println(o); - } - } - - public abstract void log(final PrintStream stream); - - public void log() { - if (stream != null) - log(stream); - } + public boolean incremental() { + return false; } - // Home directory - private static final String myHomeDir = System.getProperty("user.home"); - - // JPS directory - private static final String myJPSDir = ".jps"; - - // IDEA project structure directory name - private static final String myIDEADir = ".idea"; - - // JPS directory initialization - private static void initJPSDirectory() { - final File f = new File(myHomeDir + File.separator + myJPSDir); - - if (!f.exists()) { - if (!f.mkdir()) { - throw new RuntimeException("unable to create JPS snapshot directory " + f.getPath()); - } - } + public boolean force() { + return true; } - // File separator replacement - private static final char myFileSeparatorReplacement = '.'; + public PrintStream logStream() { + return null; + } + }; - // Original JPS Project - private final GantBasedProject myProject; - private final ProjectBuilder myProjectBuilder; + private abstract class Logger { + private final PrintStream stream; - // Project directory - private final String myRoot; - - // Project snapshot file name - private final String myProjectSnapshot; - - public interface ClasspathItemWrapper extends RW.Writable { - public List getClassPath(ClasspathKind kind); + public Logger(final Flags flags) { + this.stream = flags.logStream(); } - private final RW.Reader myLibraryWrapperReader = - new RW.Reader() { - public LibraryWrapper read(final BufferedReader r) { - return new LibraryWrapper(r); - } - }; - - public class LibraryWrapper implements ClasspathItemWrapper { - final String myName; - final List myClassPath; - - public void write(final BufferedWriter w) { - RW.writeln(w, "Library:" + myName); - RW.writeln(w, "Classpath:"); - RW.writeln(w, myClassPath, RW.fromString); - } - - public LibraryWrapper(final BufferedReader r) { - myName = RW.readStringAttribute(r, "Library:"); - - RW.readTag(r, "Classpath:"); - myClassPath = (List) RW.readMany(r, RW.myStringReader, new ArrayList()); - } - - public LibraryWrapper(final Library lib) { - lib.forceInit(); - myName = lib.getName(); - myClassPath = (List) getRelativePaths(lib.getClasspath(), new ArrayList()); - } - - public String getName() { - return myName; - } - - public List getClassPath(final ClasspathKind kind) { - return myClassPath; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - LibraryWrapper that = (LibraryWrapper) o; - - if (myName != null ? !myName.equals(that.myName) : that.myName != null) return false; - - return true; - } - - @Override - public int hashCode() { - return myName != null ? myName.hashCode() : 0; - } + public void logFilePaths(PrintStream stream, Collection paths) { + List strings = new ArrayList(paths.size()); + for (StringCache.S path : paths) { + strings.add(FileUtil.toSystemIndependentName(path.toString())); + } + logMany(stream, strings); } - private final RW.Reader myWeakClasspathItemWrapperReader = - new RW.Reader() { - public ClasspathItemWrapper read(final BufferedReader r) { - final String s = RW.lookString(r); - if (s.startsWith("Library:")) { - return new WeakClasspathItemWrapper(RW.readStringAttribute(r, "Library:"), "Library"); - } - if (s.startsWith("Module:")) { - return new WeakClasspathItemWrapper(RW.readStringAttribute(r, "Module:"), "Module"); - } else { - return new GenericClasspathItemWrapper(r); - } - } - }; + public void logMany(final PrintStream stream, final Collection list) { + final String[] a = new String[list.size()]; + int i = 0; - public class WeakClasspathItemWrapper implements ClasspathItemWrapper { - final String myName; - final String myType; + for (T e : list) { + a[i++] = e.toString(); + } - public WeakClasspathItemWrapper(final String name, final String type) { - myName = name; - myType = type; - } + Arrays.sort(a); - public WeakClasspathItemWrapper(final ModuleWrapper m) { - myType = "Module"; - myName = m.getName(); - } - - public WeakClasspathItemWrapper(final LibraryWrapper l) { - myType = "Library"; - myName = l.getName(); - } - - public boolean isModule() { - return myType.equals("Module"); - } - - public String getName() { - return myName; - } - - public List getClassPath(ClasspathKind kind) { - return null; - } - - public void write(final BufferedWriter w) { - RW.writeln(w, myType + ":" + getName()); - } + for (String o : a) { + stream.println(o); + } } - public class GenericClasspathItemWrapper implements ClasspathItemWrapper { - final List myClassPath; - final String myType; + public abstract void log(final PrintStream stream); - public GenericClasspathItemWrapper(final ClasspathItem item) { - if (item instanceof PathEntry) - myType = "PathEntry"; - else if (item instanceof JavaSdk) - myType = "JavaSdk"; - else if (item instanceof Sdk) - myType = "Sdk"; - else - myType = null; + public void log() { + if (stream != null) log(stream); + } + } - myClassPath = (List) getRelativePaths(item.getClasspathRoots(null), new ArrayList()); - } + // Home directory + private static final String myHomeDir = System.getProperty("user.home"); - public GenericClasspathItemWrapper(final BufferedReader r) { - myType = RW.readString(r); + // JPS directory + private static final String myJPSDir = ".jps"; - RW.readTag(r, "Classpath:"); - myClassPath = (List) RW.readMany(r, RW.myStringReader, new ArrayList()); - } + // IDEA project structure directory name + private static final String myIDEADir = ".idea"; - public String getType() { - return myType; - } + // JPS directory initialization + private static void initJPSDirectory() { + final File f = new File(myHomeDir + File.separator + myJPSDir); - public List getClassPath(final ClasspathKind kind) { - return myClassPath; - } + if (!f.exists()) { + if (!f.mkdir()) { + throw new RuntimeException("unable to create JPS snapshot directory " + f.getPath()); + } + } + } - public void write(final BufferedWriter w) { - RW.writeln(w, myType); - RW.writeln(w, "Classpath:"); - RW.writeln(w, myClassPath, RW.fromString); - } + // File separator replacement + private static final char myFileSeparatorReplacement = '.'; + + // Original JPS Project + private final GantBasedProject myProject; + private final ProjectBuilder myProjectBuilder; + + // Project directory + private final String myRoot; + + // Project snapshot file name + private final String myProjectSnapshot; + + public interface ClasspathItemWrapper extends RW.Writable { + public List getClassPath(ClasspathKind kind); + } + + private final RW.Reader myLibraryWrapperReader = new RW.Reader() { + public LibraryWrapper read(final BufferedReader r) { + return new LibraryWrapper(r); + } + }; + + public class LibraryWrapper implements ClasspathItemWrapper { + final String myName; + final List myClassPath; + + public void write(final BufferedWriter w) { + RW.writeln(w, "Library:" + myName); + RW.writeln(w, "Classpath:"); + RW.writeln(w, myClassPath, RW.fromString); } - private final RW.Reader myFileWrapperReader = - new RW.Reader() { - public FileWrapper read(final BufferedReader r) { - return new FileWrapper(r); - } - }; + public LibraryWrapper(final BufferedReader r) { + myName = RW.readStringAttribute(r, "Library:"); - public class FileWrapper implements RW.Writable { - final StringCache.S myName; - final long myModificationTime; - - FileWrapper(final File f) { - myName = StringCache.get(getRelativePath(f.getAbsolutePath())); - myModificationTime = f.lastModified(); - } - - FileWrapper(final BufferedReader r) { - myName = StringCache.get(RW.readString(r)); - myModificationTime = RW.readLong(r); - - final Set classes = (Set) RW.readMany(r, ClassRepr.reader, new HashSet()); - final Set>> classesWithSubclasses = new HashSet>>(); - - for (ClassRepr c : classes) { - final Set subClasses = (Set) RW.readMany(r, StringCache.reader, new HashSet()); - classesWithSubclasses.add(new Pair>(c, subClasses)); - } - - final UsageRepr.Cluster usages = new UsageRepr.Cluster(r); - final Set annotationUsages = (Set) RW.readMany(r, UsageRepr.reader, new HashSet()); - final Set formClasses = (Set) RW.readMany(r, StringCache.S.reader, new HashSet()); - - backendCallback.associate(classesWithSubclasses, new Pair>(usages, annotationUsages), myName.value); - - for (StringCache.S classFileName : formClasses) { - backendCallback.associateForm(myName, classFileName); - } - } - - public StringCache.S getName() { - return myName; - } - - public long getStamp() { - return myModificationTime; - } - - public void write(final BufferedWriter w) { - final StringCache.S name = getName(); - - RW.writeln(w, name.value); - RW.writeln(w, Long.toString(getStamp())); - - final Set classes = dependencyMapping.getClasses(name); - - RW.writeln(w, classes); - - if (classes != null) { - for (ClassRepr c : classes) { - RW.writeln(w, dependencyMapping.getSubClasses(c.name)); - } - } - - dependencyMapping.getUsages(name).write(w); - RW.writeln(w, dependencyMapping.getAnnotationUsages(name)); - RW.writeln(w, dependencyMapping.getFormClass(name)); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - FileWrapper that = (FileWrapper) o; - - if (myName != null ? !myName.equals(that.myName) : that.myName != null) return false; - - return true; - } - - @Override - public int hashCode() { - return myName != null ? myName.hashCode() : 0; - } + RW.readTag(r, "Classpath:"); + myClassPath = (List)RW.readMany(r, RW.myStringReader, new ArrayList()); } - private final RW.Reader myModuleWrapperReader = - new RW.Reader() { - public ModuleWrapper read(final BufferedReader r) { - return new ModuleWrapper(r); - } - }; - - public class ModuleWrapper implements ClasspathItemWrapper { - - private class Properties implements RW.Writable { - final Set myRoots; - final Map mySources; - - final String myOutput; - String myOutputStatus; - - public Set getFiles() { - final Set result = new HashSet(); - - for (FileWrapper f : mySources.keySet()) { - result.add(f.getName()); - } - - return result; - } - - public Set getOutdatedFiles(final Properties past) { - final Set result = new HashSet(); - - for (FileWrapper now : mySources.keySet()) { - final FileWrapper than = past == null ? null : past.mySources.get(now); - - if (than == null || than.getStamp() < now.getStamp() || affectedFiles.contains(now.getName())) { - result.add(now.getName()); - } - } - - return result; - } - - public Set getRemovedFiles(final Properties past) { - final Set result = new HashSet(); - - if (past != null) { - for (FileWrapper was : past.mySources.keySet()) { - final FileWrapper now = mySources.get(was); - - if (now == null) { - result.add(was.getName()); - } - } - } - - return result; - } - - public void write(final BufferedWriter w) { - RW.writeln(w, "Roots:"); - RW.writeln(w, myRoots, RW.fromString); - - RW.writeln(w, "Sources:"); - RW.writeln(w, mySources.keySet()); - - RW.writeln(w, "Output:"); - RW.writeln(w, myOutput == null ? "" : myOutput); - - RW.writeln(w, "OutputStatus:" + myOutputStatus); - } - - public Properties(final BufferedReader r) { - RW.readTag(r, "Roots:"); - - myRoots = (Set) RW.readMany(r, RW.myStringReader, new HashSet()); - - RW.readTag(r, "Sources:"); - - mySources = new HashMap(); - - for (FileWrapper fw : (Set) RW.readMany(r, myFileWrapperReader, new HashSet())) { - mySources.put(fw, fw); - } - - RW.readTag(r, "Output:"); - final String s = RW.readString(r); - myOutput = s.equals("") ? null : s; - - myOutputStatus = RW.readStringAttribute(r, "OutputStatus:"); - } - - public Properties(final List sources, final String output, final Set excludes) { - myRoots = (Set) getRelativePaths(sources, new HashSet()); - final DirectoryScanner.Result result = DirectoryScanner.getFiles(myRoots, excludes, ProjectWrapper.this); - - mySources = new HashMap(); - - for (FileWrapper fw : result.getFiles()) { - mySources.put(fw, fw); - } - - myOutput = getRelativePath(output); - updateOutputStatus(); - } - - public void updateOutputStatus() { - final String path = getAbsolutePath(myOutput); - final File ok = new File(path + File.separator + Reporter.myOkFlag); - final File fail = new File(path + File.separator + Reporter.myFailFlag); - - if (ok.exists()) { - myOutputStatus = "ok"; - return; - } - - if (fail.exists()) { - myOutputStatus = "fail"; - return; - } - - myOutputStatus = "empty"; - } - - public Set getRoots() { - return myRoots; - } - - public Set getSources() { - return mySources.keySet(); - } - - public String getOutputPath() { - return myOutput; - } - - public boolean emptySource() { - return mySources.isEmpty(); - } - - public boolean outputOk() { - return myOutputStatus.equals("ok"); - } - - public boolean outputEmpty() { - return myOutputStatus.equals("empty"); - } - - public boolean isOutdated() { - return (!emptySource() && !outputOk()); - } - } - - final String myName; - final Properties mySource; - final Properties myTest; - - List myDependsOn; - List myTestDependsOn; - - final Set myExcludes; - final Module myModule; - final Set myLibraries; - - public Set getOutdatedSources() { - return mySource.getOutdatedFiles(myHistory == null ? null : myHistory.getModule(myName).mySource); - } - - public Set getOutdatedTests() { - return myTest.getOutdatedFiles(myHistory == null ? null : myHistory.getModule(myName).myTest); - } - - public Set getRemovedSources() { - return mySource.getRemovedFiles(myHistory == null ? null : myHistory.getModule(myName).mySource); - } - - public Set getRemovedTests() { - return myTest.getRemovedFiles(myHistory == null ? null : myHistory.getModule(myName).myTest); - } - - public void updateOutputStatus() { - mySource.updateOutputStatus(); - myTest.updateOutputStatus(); - } - - private ClasspathItemWrapper weaken(final ClasspathItemWrapper x) { - if (x instanceof ModuleWrapper) { - return new WeakClasspathItemWrapper((ModuleWrapper) x); - } else if (x instanceof LibraryWrapper) { - return new WeakClasspathItemWrapper((LibraryWrapper) x); - } else - return x; - } - - public void write(final BufferedWriter w) { - RW.writeln(w, "Module:" + myName); - - RW.writeln(w, "SourceProperties:"); - mySource.write(w); - - RW.writeln(w, "TestProperties:"); - myTest.write(w); - - RW.writeln(w, "Excludes:"); - RW.writeln(w, myExcludes, RW.fromString); - - RW.writeln(w, "Libraries:"); - RW.writeln(w, myLibraries); - - RW.writeln(w, "Dependencies:"); - - final List weakened = new ArrayList(); - - for (ClasspathItemWrapper cpiw : dependsOn(false)) { - weakened.add(weaken(cpiw)); - } - - RW.writeln(w, weakened); - - weakened.clear(); - - for (ClasspathItemWrapper cpiw : dependsOn(true)) { - weakened.add(weaken(cpiw)); - } - - RW.writeln(w, weakened); - } - - public ModuleWrapper(final BufferedReader r) { - myModule = null; - myName = RW.readStringAttribute(r, "Module:"); - - RW.readTag(r, "SourceProperties:"); - mySource = new Properties(r); - - RW.readTag(r, "TestProperties:"); - myTest = new Properties(r); - - RW.readTag(r, "Excludes:"); - myExcludes = (Set) RW.readMany(r, RW.myStringReader, new HashSet()); - - RW.readTag(r, "Libraries:"); - myLibraries = (Set) RW.readMany(r, myLibraryWrapperReader, new HashSet()); - - RW.readTag(r, "Dependencies:"); - myDependsOn = (List) RW.readMany(r, myWeakClasspathItemWrapperReader, new ArrayList()); - myTestDependsOn = (List) RW.readMany(r, myWeakClasspathItemWrapperReader, new ArrayList()); - } - - public ModuleWrapper(final Module m) { - m.forceInit(); - myModule = m; - myDependsOn = null; - myTestDependsOn = null; - myName = m.getName(); - myExcludes = (Set) getRelativePaths(m.getExcludes(), new HashSet()); - mySource = new Properties(m.getSourceRoots(), m.getOutputPath(), myExcludes); - myTest = new Properties(m.getTestRoots(), m.getTestOutputPath(), myExcludes); - - myLibraries = new HashSet(); - - for (Library lib : m.getLibraries().values()) { - myLibraries.add(new LibraryWrapper(lib)); - } - } - - public String getName() { - return myName; - } - - public Set getOutdatedFiles(final boolean tests) { - if (tests) { - return myTest.outputEmpty() ? getTests() : getOutdatedTests(); - } - - return mySource.outputEmpty() ? getSources() : getOutdatedSources(); - } - - public Set getRemovedFiles(final boolean tests) { - if (tests) { - return getRemovedTests(); - } - - return getRemovedSources(); - } - - public Set getSources(final boolean tests) { - if (tests) { - return myTest.getFiles(); - } - - return mySource.getFiles(); - } - - public Set getSourceRoots() { - return mySource.getRoots(); - } - - public Set getSourceFiles() { - return mySource.getSources(); - } - - public Set getTestFiles() { - return myTest.getSources(); - } - - public Set getSources() { - return mySource.getFiles(); - } - - public Set getTests() { - return myTest.getFiles(); - } - - public String getOutputPath() { - return mySource.getOutputPath(); - } - - public Set getTestSourceRoots() { - return myTest.getRoots(); - } - - public Set getTestSourceFiles() { - return myTest.getSources(); - } - - public String getTestOutputPath() { - return myTest.getOutputPath(); - } - - public List dependsOn(final boolean tests) { - if (tests) { - if (myTestDependsOn != null) { - return myTestDependsOn; - } - } else if (myDependsOn != null) { - return myDependsOn; - } - - final List result = new ArrayList(); - - for (ClasspathItem cpi : myModule.getClasspath(ClasspathKind.compile(tests))) { - if (cpi instanceof Module) { - result.add(getModule(((Module) cpi).getName())); - } else if (cpi instanceof Library) { - result.add(new LibraryWrapper((Library) cpi)); - } else { - result.add(new GenericClasspathItemWrapper(cpi)); - } - } - - if (tests) { - myTestDependsOn = result; - } else { - myDependsOn = result; - } - - return result; - } - - public List getClassPath(final ClasspathKind kind) { - final List result = new ArrayList(); - - result.add(getOutputPath()); - - if (kind.isTestsIncluded()) { - result.add(getTestOutputPath()); - } - - return result; - } - - private boolean safeEquals(final String a, final String b) { - if (a == null || b == null) - return a == b; - - return a.equals(b); - } - - private boolean safeEquals(final ClasspathItemWrapper a, final ClasspathItemWrapper b) { - try { - final StringWriter as = new StringWriter(); - final StringWriter bs = new StringWriter(); - - final BufferedWriter bas = new BufferedWriter(as); - final BufferedWriter bbs = new BufferedWriter(bs); - - weaken(a).write(bas); - weaken(b).write(bbs); - - bas.flush(); - bbs.flush(); - - as.close(); - bs.close(); - - final String x = as.getBuffer().toString(); - final String y = bs.getBuffer().toString(); - - return x.equals(y); - } catch (IOException e) { - e.printStackTrace(); - return false; - } - } - - public boolean isOutdated(final boolean tests, final ProjectWrapper history) { - if (history == null) { - return true; - } - - final ModuleWrapper past = history.getModule(myName); - - if (past == null) { - return true; - } - - final boolean outputChanged = !safeEquals(past.getOutputPath(), getOutputPath()); - final boolean testOutputChanged = tests && !safeEquals(past.getTestOutputPath(), getTestOutputPath()); - final boolean sourceChanged = !past.getSourceFiles().equals(getSourceFiles()); - final boolean testSourceChanged = tests && !past.getTestSourceFiles().equals(getTestSourceFiles()); - final boolean sourceOutdated = mySource.isOutdated() || !mySource.getOutdatedFiles(past.mySource).isEmpty(); - final boolean testSourceOutdated = tests && (myTest.isOutdated() || !myTest.getOutdatedFiles(past.myTest).isEmpty()); - final boolean unsafeDependencyChange = ( - new Object() { - public boolean run(final List today, final List yesterday) { - final Iterator t = today.iterator(); - final Iterator y = yesterday.iterator(); - - while (true) { - if (!y.hasNext()) - return false; - - if (!t.hasNext()) - return true; - - if (!safeEquals(t.next(), y.next())) - return true; - } - } - }.run(dependsOn(tests), past.dependsOn(tests)) - ); - - return sourceOutdated || - testSourceOutdated || - sourceChanged || - testSourceChanged || - outputChanged || - testOutputChanged || - unsafeDependencyChange; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - ModuleWrapper that = (ModuleWrapper) o; - - if (myName != null ? !myName.equals(that.myName) : that.myName != null) return false; - - return true; - } - - @Override - public int hashCode() { - return myName != null ? myName.hashCode() : 0; - } + public LibraryWrapper(final Library lib) { + lib.forceInit(); + myName = lib.getName(); + myClassPath = (List)getRelativePaths(lib.getClasspath(), new ArrayList()); } - final Map myModules = new HashMap(); - final Map myLibraries = new HashMap(); - - public ModuleWrapper getModule(final String name) { - return myModules.get(name); + public String getName() { + return myName; } - public LibraryWrapper getLibrary(final String name) { - return myLibraries.get(name); + public List getClassPath(final ClasspathKind kind) { + return myClassPath; } - public Collection getLibraries() { - return myLibraries.values(); + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + LibraryWrapper that = (LibraryWrapper)o; + + if (myName != null ? !myName.equals(that.myName) : that.myName != null) return false; + + return true; } - public Collection getModules() { - return myModules.values(); + @Override + public int hashCode() { + return myName != null ? myName.hashCode() : 0; + } + } + + private final RW.Reader myWeakClasspathItemWrapperReader = new RW.Reader() { + public ClasspathItemWrapper read(final BufferedReader r) { + final String s = RW.lookString(r); + if (s.startsWith("Library:")) { + return new WeakClasspathItemWrapper(RW.readStringAttribute(r, "Library:"), "Library"); + } + if (s.startsWith("Module:")) { + return new WeakClasspathItemWrapper(RW.readStringAttribute(r, "Module:"), "Module"); + } + else { + return new GenericClasspathItemWrapper(r); + } + } + }; + + public class WeakClasspathItemWrapper implements ClasspathItemWrapper { + final String myName; + final String myType; + + public WeakClasspathItemWrapper(final String name, final String type) { + myName = name; + myType = type; } - final Mappings dependencyMapping; - final Callbacks.Backend backendCallback; - final Set affectedFiles; - - final ProjectWrapper myHistory; - - private static String getCanonicalPath(final String path) { - try { - return new File(path).getCanonicalPath(); - } catch (IOException e) { - e.printStackTrace(); - return null; - } + public WeakClasspathItemWrapper(final ModuleWrapper m) { + myType = "Module"; + myName = m.getName(); } - private ProjectWrapper(final GantBinding binding, final String prjDir, final String setupScript, final Map pathVariables, final boolean loadHistory) { - dependencyMapping = new Mappings(this); - backendCallback = dependencyMapping.getCallback(); - affectedFiles = new HashSet(); - - myProject = new GantBasedProject(binding == null ? new GantBinding() : binding); - myProjectBuilder = myProject.getBuilder(); - - final File prjFile = new File(prjDir); - final boolean dirBased = !(prjFile.isFile() && prjDir.endsWith(".ipr")); - - myRoot = dirBased ? getCanonicalPath(prjDir) : getCanonicalPath(prjFile.getParent()); - - final String loadPath = dirBased ? getAbsolutePath(myIDEADir) : prjDir; - - myProjectSnapshot = myHomeDir + File.separator + myJPSDir + File.separator + myRoot.replace(File.separatorChar, myFileSeparatorReplacement); - - IdeaProjectLoader.loadFromPath(myProject, loadPath, pathVariables != null ? pathVariables : Collections.emptyMap(), setupScript); - - for (Module m : myProject.getModules().values()) { - myModules.put(m.getName(), new ModuleWrapper(m)); - } - - for (Library l : myProject.getLibraries().values()) { - myLibraries.put(l.getName(), new LibraryWrapper(l)); - } - - myHistory = loadHistory ? loadSnapshot(dependencyMapping, affectedFiles) : null; + public WeakClasspathItemWrapper(final LibraryWrapper l) { + myType = "Library"; + myName = l.getName(); } - private ProjectWrapper(final BufferedReader r, final Mappings mappings, final Set affected) { - dependencyMapping = mappings; - affectedFiles = affected; - backendCallback = dependencyMapping.getCallback(); - myProject = null; - myProjectBuilder = null; - myHistory = null; - - myRoot = RW.readStringAttribute(r, "Root:"); - myProjectSnapshot = myHomeDir + File.separator + myJPSDir + File.separator + myRoot.replace(File.separatorChar, myFileSeparatorReplacement); - - RW.readTag(r, "Libraries:"); - final Set libs = (Set) RW.readMany(r, myLibraryWrapperReader, new HashSet()); - - for (LibraryWrapper l : libs) { - myLibraries.put(l.getName(), l); - } - - RW.readTag(r, "Modules:"); - final Set mods = (Set) RW.readMany(r, myModuleWrapperReader, new HashSet()); - - for (ModuleWrapper m : mods) { - myModules.put(m.getName(), m); - } - - RW.readMany(r, StringCache.reader, affectedFiles); + public boolean isModule() { + return myType.equals("Module"); } - public String getAbsolutePath(final String relative) { - if (relative == null) - return relative; - - if (new File(relative).isAbsolute()) - return relative; - - return myRoot + File.separator + relative; + public String getName() { + return myName; } - public String getRelativePath(final String absolute) { - if (absolute == null) - return absolute; - - if (absolute.startsWith(myRoot)) { - return absolute.substring(myRoot.length() + 1); - } - - return absolute; - } - - public Collection getAbsolutePaths(final Collection paths, final Collection result) { - for (String path : paths) { - if (path != null) - result.add(getAbsolutePath(path)); - } - - return result; - } - - public Collection getRelativePaths(final Collection paths, final Collection result) { - for (String path : paths) { - if (path != null) - result.add(getRelativePath(path)); - } - - return result; - } - - private boolean isHistory() { - return myProject == null; + public List getClassPath(ClasspathKind kind) { + return null; } public void write(final BufferedWriter w) { - RW.writeln(w, "Root:" + myRoot); + RW.writeln(w, myType + ":" + getName()); + } + } - RW.writeln(w, "Libraries:"); - RW.writeln(w, getLibraries()); + public class GenericClasspathItemWrapper implements ClasspathItemWrapper { + final List myClassPath; + final String myType; - RW.writeln(w, "Modules:"); - RW.writeln(w, getModules()); + public GenericClasspathItemWrapper(final ClasspathItem item) { + if (item instanceof PathEntry) { + myType = "PathEntry"; + } + else if (item instanceof JavaSdk) { + myType = "JavaSdk"; + } + else if (item instanceof Sdk) { + myType = "Sdk"; + } + else { + myType = null; + } - RW.writeln(w, affectedFiles, StringCache.fromS); + myClassPath = (List)getRelativePaths(item.getClasspathRoots(null), new ArrayList()); } - private String getProjectSnapshotFileName() { - return myProjectSnapshot; + public GenericClasspathItemWrapper(final BufferedReader r) { + myType = RW.readString(r); + + RW.readTag(r, "Classpath:"); + myClassPath = (List)RW.readMany(r, RW.myStringReader, new ArrayList()); } - private ProjectWrapper loadSnapshot(final Mappings mappings, final Set affectedFiles) { - initJPSDirectory(); + public String getType() { + return myType; + } - try { - final BufferedReader r = new BufferedReader(new FileReader(getProjectSnapshotFileName())); - final ProjectWrapper w = new ProjectWrapper(r, mappings, affectedFiles); - r.close(); + public List getClassPath(final ClasspathKind kind) { + return myClassPath; + } - return w; - } catch (FileNotFoundException e) { - } catch (IOException e) { - e.printStackTrace(); + public void write(final BufferedWriter w) { + RW.writeln(w, myType); + RW.writeln(w, "Classpath:"); + RW.writeln(w, myClassPath, RW.fromString); + } + } + + private final RW.Reader myFileWrapperReader = new RW.Reader() { + public FileWrapper read(final BufferedReader r) { + return new FileWrapper(r); + } + }; + + public class FileWrapper implements RW.Writable { + final StringCache.S myName; + final long myModificationTime; + + FileWrapper(final File f) { + myName = StringCache.get(getRelativePath(f.getAbsolutePath())); + myModificationTime = f.lastModified(); + } + + FileWrapper(final BufferedReader r) { + myName = StringCache.get(RW.readString(r)); + myModificationTime = RW.readLong(r); + + final Set classes = (Set)RW.readMany(r, ClassRepr.reader, new HashSet()); + final Set>> classesWithSubclasses = new HashSet>>(); + + for (ClassRepr c : classes) { + final Set subClasses = (Set)RW.readMany(r, StringCache.reader, new HashSet()); + classesWithSubclasses.add(new Pair>(c, subClasses)); + } + + final UsageRepr.Cluster usages = new UsageRepr.Cluster(r); + final Set annotationUsages = (Set)RW.readMany(r, UsageRepr.reader, new HashSet()); + final Set formClasses = (Set)RW.readMany(r, StringCache.S.reader, new HashSet()); + + backendCallback + .associate(classesWithSubclasses, new Pair>(usages, annotationUsages), myName.value); + + for (StringCache.S classFileName : formClasses) { + backendCallback.associateForm(myName, classFileName); + } + } + + public StringCache.S getName() { + return myName; + } + + public long getStamp() { + return myModificationTime; + } + + public void write(final BufferedWriter w) { + final StringCache.S name = getName(); + + RW.writeln(w, name.value); + RW.writeln(w, Long.toString(getStamp())); + + final Set classes = dependencyMapping.getClasses(name); + + RW.writeln(w, classes); + + if (classes != null) { + for (ClassRepr c : classes) { + RW.writeln(w, dependencyMapping.getSubClasses(c.name)); + } + } + + dependencyMapping.getUsages(name).write(w); + RW.writeln(w, dependencyMapping.getAnnotationUsages(name)); + RW.writeln(w, dependencyMapping.getFormClass(name)); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + FileWrapper that = (FileWrapper)o; + + if (myName != null ? !myName.equals(that.myName) : that.myName != null) return false; + + return true; + } + + @Override + public int hashCode() { + return myName != null ? myName.hashCode() : 0; + } + } + + private final RW.Reader myModuleWrapperReader = new RW.Reader() { + public ModuleWrapper read(final BufferedReader r) { + return new ModuleWrapper(r); + } + }; + + public class ModuleWrapper implements ClasspathItemWrapper { + + private class Properties implements RW.Writable { + final Set myRoots; + final Map mySources; + + final String myOutput; + String myOutputStatus; + + public Set getFiles() { + final Set result = new HashSet(); + + for (FileWrapper f : mySources.keySet()) { + result.add(f.getName()); } - return null; - } + return result; + } - private void saveSnapshot() { - initJPSDirectory(); + public Set getOutdatedFiles(final Properties past) { + final Set result = new HashSet(); - try { - BufferedWriter bw = new BufferedWriter(new FileWriter(getProjectSnapshotFileName())); + for (FileWrapper now : mySources.keySet()) { + final FileWrapper than = past == null ? null : past.mySources.get(now); - write(bw); - - bw.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - public static ProjectWrapper load(final String path, final String setupScript, final boolean loadHistory) { - return new ProjectWrapper(null, path, setupScript, null, loadHistory); - } - - public static ProjectWrapper load(final GantBinding binding, final String path, final String setupScript, final Map pathVariables, final boolean loadHistory) { - return new ProjectWrapper(binding, path, setupScript, pathVariables, loadHistory); - } - - public void report(final String module) { - final ModuleWrapper m = getModule(module); - - if (m == null) { - System.out.println("No module \"" + module + "\" found in project \""); - } else { - System.out.println("Module " + m.myName + " " + (m.isOutdated(false, myHistory) ? "is outdated" : "is up-to-date")); - System.out.println("Module " + m.myName + " tests " + (m.isOutdated(true, myHistory) ? "are outdated" : "are up-to-date")); - } - } - - public void report() { - boolean moduleReport = true; - - System.out.println("Project \"" + myRoot + "\" report:"); - - if (myHistory == null) { - System.out.println(" no project history found"); + if (than == null || than.getStamp() < now.getStamp() || affectedFiles.contains(now.getName())) { + result.add(now.getName()); + } } - if (moduleReport) { - for (ModuleWrapper m : myModules.values()) { - System.out.println(" module " + m.getName() + " " + (m.isOutdated(false, myHistory) ? "is outdated" : "is up-to-date")); - System.out.println(" module " + m.getName() + " tests " + (m.isOutdated(true, myHistory) ? "are outdated" : "are up-to-date")); + return result; + } + + public Set getRemovedFiles(final Properties past) { + final Set result = new HashSet(); + + if (past != null) { + for (FileWrapper was : past.mySources.keySet()) { + final FileWrapper now = mySources.get(was); + + if (now == null) { + result.add(was.getName()); } + } } + + return result; + } + + public void write(final BufferedWriter w) { + RW.writeln(w, "Roots:"); + RW.writeln(w, myRoots, RW.fromString); + + RW.writeln(w, "Sources:"); + RW.writeln(w, mySources.keySet()); + + RW.writeln(w, "Output:"); + RW.writeln(w, myOutput == null ? "" : myOutput); + + RW.writeln(w, "OutputStatus:" + myOutputStatus); + } + + public Properties(final BufferedReader r) { + RW.readTag(r, "Roots:"); + + myRoots = (Set)RW.readMany(r, RW.myStringReader, new HashSet()); + + RW.readTag(r, "Sources:"); + + mySources = new HashMap(); + + for (FileWrapper fw : (Set)RW.readMany(r, myFileWrapperReader, new HashSet())) { + mySources.put(fw, fw); + } + + RW.readTag(r, "Output:"); + final String s = RW.readString(r); + myOutput = s.equals("") ? null : s; + + myOutputStatus = RW.readStringAttribute(r, "OutputStatus:"); + } + + public Properties(final List sources, final String output, final Set excludes) { + myRoots = (Set)getRelativePaths(sources, new HashSet()); + final DirectoryScanner.Result result = DirectoryScanner.getFiles(myRoots, excludes, ProjectWrapper.this); + + mySources = new HashMap(); + + for (FileWrapper fw : result.getFiles()) { + mySources.put(fw, fw); + } + + myOutput = getRelativePath(output); + updateOutputStatus(); + } + + public void updateOutputStatus() { + final String path = getAbsolutePath(myOutput); + final File ok = new File(path + File.separator + Reporter.myOkFlag); + final File fail = new File(path + File.separator + Reporter.myFailFlag); + + if (ok.exists()) { + myOutputStatus = "ok"; + return; + } + + if (fail.exists()) { + myOutputStatus = "fail"; + return; + } + + myOutputStatus = "empty"; + } + + public Set getRoots() { + return myRoots; + } + + public Set getSources() { + return mySources.keySet(); + } + + public String getOutputPath() { + return myOutput; + } + + public boolean emptySource() { + return mySources.isEmpty(); + } + + public boolean outputOk() { + return myOutputStatus.equals("ok"); + } + + public boolean outputEmpty() { + return myOutputStatus.equals("empty"); + } + + public boolean isOutdated() { + return (!emptySource() && !outputOk()); + } } - public void save() { - saveSnapshot(); + final String myName; + final Properties mySource; + final Properties myTest; + + List myDependsOn; + List myTestDependsOn; + + final Set myExcludes; + final Module myModule; + final Set myLibraries; + + public Set getOutdatedSources() { + return mySource.getOutdatedFiles(myHistory == null ? null : myHistory.getModule(myName).mySource); } - public void clean() { - myProjectBuilder.clean(); - - for (ModuleWrapper m : myModules.values()) { - m.updateOutputStatus(); - } - - new File(myProjectSnapshot).delete(); + public Set getOutdatedTests() { + return myTest.getOutdatedFiles(myHistory == null ? null : myHistory.getModule(myName).myTest); } - public void rebuild() { - makeModules(myProject.getModules().values(), defaultFlags); + public Set getRemovedSources() { + return mySource.getRemovedFiles(myHistory == null ? null : myHistory.getModule(myName).mySource); } - public Project getProject() { - return myProject; + public Set getRemovedTests() { + return myTest.getRemovedFiles(myHistory == null ? null : myHistory.getModule(myName).myTest); } - enum BuildStatus {FAILURE, INCREMENTAL, CONSERVATIVE} - - class BusyBeaver { - final ProjectBuilder builder; - final Set compiledFiles = new HashSet(); - final Set cleared = new HashSet(); - - BusyBeaver(ProjectBuilder builder) { - this.builder = builder; - } - - BuildStatus iterativeCompile(final ModuleChunk chunk, final Set sources, final Set outdated, final Set removed, final Flags flags) { - final Collection filesToCompile = DefaultGroovyMethods.intersect(affectedFiles, sources); - final Set safeFiles = new HashSet(); - - if (outdated != null) { - for (StringCache.S s : outdated) { - assert (s != null); - } - - filesToCompile.addAll(outdated); - - for (StringCache.S f : outdated) { - if (f.value.endsWith(".form")) { - final StringCache.S sourceFileName = dependencyMapping.getJavaByForm(f); - - if (sourceFileName != null && !filesToCompile.contains(sourceFileName)) { - safeFiles.add(sourceFileName); - filesToCompile.add(sourceFileName); - } - } else if (f.value.endsWith(".java")) { - final StringCache.S formFileName = dependencyMapping.getFormByJava(f); - - if (formFileName != null) { - filesToCompile.add(formFileName); - } - } - } - } - - filesToCompile.removeAll(compiledFiles); - - if (!filesToCompile.isEmpty() || removed != null) { - final Set outputFiles = new HashSet(); - - for (StringCache.S f : filesToCompile) { - final Set classes = dependencyMapping.getClasses(f); - - if (classes != null) - for (ClassRepr cr : classes) { - outputFiles.add(cr.fileName); - } - } - - if (removed != null) { - for (StringCache.S f : removed) { - final Set classes = dependencyMapping.getClasses(f); - if (classes != null) { - for (ClassRepr cr : classes) { - outputFiles.add(cr.fileName); - } - } - } - } - - if (!outputFiles.isEmpty()) { - new Logger(flags) { - @Override - public void log(PrintStream stream) { - stream.println("Cleaning output files:"); - logFilePaths(stream, outputFiles); - stream.println("End of files"); - } - }.log(); - - builder.clearChunk(chunk, outputFiles, ProjectWrapper.this); - } - - final Mappings delta = new Mappings(ProjectWrapper.this); - final Callbacks.Backend deltaBackend = delta.getCallback(); - - new Logger(flags) { - @Override - public void log(PrintStream stream) { - stream.println("Compiling files:"); - logFilePaths(stream, filesToCompile); - stream.println("End of files"); - } - }.log(); - - boolean buildException = false; - - try { - builder.buildChunk(chunk, flags.tests(), filesToCompile, deltaBackend, ProjectWrapper.this); - } catch (Exception e) { - e.printStackTrace(); - buildException = true; - } - - if (!buildException) { - compiledFiles.addAll(filesToCompile); - affectedFiles.removeAll(filesToCompile); - - delta.compensateRemovedContent(filesToCompile); - - final boolean incremental = dependencyMapping.differentiate(delta, removed, compiledFiles, affectedFiles, safeFiles); - - dependencyMapping.integrate(delta, removed); - - if (!incremental) { - affectedFiles.addAll(sources); - affectedFiles.removeAll(compiledFiles); - - final BuildStatus result = iterativeCompile(chunk, sources, null, null, flags); - - if (result == BuildStatus.FAILURE) { - return result; - } - - return BuildStatus.CONSERVATIVE; - } - - return iterativeCompile(chunk, sources, null, null, flags); - } else { - return BuildStatus.FAILURE; - } - } else { - for (Module m : chunk.getElements()) { - Reporter.reportBuildSuccess(m, flags.tests()); - } - } - - return BuildStatus.INCREMENTAL; - } - - public BuildStatus build(final Collection modules, final Flags flags) { - boolean incremental = flags.incremental(); - final List chunks = myProjectBuilder.getChunks(flags.tests()).getChunkList(); - - for (final ModuleChunk c : chunks) { - final Set chunkModules = c.getElements(); - - if (!DefaultGroovyMethods.intersect(modules, chunkModules).isEmpty()) { - final Set removedSources = new HashSet(); - - if (incremental) { - final Set chunkSources = new HashSet(); - final Set outdatedSources = new HashSet(); - - for (Module m : chunkModules) { - final ModuleWrapper mw = getModule(m.getName()); - - outdatedSources.addAll(mw.getOutdatedFiles(flags.tests())); - chunkSources.addAll(mw.getSources(flags.tests())); - removedSources.addAll(mw.getRemovedFiles(flags.tests())); - } - - final BuildStatus result = iterativeCompile(c, chunkSources, outdatedSources, removedSources, flags); - - incremental = result == BuildStatus.INCREMENTAL; - - if (result == BuildStatus.FAILURE) { - return result; - } - } else { - new Logger(flags) { - @Override - public void log(PrintStream stream) { - stream.println("Compiling chunk " + c.getName() + " non-incrementally."); - } - }.log(); - - for (Module m : chunkModules) { - final ModuleWrapper mw = getModule(m.getName()); - removedSources.addAll(flags.tests() ? mw.getRemovedTests() : mw.getRemovedSources()); - } - - final Set toClean = new HashSet(); - - for (Module m : chunkModules) { - if (!cleared.contains(m)) { - toClean.add(m); - } - } - - if (!toClean.isEmpty() && !flags.tests()) { - builder.clearChunk(new ModuleChunk(toClean), null, ProjectWrapper.this); - cleared.addAll(toClean); - } - - final Mappings delta = new Mappings(ProjectWrapper.this); - final Callbacks.Backend deltaCallback = delta.getCallback(); - - try { - builder.buildChunk(c, flags.tests(), null, deltaCallback, ProjectWrapper.this); - } catch (Exception e) { - e.printStackTrace(); - return BuildStatus.FAILURE; - } - - for (Module m : c.getElements()) { - final ModuleWrapper module = getModule(m.getName()); - affectedFiles.removeAll(module.getSources(flags.tests())); - } - - dependencyMapping.integrate(delta, removedSources); - - for (Module m : chunkModules) { - Reporter.reportBuildSuccess(m, flags.tests()); - } - } - } - } - - return BuildStatus.INCREMENTAL; - } + public void updateOutputStatus() { + mySource.updateOutputStatus(); + myTest.updateOutputStatus(); } - public void makeModules(final Collection initial, final Flags flags) { - if (myHistory == null && !flags.tests()) { - clean(); + private ClasspathItemWrapper weaken(final ClasspathItemWrapper x) { + if (x instanceof ModuleWrapper) { + return new WeakClasspathItemWrapper((ModuleWrapper)x); + } + else if (x instanceof LibraryWrapper) { + return new WeakClasspathItemWrapper((LibraryWrapper)x); + } + else { + return x; + } + } + + public void write(final BufferedWriter w) { + RW.writeln(w, "Module:" + myName); + + RW.writeln(w, "SourceProperties:"); + mySource.write(w); + + RW.writeln(w, "TestProperties:"); + myTest.write(w); + + RW.writeln(w, "Excludes:"); + RW.writeln(w, myExcludes, RW.fromString); + + RW.writeln(w, "Libraries:"); + RW.writeln(w, myLibraries); + + RW.writeln(w, "Dependencies:"); + + final List weakened = new ArrayList(); + + for (ClasspathItemWrapper cpiw : dependsOn(false)) { + weakened.add(weaken(cpiw)); + } + + RW.writeln(w, weakened); + + weakened.clear(); + + for (ClasspathItemWrapper cpiw : dependsOn(true)) { + weakened.add(weaken(cpiw)); + } + + RW.writeln(w, weakened); + } + + public ModuleWrapper(final BufferedReader r) { + myModule = null; + myName = RW.readStringAttribute(r, "Module:"); + + RW.readTag(r, "SourceProperties:"); + mySource = new Properties(r); + + RW.readTag(r, "TestProperties:"); + myTest = new Properties(r); + + RW.readTag(r, "Excludes:"); + myExcludes = (Set)RW.readMany(r, RW.myStringReader, new HashSet()); + + RW.readTag(r, "Libraries:"); + myLibraries = (Set)RW.readMany(r, myLibraryWrapperReader, new HashSet()); + + RW.readTag(r, "Dependencies:"); + myDependsOn = (List)RW.readMany(r, myWeakClasspathItemWrapperReader, new ArrayList()); + myTestDependsOn = (List)RW.readMany(r, myWeakClasspathItemWrapperReader, new ArrayList()); + } + + public ModuleWrapper(final Module m) { + m.forceInit(); + myModule = m; + myDependsOn = null; + myTestDependsOn = null; + myName = m.getName(); + myExcludes = (Set)getRelativePaths(m.getExcludes(), new HashSet()); + mySource = new Properties(m.getSourceRoots(), m.getOutputPath(), myExcludes); + myTest = new Properties(m.getTestRoots(), m.getTestOutputPath(), myExcludes); + + myLibraries = new HashSet(); + + for (Library lib : m.getLibraries().values()) { + myLibraries.add(new LibraryWrapper(lib)); + } + } + + public String getName() { + return myName; + } + + public Set getOutdatedFiles(final boolean tests) { + if (tests) { + return myTest.outputEmpty() ? getTests() : getOutdatedTests(); + } + + return mySource.outputEmpty() ? getSources() : getOutdatedSources(); + } + + public Set getRemovedFiles(final boolean tests) { + if (tests) { + return getRemovedTests(); + } + + return getRemovedSources(); + } + + public Set getSources(final boolean tests) { + if (tests) { + return myTest.getFiles(); + } + + return mySource.getFiles(); + } + + public Set getSourceRoots() { + return mySource.getRoots(); + } + + public Set getSourceFiles() { + return mySource.getSources(); + } + + public Set getTestFiles() { + return myTest.getSources(); + } + + public Set getSources() { + return mySource.getFiles(); + } + + public Set getTests() { + return myTest.getFiles(); + } + + public String getOutputPath() { + return mySource.getOutputPath(); + } + + public Set getTestSourceRoots() { + return myTest.getRoots(); + } + + public Set getTestSourceFiles() { + return myTest.getSources(); + } + + public String getTestOutputPath() { + return myTest.getOutputPath(); + } + + public List dependsOn(final boolean tests) { + if (tests) { + if (myTestDependsOn != null) { + return myTestDependsOn; + } + } + else if (myDependsOn != null) { + return myDependsOn; + } + + final List result = new ArrayList(); + + for (ClasspathItem cpi : myModule.getClasspath(ClasspathKind.compile(tests))) { + if (cpi instanceof Module) { + result.add(getModule(((Module)cpi).getName())); + } + else if (cpi instanceof Library) { + result.add(new LibraryWrapper((Library)cpi)); + } + else { + result.add(new GenericClasspathItemWrapper(cpi)); + } + } + + if (tests) { + myTestDependsOn = result; + } + else { + myDependsOn = result; + } + + return result; + } + + public List getClassPath(final ClasspathKind kind) { + final List result = new ArrayList(); + + result.add(getOutputPath()); + + if (kind.isTestsIncluded()) { + result.add(getTestOutputPath()); + } + + return result; + } + + private boolean safeEquals(final String a, final String b) { + if (a == null || b == null) return a == b; + + return a.equals(b); + } + + private boolean safeEquals(final ClasspathItemWrapper a, final ClasspathItemWrapper b) { + try { + final StringWriter as = new StringWriter(); + final StringWriter bs = new StringWriter(); + + final BufferedWriter bas = new BufferedWriter(as); + final BufferedWriter bbs = new BufferedWriter(bs); + + weaken(a).write(bas); + weaken(b).write(bbs); + + bas.flush(); + bbs.flush(); + + as.close(); + bs.close(); + + final String x = as.getBuffer().toString(); + final String y = bs.getBuffer().toString(); + + return x.equals(y); + } + catch (IOException e) { + e.printStackTrace(); + return false; + } + } + + public boolean isOutdated(final boolean tests, final ProjectWrapper history) { + if (history == null) { + return true; + } + + final ModuleWrapper past = history.getModule(myName); + + if (past == null) { + return true; + } + + final boolean outputChanged = !safeEquals(past.getOutputPath(), getOutputPath()); + final boolean testOutputChanged = tests && !safeEquals(past.getTestOutputPath(), getTestOutputPath()); + final boolean sourceChanged = !past.getSourceFiles().equals(getSourceFiles()); + final boolean testSourceChanged = tests && !past.getTestSourceFiles().equals(getTestSourceFiles()); + final boolean sourceOutdated = mySource.isOutdated() || !mySource.getOutdatedFiles(past.mySource).isEmpty(); + final boolean testSourceOutdated = tests && (myTest.isOutdated() || !myTest.getOutdatedFiles(past.myTest).isEmpty()); + final boolean unsafeDependencyChange = (new Object() { + public boolean run(final List today, final List yesterday) { + final Iterator t = today.iterator(); + final Iterator y = yesterday.iterator(); + + while (true) { + if (!y.hasNext()) return false; + + if (!t.hasNext()) return true; + + if (!safeEquals(t.next(), y.next())) return true; + } + } + }.run(dependsOn(tests), past.dependsOn(tests))); + + return sourceOutdated || + testSourceOutdated || + sourceChanged || + testSourceChanged || + outputChanged || + testOutputChanged || + unsafeDependencyChange; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ModuleWrapper that = (ModuleWrapper)o; + + if (myName != null ? !myName.equals(that.myName) : that.myName != null) return false; + + return true; + } + + @Override + public int hashCode() { + return myName != null ? myName.hashCode() : 0; + } + } + + final Map myModules = new HashMap(); + final Map myLibraries = new HashMap(); + + public ModuleWrapper getModule(final String name) { + return myModules.get(name); + } + + public LibraryWrapper getLibrary(final String name) { + return myLibraries.get(name); + } + + public Collection getLibraries() { + return myLibraries.values(); + } + + public Collection getModules() { + return myModules.values(); + } + + final Mappings dependencyMapping; + final Callbacks.Backend backendCallback; + final Set affectedFiles; + + final ProjectWrapper myHistory; + + private static String getCanonicalPath(final String path) { + try { + return new File(path).getCanonicalPath(); + } + catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + private ProjectWrapper(final GantBinding binding, + final String prjDir, + final String setupScript, + final Map pathVariables, + final boolean loadHistory) { + dependencyMapping = new Mappings(this); + backendCallback = dependencyMapping.getCallback(); + affectedFiles = new HashSet(); + + myProject = new GantBasedProject(binding == null ? new GantBinding() : binding); + myProjectBuilder = myProject.getBuilder(); + + final File prjFile = new File(prjDir); + final boolean dirBased = !(prjFile.isFile() && prjDir.endsWith(".ipr")); + + myRoot = dirBased ? getCanonicalPath(prjDir) : getCanonicalPath(prjFile.getParent()); + + final String loadPath = dirBased ? getAbsolutePath(myIDEADir) : prjDir; + + myProjectSnapshot = + myHomeDir + File.separator + myJPSDir + File.separator + myRoot.replace(File.separatorChar, myFileSeparatorReplacement); + + IdeaProjectLoader + .loadFromPath(myProject, loadPath, pathVariables != null ? pathVariables : Collections.emptyMap(), setupScript); + + for (Module m : myProject.getModules().values()) { + myModules.put(m.getName(), new ModuleWrapper(m)); + } + + for (Library l : myProject.getLibraries().values()) { + myLibraries.put(l.getName(), new LibraryWrapper(l)); + } + + myHistory = loadHistory ? loadSnapshot(dependencyMapping, affectedFiles) : null; + } + + private ProjectWrapper(final BufferedReader r, final Mappings mappings, final Set affected) { + dependencyMapping = mappings; + affectedFiles = affected; + backendCallback = dependencyMapping.getCallback(); + myProject = null; + myProjectBuilder = null; + myHistory = null; + + myRoot = RW.readStringAttribute(r, "Root:"); + myProjectSnapshot = + myHomeDir + File.separator + myJPSDir + File.separator + myRoot.replace(File.separatorChar, myFileSeparatorReplacement); + + RW.readTag(r, "Libraries:"); + final Set libs = (Set)RW.readMany(r, myLibraryWrapperReader, new HashSet()); + + for (LibraryWrapper l : libs) { + myLibraries.put(l.getName(), l); + } + + RW.readTag(r, "Modules:"); + final Set mods = (Set)RW.readMany(r, myModuleWrapperReader, new HashSet()); + + for (ModuleWrapper m : mods) { + myModules.put(m.getName(), m); + } + + RW.readMany(r, StringCache.reader, affectedFiles); + } + + public String getAbsolutePath(final String relative) { + if (relative == null) return relative; + + if (new File(relative).isAbsolute()) return relative; + + return myRoot + File.separator + relative; + } + + public String getRelativePath(final String absolute) { + if (absolute == null) return absolute; + + if (absolute.startsWith(myRoot)) { + return absolute.substring(myRoot.length() + 1); + } + + return absolute; + } + + public Collection getAbsolutePaths(final Collection paths, final Collection result) { + for (String path : paths) { + if (path != null) result.add(getAbsolutePath(path)); + } + + return result; + } + + public Collection getRelativePaths(final Collection paths, final Collection result) { + for (String path : paths) { + if (path != null) result.add(getRelativePath(path)); + } + + return result; + } + + private boolean isHistory() { + return myProject == null; + } + + public void write(final BufferedWriter w) { + RW.writeln(w, "Root:" + myRoot); + + RW.writeln(w, "Libraries:"); + RW.writeln(w, getLibraries()); + + RW.writeln(w, "Modules:"); + RW.writeln(w, getModules()); + + RW.writeln(w, affectedFiles, StringCache.fromS); + } + + private String getProjectSnapshotFileName() { + return myProjectSnapshot; + } + + private ProjectWrapper loadSnapshot(final Mappings mappings, final Set affectedFiles) { + initJPSDirectory(); + + try { + final BufferedReader r = new BufferedReader(new FileReader(getProjectSnapshotFileName())); + final ProjectWrapper w = new ProjectWrapper(r, mappings, affectedFiles); + r.close(); + + return w; + } + catch (FileNotFoundException e) { + } + catch (IOException e) { + e.printStackTrace(); + } + + return null; + } + + private void saveSnapshot() { + initJPSDirectory(); + + try { + BufferedWriter bw = new BufferedWriter(new FileWriter(getProjectSnapshotFileName())); + + write(bw); + + bw.close(); + } + catch (IOException e) { + e.printStackTrace(); + } + } + + public static ProjectWrapper load(final String path, final String setupScript, final boolean loadHistory) { + return new ProjectWrapper(null, path, setupScript, null, loadHistory); + } + + public static ProjectWrapper load(final GantBinding binding, + final String path, + final String setupScript, + final Map pathVariables, + final boolean loadHistory) { + return new ProjectWrapper(binding, path, setupScript, pathVariables, loadHistory); + } + + public void report(final String module) { + final ModuleWrapper m = getModule(module); + + if (m == null) { + System.out.println("No module \"" + module + "\" found in project \""); + } + else { + System.out.println("Module " + m.myName + " " + (m.isOutdated(false, myHistory) ? "is outdated" : "is up-to-date")); + System.out.println("Module " + m.myName + " tests " + (m.isOutdated(true, myHistory) ? "are outdated" : "are up-to-date")); + } + } + + public void report() { + boolean moduleReport = true; + + System.out.println("Project \"" + myRoot + "\" report:"); + + if (myHistory == null) { + System.out.println(" no project history found"); + } + + if (moduleReport) { + for (ModuleWrapper m : myModules.values()) { + System.out.println(" module " + m.getName() + " " + (m.isOutdated(false, myHistory) ? "is outdated" : "is up-to-date")); + System.out.println(" module " + m.getName() + " tests " + (m.isOutdated(true, myHistory) ? "are outdated" : "are up-to-date")); + } + } + } + + public void save() { + saveSnapshot(); + } + + public void clean() { + myProjectBuilder.clean(); + + for (ModuleWrapper m : myModules.values()) { + m.updateOutputStatus(); + } + + new File(myProjectSnapshot).delete(); + } + + public void rebuild() { + makeModules(myProject.getModules().values(), defaultFlags); + } + + public Project getProject() { + return myProject; + } + + enum BuildStatus {FAILURE, INCREMENTAL, CONSERVATIVE} + + class BusyBeaver { + final ProjectBuilder builder; + final Set compiledFiles = new HashSet(); + final Set cleared = new HashSet(); + + BusyBeaver(ProjectBuilder builder) { + this.builder = builder; + } + + BuildStatus iterativeCompile(final ModuleChunk chunk, + final Set sources, + final Set outdated, + final Set removed, + final Flags flags) { + final Collection filesToCompile = DefaultGroovyMethods.intersect(affectedFiles, sources); + final Set safeFiles = new HashSet(); + + if (outdated != null) { + for (StringCache.S s : outdated) { + assert (s != null); } - new Logger(flags) { - @Override - public void log(final PrintStream stream) { - stream.println("Request to make modules:"); - logMany(stream, initial); - stream.println("End of request"); + filesToCompile.addAll(outdated); + + for (StringCache.S f : outdated) { + if (f.value.endsWith(".form")) { + final StringCache.S sourceFileName = dependencyMapping.getJavaByForm(f); + + if (sourceFileName != null && !filesToCompile.contains(sourceFileName)) { + safeFiles.add(sourceFileName); + filesToCompile.add(sourceFileName); } - }.log(); + } + else if (f.value.endsWith(".java")) { + final StringCache.S formFileName = dependencyMapping.getFormByJava(f); - final ClasspathKind kind = ClasspathKind.compile(flags.tests()); - - final Set modules = new HashSet(); - final Set marked = new HashSet(); - final Map visited = new HashMap(); - final Set frontier = new HashSet(); - - final Map> reversedDependencies = new HashMap>(); - final DotPrinter printer = new DotPrinter(flags.logStream()); - - printer.header(); - - for (Module m : myProject.getModules().values()) { - final String mName = m.getName(); - - printer.node(mName); - - for (ClasspathItem cpi : m.getClasspath(kind)) { - if (cpi instanceof Module) { - final String name = ((Module) cpi).getName(); - - printer.edge(name, mName); - - Set sm = reversedDependencies.get(name); - - if (sm == null) { - sm = new HashSet(); - reversedDependencies.put(name, sm); - } - - sm.add(mName); - } + if (formFileName != null) { + filesToCompile.add(formFileName); } + } + } + } + + filesToCompile.removeAll(compiledFiles); + + if (!filesToCompile.isEmpty() || removed != null) { + final Set outputFiles = new HashSet(); + + for (StringCache.S f : filesToCompile) { + final Set classes = dependencyMapping.getClasses(f); + + if (classes != null) { + for (ClassRepr cr : classes) { + outputFiles.add(cr.fileName); + } + } } - printer.footer(); - - // Building "upper" subgraph - - printer.header(); - - new Object() { - public void run(final Collection initial) { - if (initial == null) - return; - - for (Module module : initial) { - - final String mName = module.getName(); - - if (marked.contains(mName)) - continue; - - printer.node(mName); - - final List dep = new ArrayList(); - - for (ClasspathItem cpi : module.getClasspath(kind)) { - if (cpi instanceof Module && !marked.contains(((Module) cpi).getName())) { - printer.edge(((Module) cpi).getName(), mName); - dep.add((Module) cpi); - } - } - - if (dep.size() == 0) { - frontier.add(mName); - } - - marked.add(mName); - - run(dep); - } + if (removed != null) { + for (StringCache.S f : removed) { + final Set classes = dependencyMapping.getClasses(f); + if (classes != null) { + for (ClassRepr cr : classes) { + outputFiles.add(cr.fileName); + } } - }.run(initial); + } + } - printer.footer(); - - // Traversing "upper" subgraph and collecting outdated modules and their descendants - new Object() { - public void run(final Collection initial, final boolean force) { - if (initial == null) - return; - - for (String moduleName : initial) { - if (!marked.contains(moduleName)) - continue; - - final Boolean property = visited.get(moduleName); - - if (property == null || !property && force) { - final boolean outdated = getModule(moduleName).isOutdated(flags.tests(), myHistory); - - - if (force || outdated) { - visited.put(moduleName, true); - modules.add(myProject.getModules().get(moduleName)); - - run(reversedDependencies.get(moduleName), true); - } else { - if (property == null) { - visited.put(moduleName, false); - } - run(reversedDependencies.get(moduleName), false); - } - } - } - } - }.run(frontier, flags.force()); - - new Logger(flags) { + if (!outputFiles.isEmpty()) { + new Logger(flags) { @Override public void log(PrintStream stream) { - stream.println("Propagated modules:"); - logMany(stream, modules); - stream.println("End of propagated"); + stream.println("Cleaning output files:"); + logFilePaths(stream, outputFiles); + stream.println("End of files"); } + }.log(); + + builder.clearChunk(chunk, outputFiles, ProjectWrapper.this); + } + + final Mappings delta = new Mappings(ProjectWrapper.this); + final Callbacks.Backend deltaBackend = delta.getCallback(); + + new Logger(flags) { + @Override + public void log(PrintStream stream) { + stream.println("Compiling files:"); + logFilePaths(stream, filesToCompile); + stream.println("End of files"); + } }.log(); - if (modules.size() == 0 && !flags.force()) { - System.out.println("All requested modules are up-to-date."); - return; + boolean buildException = false; + + try { + builder.buildChunk(chunk, flags.tests(), filesToCompile, deltaBackend, ProjectWrapper.this); + } + catch (Exception e) { + e.printStackTrace(); + buildException = true; } - final BusyBeaver beaver = new BusyBeaver(myProjectBuilder); + if (!buildException) { + compiledFiles.addAll(filesToCompile); + affectedFiles.removeAll(filesToCompile); - myProjectBuilder.buildStart(); + final boolean incremental = dependencyMapping.differentiate(delta, removed, filesToCompile, compiledFiles, affectedFiles, safeFiles); - if (flags.tests()) { - beaver.build(modules, new Flags() { - public boolean tests() { - return false; - } + dependencyMapping.integrate(delta, removed); - public boolean incremental() { - return flags.incremental(); - } + if (!incremental) { + affectedFiles.addAll(sources); + affectedFiles.removeAll(compiledFiles); - public boolean force() { - return flags.force(); - } + final BuildStatus result = iterativeCompile(chunk, sources, null, null, flags); - public PrintStream logStream() { - return flags.logStream(); - } - }); - } - - beaver.build(modules, flags); - - myProjectBuilder.buildStop(); - - for (Module mod : modules) { - getModule(mod.getName()).updateOutputStatus(); - } - } - - public void makeModule(final String modName, final Flags flags) { - if (modName == null) { - makeModules(myProject.getModules().values(), flags); - } else { - final Module module = myProject.getModules().get(modName); - final List list = new ArrayList(); - - if (module == null) { - System.err.println("Module \"" + modName + "\" not found in project \"" + myRoot + "\""); - return; + if (result == BuildStatus.FAILURE) { + return result; } - list.add(module); + return BuildStatus.CONSERVATIVE; + } - makeModules(list, flags); + return iterativeCompile(chunk, sources, null, null, flags); } + else { + return BuildStatus.FAILURE; + } + } + else { + for (Module m : chunk.getElements()) { + Reporter.reportBuildSuccess(m, flags.tests()); + } + } + + return BuildStatus.INCREMENTAL; } + + public BuildStatus build(final Collection modules, final Flags flags) { + boolean incremental = flags.incremental(); + final List chunks = myProjectBuilder.getChunks(flags.tests()).getChunkList(); + + for (final ModuleChunk c : chunks) { + final Set chunkModules = c.getElements(); + + if (!DefaultGroovyMethods.intersect(modules, chunkModules).isEmpty()) { + final Set removedSources = new HashSet(); + + if (incremental) { + final Set chunkSources = new HashSet(); + final Set outdatedSources = new HashSet(); + + for (Module m : chunkModules) { + final ModuleWrapper mw = getModule(m.getName()); + + outdatedSources.addAll(mw.getOutdatedFiles(flags.tests())); + chunkSources.addAll(mw.getSources(flags.tests())); + removedSources.addAll(mw.getRemovedFiles(flags.tests())); + } + + final BuildStatus result = iterativeCompile(c, chunkSources, outdatedSources, removedSources, flags); + + incremental = result == BuildStatus.INCREMENTAL; + + if (result == BuildStatus.FAILURE) { + return result; + } + } + else { + new Logger(flags) { + @Override + public void log(PrintStream stream) { + stream.println("Compiling chunk " + c.getName() + " non-incrementally."); + } + }.log(); + + for (Module m : chunkModules) { + final ModuleWrapper mw = getModule(m.getName()); + removedSources.addAll(flags.tests() ? mw.getRemovedTests() : mw.getRemovedSources()); + } + + final Set toClean = new HashSet(); + + for (Module m : chunkModules) { + if (!cleared.contains(m)) { + toClean.add(m); + } + } + + if (!toClean.isEmpty() && !flags.tests()) { + builder.clearChunk(new ModuleChunk(toClean), null, ProjectWrapper.this); + cleared.addAll(toClean); + } + + final Mappings delta = new Mappings(ProjectWrapper.this); + final Callbacks.Backend deltaCallback = delta.getCallback(); + + try { + builder.buildChunk(c, flags.tests(), null, deltaCallback, ProjectWrapper.this); + } + catch (Exception e) { + e.printStackTrace(); + return BuildStatus.FAILURE; + } + + for (Module m : c.getElements()) { + final ModuleWrapper module = getModule(m.getName()); + affectedFiles.removeAll(module.getSources(flags.tests())); + } + + dependencyMapping.integrate(delta, removedSources); + + for (Module m : chunkModules) { + Reporter.reportBuildSuccess(m, flags.tests()); + } + } + } + } + + return BuildStatus.INCREMENTAL; + } + } + + public void makeModules(final Collection initial, final Flags flags) { + if (myHistory == null && !flags.tests()) { + clean(); + } + + new Logger(flags) { + @Override + public void log(final PrintStream stream) { + stream.println("Request to make modules:"); + logMany(stream, initial); + stream.println("End of request"); + } + }.log(); + + final ClasspathKind kind = ClasspathKind.compile(flags.tests()); + + final Set modules = new HashSet(); + final Set marked = new HashSet(); + final Map visited = new HashMap(); + final Set frontier = new HashSet(); + + final Map> reversedDependencies = new HashMap>(); + final DotPrinter printer = new DotPrinter(flags.logStream()); + + printer.header(); + + for (Module m : myProject.getModules().values()) { + final String mName = m.getName(); + + printer.node(mName); + + for (ClasspathItem cpi : m.getClasspath(kind)) { + if (cpi instanceof Module) { + final String name = ((Module)cpi).getName(); + + printer.edge(name, mName); + + Set sm = reversedDependencies.get(name); + + if (sm == null) { + sm = new HashSet(); + reversedDependencies.put(name, sm); + } + + sm.add(mName); + } + } + } + + printer.footer(); + + // Building "upper" subgraph + + printer.header(); + + new Object() { + public void run(final Collection initial) { + if (initial == null) return; + + for (Module module : initial) { + + final String mName = module.getName(); + + if (marked.contains(mName)) continue; + + printer.node(mName); + + final List dep = new ArrayList(); + + for (ClasspathItem cpi : module.getClasspath(kind)) { + if (cpi instanceof Module && !marked.contains(((Module)cpi).getName())) { + printer.edge(((Module)cpi).getName(), mName); + dep.add((Module)cpi); + } + } + + if (dep.size() == 0) { + frontier.add(mName); + } + + marked.add(mName); + + run(dep); + } + } + }.run(initial); + + printer.footer(); + + // Traversing "upper" subgraph and collecting outdated modules and their descendants + new Object() { + public void run(final Collection initial, final boolean force) { + if (initial == null) return; + + for (String moduleName : initial) { + if (!marked.contains(moduleName)) continue; + + final Boolean property = visited.get(moduleName); + + if (property == null || !property && force) { + final boolean outdated = getModule(moduleName).isOutdated(flags.tests(), myHistory); + + + if (force || outdated) { + visited.put(moduleName, true); + modules.add(myProject.getModules().get(moduleName)); + + run(reversedDependencies.get(moduleName), true); + } + else { + if (property == null) { + visited.put(moduleName, false); + } + run(reversedDependencies.get(moduleName), false); + } + } + } + } + }.run(frontier, flags.force()); + + new Logger(flags) { + @Override + public void log(PrintStream stream) { + stream.println("Propagated modules:"); + logMany(stream, modules); + stream.println("End of propagated"); + } + }.log(); + + if (modules.size() == 0 && !flags.force()) { + System.out.println("All requested modules are up-to-date."); + return; + } + + final BusyBeaver beaver = new BusyBeaver(myProjectBuilder); + + myProjectBuilder.buildStart(); + + if (flags.tests()) { + beaver.build(modules, new Flags() { + public boolean tests() { + return false; + } + + public boolean incremental() { + return flags.incremental(); + } + + public boolean force() { + return flags.force(); + } + + public PrintStream logStream() { + return flags.logStream(); + } + }); + } + + beaver.build(modules, flags); + + myProjectBuilder.buildStop(); + + for (Module mod : modules) { + getModule(mod.getName()).updateOutputStatus(); + } + } + + public void makeModule(final String modName, final Flags flags) { + if (modName == null) { + makeModules(myProject.getModules().values(), flags); + } + else { + final Module module = myProject.getModules().get(modName); + final List list = new ArrayList(); + + if (module == null) { + System.err.println("Module \"" + modName + "\" not found in project \"" + myRoot + "\""); + return; + } + + list.add(module); + + makeModules(list, flags); + } + } } diff --git a/jps/src/org/jetbrains/ether/dependencyView/Mappings.java b/jps/src/org/jetbrains/ether/dependencyView/Mappings.java index d060b36f083e..760aac438542 100644 --- a/jps/src/org/jetbrains/ether/dependencyView/Mappings.java +++ b/jps/src/org/jetbrains/ether/dependencyView/Mappings.java @@ -51,7 +51,7 @@ public class Mappings { private Map formToClass = new HashMap(); private Map classToForm = new HashMap(); - public void compensateRemovedContent(final Collection compiled) { + private void compensateRemovedContent(final Collection compiled) { for (StringCache.S name : compiled) { final Collection classes = sourceFileToClasses.foxyGet(name); @@ -369,11 +369,40 @@ public class Mappings { } } + private Set cache (final Collection x) { + final Set y = new HashSet(); + + for (String s : x) { + y.add(StringCache.get(s)); + } + + return y; + } + + public boolean differentiate(final Mappings delta, + final Collection removed, + final Collection filesToCompile, + final Collection compiledFiles, + final Collection affectedFiles, + final Collection safeFiles) { + final Set affectedCache = cache(affectedFiles); + + final boolean result = differentiate(delta, cache(removed), cache(filesToCompile), cache(compiledFiles), affectedCache, cache(safeFiles)); + + for (StringCache.S a : affectedCache) { + affectedFiles.add(a.value); + } + + return result; + } + public boolean differentiate(final Mappings delta, final Set removed, + final Collection filesToCompile, final Set compiledFiles, final Set affectedFiles, final Set safeFiles) { + delta.compensateRemovedContent(filesToCompile); final Util u = new Util(delta); final Util o = new Util(); @@ -779,6 +808,10 @@ public class Mappings { return true; } + public void integrate (final Mappings delta, final Collection removed) { + integrate(delta, cache(removed)); + } + public void integrate(final Mappings delta, final Set removed) { if (removed != null) { for (StringCache.S file : removed) {