Package org.jetbrains.ether

Source Code of org.jetbrains.ether.ProjectWrapper$ClasspathItemWrapper

package org.jetbrains.ether;

import com.intellij.openapi.util.io.FileUtil;
import org.codehaus.gant.GantBinding;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
import org.jetbrains.ether.dependencyView.*;
import org.jetbrains.jps.*;
import org.jetbrains.jps.idea.IdeaProjectLoader;
import org.jetbrains.jps.resolvers.PathEntry;

import java.io.*;
import java.util.*;

/**
* Created by IntelliJ IDEA.
* User: db
* Date: 19.11.10
* Time: 2:58
* To change this template use File | Settings | File Templates.
*/

public class ProjectWrapper {
    public interface Flags {
        boolean tests();

        boolean incremental();

        boolean force();

        PrintStream logStream();
    }

    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<StringCache.S> paths) {
            List<String> strings = new ArrayList<String>(paths.size());
            for (StringCache.S path : paths) {
              strings.add(FileUtil.toSystemIndependentName(path.toString()));
            }
            logMany(stream, strings);
        }

        public <T> void logMany(final PrintStream stream, final Collection<T> 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);
        }
    }

    // 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());
            }
        }
    }

    // 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<String> getClassPath(ClasspathKind kind);
    }

    private final RW.Reader<LibraryWrapper> myLibraryWrapperReader =
            new RW.Reader<LibraryWrapper>() {
                public LibraryWrapper read(final BufferedReader r) {
                    return new LibraryWrapper(r);
                }
            };

    public class LibraryWrapper implements ClasspathItemWrapper {
        final String myName;
        final List<String> 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<String>) RW.readMany(r, RW.myStringReader, new ArrayList<String>());
        }

        public LibraryWrapper(final Library lib) {
            lib.forceInit();
            myName = lib.getName();
            myClassPath = (List<String>) getRelativePaths(lib.getClasspath(), new ArrayList<String>());
        }

        public String getName() {
            return myName;
        }

        public List<String> 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;
        }
    }

    private final RW.Reader<ClasspathItemWrapper> myWeakClasspathItemWrapperReader =
            new RW.Reader<ClasspathItemWrapper>() {
                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;
        }

        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<String> getClassPath(ClasspathKind kind) {
            return null;
        }

        public void write(final BufferedWriter w) {
            RW.writeln(w, myType + ":" + getName());
        }
    }

    public class GenericClasspathItemWrapper implements ClasspathItemWrapper {
        final List<String> myClassPath;
        final String myType;

        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;

            myClassPath = (List<String>) getRelativePaths(item.getClasspathRoots(null), new ArrayList<String>());
        }

        public GenericClasspathItemWrapper(final BufferedReader r) {
            myType = RW.readString(r);

            RW.readTag(r, "Classpath:");
            myClassPath = (List<String>) RW.readMany(r, RW.myStringReader, new ArrayList<String>());
        }

        public String getType() {
            return myType;
        }

        public List<String> getClassPath(final ClasspathKind kind) {
            return myClassPath;
        }

        public void write(final BufferedWriter w) {
            RW.writeln(w, myType);
            RW.writeln(w, "Classpath:");
            RW.writeln(w, myClassPath, RW.fromString);
        }
    }

    private final RW.Reader<FileWrapper> myFileWrapperReader =
            new RW.Reader<FileWrapper>() {
                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<ClassRepr> classes = (Set<ClassRepr>) RW.readMany(r, ClassRepr.reader, new HashSet<ClassRepr>());
            final Set<Pair<ClassRepr, Set<StringCache.S>>> classesWithSubclasses = new HashSet<Pair<ClassRepr, Set<StringCache.S>>>();

            for (ClassRepr c : classes) {
                final Set<StringCache.S> subClasses = (Set<StringCache.S>) RW.readMany(r, StringCache.reader, new HashSet<StringCache.S>());
                classesWithSubclasses.add(new Pair<ClassRepr, Set<StringCache.S>>(c, subClasses));
            }

            final UsageRepr.Cluster usages = new UsageRepr.Cluster(r);
            final Set<UsageRepr.Usage> annotationUsages = (Set<UsageRepr.Usage>) RW.readMany(r, UsageRepr.reader, new HashSet<UsageRepr.Usage>());
            final Set<StringCache.S> formClasses = (Set<StringCache.S>) RW.readMany(r, StringCache.S.reader, new HashSet<StringCache.S>());

            backendCallback.associate(classesWithSubclasses, new Pair<UsageRepr.Cluster, Set<UsageRepr.Usage>>(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<ClassRepr> 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<ModuleWrapper> myModuleWrapperReader =
            new RW.Reader<ModuleWrapper>() {
                public ModuleWrapper read(final BufferedReader r) {
                    return new ModuleWrapper(r);
                }
            };

    public class ModuleWrapper implements ClasspathItemWrapper {

        private class Properties implements RW.Writable {
            final Set<String> myRoots;
            final Map<FileWrapper, FileWrapper> mySources;

            final String myOutput;
            String myOutputStatus;

            public Set<StringCache.S> getFiles() {
                final Set<StringCache.S> result = new HashSet<StringCache.S>();

                for (FileWrapper f : mySources.keySet()) {
                    result.add(f.getName());
                }

                return result;
            }

            public Set<StringCache.S> getOutdatedFiles(final Properties past) {
                final Set<StringCache.S> result = new HashSet<StringCache.S>();

                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<StringCache.S> getRemovedFiles(final Properties past) {
                final Set<StringCache.S> result = new HashSet<StringCache.S>();

                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<String>) RW.readMany(r, RW.myStringReader, new HashSet<String>());

                RW.readTag(r, "Sources:");

                mySources = new HashMap<FileWrapper, FileWrapper>();

                for (FileWrapper fw : (Set<FileWrapper>) RW.readMany(r, myFileWrapperReader, new HashSet<FileWrapper>())) {
                    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<String> sources, final String output, final Set<String> excludes) {
                myRoots = (Set<String>) getRelativePaths(sources, new HashSet<String>());
                final DirectoryScanner.Result result = DirectoryScanner.getFiles(myRoots, excludes, ProjectWrapper.this);

                mySources = new HashMap<FileWrapper, FileWrapper>();

                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<String> getRoots() {
                return myRoots;
            }

            public Set<FileWrapper> 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<ClasspathItemWrapper> myDependsOn;
        List<ClasspathItemWrapper> myTestDependsOn;

        final Set<String> myExcludes;
        final Module myModule;
        final Set<LibraryWrapper> myLibraries;

        public Set<StringCache.S> getOutdatedSources() {
            return mySource.getOutdatedFiles(myHistory == null ? null : myHistory.getModule(myName).mySource);
        }

        public Set<StringCache.S> getOutdatedTests() {
            return myTest.getOutdatedFiles(myHistory == null ? null : myHistory.getModule(myName).myTest);
        }

        public Set<StringCache.S> getRemovedSources() {
            return mySource.getRemovedFiles(myHistory == null ? null : myHistory.getModule(myName).mySource);
        }

        public Set<StringCache.S> 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<ClasspathItemWrapper> weakened = new ArrayList<ClasspathItemWrapper>();

            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<String>) RW.readMany(r, RW.myStringReader, new HashSet<String>());

            RW.readTag(r, "Libraries:");
            myLibraries = (Set<LibraryWrapper>) RW.readMany(r, myLibraryWrapperReader, new HashSet<LibraryWrapper>());

            RW.readTag(r, "Dependencies:");
            myDependsOn = (List<ClasspathItemWrapper>) RW.readMany(r, myWeakClasspathItemWrapperReader, new ArrayList<ClasspathItemWrapper>());
            myTestDependsOn = (List<ClasspathItemWrapper>) RW.readMany(r, myWeakClasspathItemWrapperReader, new ArrayList<ClasspathItemWrapper>());
        }

        public ModuleWrapper(final Module m) {
            m.forceInit();
            myModule = m;
            myDependsOn = null;
            myTestDependsOn = null;
            myName = m.getName();
            myExcludes = (Set<String>) getRelativePaths(m.getExcludes(), new HashSet<String>());
            mySource = new Properties(m.getSourceRoots(), m.getOutputPath(), myExcludes);
            myTest = new Properties(m.getTestRoots(), m.getTestOutputPath(), myExcludes);

            myLibraries = new HashSet<LibraryWrapper>();

            for (Library lib : m.getLibraries().values()) {
                myLibraries.add(new LibraryWrapper(lib));
            }
        }

        public String getName() {
            return myName;
        }

        public Set<StringCache.S> getOutdatedFiles(final boolean tests) {
            if (tests) {
                return myTest.outputEmpty() ? getTests() : getOutdatedTests();
            }

            return mySource.outputEmpty() ? getSources() : getOutdatedSources();
        }

        public Set<StringCache.S> getRemovedFiles(final boolean tests) {
            if (tests) {
                return getRemovedTests();
            }

            return getRemovedSources();
        }

        public Set<StringCache.S> getSources(final boolean tests) {
            if (tests) {
                return myTest.getFiles();
            }

            return mySource.getFiles();
        }

        public Set<String> getSourceRoots() {
            return mySource.getRoots();
        }

        public Set<FileWrapper> getSourceFiles() {
            return mySource.getSources();
        }

        public Set<FileWrapper> getTestFiles() {
            return myTest.getSources();
        }

        public Set<StringCache.S> getSources() {
            return mySource.getFiles();
        }

        public Set<StringCache.S> getTests() {
            return myTest.getFiles();
        }

        public String getOutputPath() {
            return mySource.getOutputPath();
        }

        public Set<String> getTestSourceRoots() {
            return myTest.getRoots();
        }

        public Set<FileWrapper> getTestSourceFiles() {
            return myTest.getSources();
        }

        public String getTestOutputPath() {
            return myTest.getOutputPath();
        }

        public List<ClasspathItemWrapper> dependsOn(final boolean tests) {
            if (tests) {
                if (myTestDependsOn != null) {
                    return myTestDependsOn;
                }
            } else if (myDependsOn != null) {
                return myDependsOn;
            }

            final List<ClasspathItemWrapper> result = new ArrayList<ClasspathItemWrapper>();

            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<String> getClassPath(final ClasspathKind kind) {
            final List<String> result = new ArrayList<String>();

            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<ClasspathItemWrapper> today, final List<ClasspathItemWrapper> yesterday) {
                            final Iterator<ClasspathItemWrapper> t = today.iterator();
                            final Iterator<ClasspathItemWrapper> 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<String, ModuleWrapper> myModules = new HashMap<String, ModuleWrapper>();
    final Map<String, LibraryWrapper> myLibraries = new HashMap<String, LibraryWrapper>();

    public ModuleWrapper getModule(final String name) {
        return myModules.get(name);
    }

    public LibraryWrapper getLibrary(final String name) {
        return myLibraries.get(name);
    }

    public Collection<LibraryWrapper> getLibraries() {
        return myLibraries.values();
    }

    public Collection<ModuleWrapper> getModules() {
        return myModules.values();
    }

    final Mappings dependencyMapping;
    final Callbacks.Backend backendCallback;
    final Set<StringCache.S> 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<String, String> pathVariables, final boolean loadHistory) {
        dependencyMapping = new Mappings(this);
        backendCallback = dependencyMapping.getCallback();
        affectedFiles = new HashSet<StringCache.S>();

        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.<String, String>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<StringCache.S> 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<LibraryWrapper> libs = (Set<LibraryWrapper>) RW.readMany(r, myLibraryWrapperReader, new HashSet<LibraryWrapper>());

        for (LibraryWrapper l : libs) {
            myLibraries.put(l.getName(), l);
        }

        RW.readTag(r, "Modules:");
        final Set<ModuleWrapper> mods = (Set<ModuleWrapper>) RW.readMany(r, myModuleWrapperReader, new HashSet<ModuleWrapper>());

        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<String> getAbsolutePaths(final Collection<String> paths, final Collection<String> result) {
        for (String path : paths) {
            if (path != null)
                result.add(getAbsolutePath(path));
        }

        return result;
    }

    public Collection<String> getRelativePaths(final Collection<String> paths, final Collection<String> 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<StringCache.S> 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<String, String> 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<StringCache.S> compiledFiles = new HashSet<StringCache.S>();
        final Set<Module> cleared = new HashSet<Module>();

        BusyBeaver(ProjectBuilder builder) {
            this.builder = builder;
        }

        BuildStatus iterativeCompile(final ModuleChunk chunk, final Set<StringCache.S> sources, final Set<StringCache.S> outdated, final Set<StringCache.S> removed, final Flags flags) {
            final Collection<StringCache.S> filesToCompile = DefaultGroovyMethods.intersect(affectedFiles, sources);
            final Set<StringCache.S> safeFiles = new HashSet<StringCache.S>();

            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<StringCache.S> outputFiles = new HashSet<StringCache.S>();

                for (StringCache.S f : filesToCompile) {
                    final Set<ClassRepr> 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<ClassRepr> 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<Module> modules, final Flags flags) {
            boolean incremental = flags.incremental();
            final List<ModuleChunk> chunks = myProjectBuilder.getChunks(flags.tests()).getChunkList();

            for (final ModuleChunk c : chunks) {
                final Set<Module> chunkModules = c.getElements();

                if (!DefaultGroovyMethods.intersect(modules, chunkModules).isEmpty()) {
                    final Set<StringCache.S> removedSources = new HashSet<StringCache.S>();

                    if (incremental) {
                        final Set<StringCache.S> chunkSources = new HashSet<StringCache.S>();
                        final Set<StringCache.S> outdatedSources = new HashSet<StringCache.S>();

                        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<Module> toClean = new HashSet<Module>();

                        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<Module> 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<Module> modules = new HashSet<Module>();
        final Set<String> marked = new HashSet<String>();
        final Map<String, Boolean> visited = new HashMap<String, Boolean>();
        final Set<String> frontier = new HashSet<String>();

        final Map<String, Set<String>> reversedDependencies = new HashMap<String, Set<String>>();
        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<String> sm = reversedDependencies.get(name);

                    if (sm == null) {
                        sm = new HashSet<String>();
                        reversedDependencies.put(name, sm);
                    }

                    sm.add(mName);
                }
            }
        }

        printer.footer();

        // Building "upper" subgraph

        printer.header();

        new Object() {
            public void run(final Collection<Module> initial) {
                if (initial == null)
                    return;

                for (Module module : initial) {

                    final String mName = module.getName();

                    if (marked.contains(mName))
                        continue;

                    printer.node(mName);

                    final List<Module> dep = new ArrayList<Module>();

                    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<String> 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<Module> list = new ArrayList<Module>();

            if (module == null) {
                System.err.println("Module \"" + modName + "\" not found in project \"" + myRoot + "\"");
                return;
            }

            list.add(module);

            makeModules(list, flags);
        }
    }
}
TOP

Related Classes of org.jetbrains.ether.ProjectWrapper$ClasspathItemWrapper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.