From 91d4b867e1758f685fea873ca68c45b0ba297abc Mon Sep 17 00:00:00 2001 From: nik Date: Tue, 24 Jul 2012 15:58:41 +0400 Subject: [PATCH] FilesDelta split to separate instances for production and tests --- .../jps/incremental/fs/BuildFSState.java | 16 +- .../jetbrains/jps/incremental/fs/FSState.java | 298 ++++++------- .../jps/incremental/fs/FilesDelta.java | 404 ++++++++---------- 3 files changed, 351 insertions(+), 367 deletions(-) diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/BuildFSState.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/BuildFSState.java index eb8a4174337d..4d1b30c3f996 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/BuildFSState.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/BuildFSState.java @@ -33,7 +33,7 @@ public class BuildFSState extends FSState { private static final Key LAST_ROUND_DELTA_KEY = Key.create("_last_round_delta_"); // when true, will always determine dirty files by scanning FS and comparing timestamps - // alternatively, when false, after first scan will rely on extarnal notifications about changes + // alternatively, when false, after first scan will rely on external notifications about changes private final boolean myAlwaysScanFS; public BuildFSState(boolean alwaysScanFS) { @@ -49,7 +49,7 @@ public class BuildFSState extends FSState { public Map> getSourcesToRecompile(@NotNull CompileContext context, final String moduleName, boolean forTests) { final FilesDelta lastRoundDelta = getRoundDelta(LAST_ROUND_DELTA_KEY, context); if (lastRoundDelta != null) { - return lastRoundDelta.getSourcesToRecompile(forTests); + return lastRoundDelta.getSourcesToRecompile(); } return super.getSourcesToRecompile(context, moduleName, forTests); } @@ -59,7 +59,7 @@ public class BuildFSState extends FSState { final FilesDelta roundDelta = getRoundDelta(CURRENT_ROUND_DELTA_KEY, context); if (roundDelta != null) { if (getContextModules(context).contains(rd.module)) { - roundDelta.markRecompile(rd.root, rd.isTestRoot, file); + roundDelta.markRecompile(rd.root, file); } } return super.markDirty(context, file, rd, tsStorage); @@ -72,7 +72,7 @@ public class BuildFSState extends FSState { final FilesDelta roundDelta = getRoundDelta(CURRENT_ROUND_DELTA_KEY, context); if (roundDelta != null) { if (getContextModules(context).contains(rd.module)) { - roundDelta.markRecompile(rd.root, rd.isTestRoot, file); + roundDelta.markRecompile(rd.root, file); } } } @@ -138,8 +138,8 @@ public class BuildFSState extends FSState { */ public boolean markAllUpToDate(CompileScope scope, final RootDescriptor rd, final Timestamps stamps, final long compilationStartStamp) throws IOException { boolean marked = false; - final FilesDelta delta = getDelta(rd.module); - final Set files = delta.clearRecompile(rd.root, rd.isTestRoot); + final FilesDelta delta = getDelta(rd.module, rd.isTestRoot); + final Set files = delta.clearRecompile(rd.root); if (files != null) { final CompilerExcludes excludes = scope.getProject().getCompilerConfiguration().getExcludes(); for (File file : files) { @@ -149,7 +149,7 @@ public class BuildFSState extends FSState { if (!rd.isGeneratedSources && stamp > compilationStartStamp) { // if the file was modified after the compilation had started, // do not save the stamp considering file dirty - delta.markRecompile(rd.root, rd.isTestRoot, file); + delta.markRecompile(rd.root, file); if (Utils.IS_TEST_MODE) { LOG.info("Timestamp after compilation started; marking dirty again: " + file.getPath()); } @@ -163,7 +163,7 @@ public class BuildFSState extends FSState { if (Utils.IS_TEST_MODE) { LOG.info("Not affected by compile scope; marking dirty again: " + file.getPath()); } - delta.markRecompile(rd.root, rd.isTestRoot, file); + delta.markRecompile(rd.root, file); } } else { diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FSState.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FSState.java index 4ef682bd7964..52bd359882f8 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FSState.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FSState.java @@ -1,143 +1,155 @@ -package org.jetbrains.jps.incremental.fs; - -import com.intellij.util.io.IOUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.jps.incremental.CompileContext; -import org.jetbrains.jps.incremental.storage.Timestamps; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.File; -import java.io.IOException; -import java.util.*; - -/** - * @author Eugene Zhuravlev - * Date: 4/20/12 - */ -public class FSState { - private final Map myDeltas = Collections.synchronizedMap(new HashMap()); - protected final Set myInitialTestsScanPerformed = Collections.synchronizedSet(new HashSet()); - protected final Set myInitialProductionScanPerformed = Collections.synchronizedSet(new HashSet()); - - public void save(DataOutput out) throws IOException { - out.writeInt(myInitialTestsScanPerformed.size()); - for (String moduleName : myInitialTestsScanPerformed) { - IOUtil.writeString(moduleName, out); - getDelta(moduleName).save(out, true); - } - - out.writeInt(myInitialProductionScanPerformed.size()); - for (String moduleName : myInitialProductionScanPerformed) { - IOUtil.writeString(moduleName, out); - getDelta(moduleName).save(out, false); - } - } - - public void load(DataInput in) throws IOException { - int testsDeltaCount = in.readInt(); - while (testsDeltaCount-- > 0) { - final String moduleName = IOUtil.readString(in); - getDelta(moduleName).load(in, true); - myInitialTestsScanPerformed.add(moduleName); - } - - int productionDeltaCount = in.readInt(); - while (productionDeltaCount-- > 0) { - final String moduleName = IOUtil.readString(in); - getDelta(moduleName).load(in, false); - myInitialProductionScanPerformed.add(moduleName); - } - } - - public void clearAll() { - myDeltas.clear(); - } - - public void init(String moduleName, final Collection deleteProduction, final Collection deletedTests, final Map> recompileProduction, final Map> recompileTests) { - getDelta(moduleName).init(deleteProduction, deletedTests, recompileProduction, recompileTests); - myInitialTestsScanPerformed.add(moduleName); - myInitialProductionScanPerformed.add(moduleName); - } - - public final void clearRecompile(final RootDescriptor rd) { - getDelta(rd.module).clearRecompile(rd.root, rd.isTestRoot); - } - - public boolean markDirty(@Nullable CompileContext context, final File file, final RootDescriptor rd, final @Nullable Timestamps tsStorage) throws IOException { - final FilesDelta mainDelta = getDelta(rd.module); - final boolean marked = mainDelta.markRecompile(rd.root, rd.isTestRoot, file); - if (marked && tsStorage != null) { - tsStorage.removeStamp(file); - } - return marked; - } - - public boolean markDirtyIfNotDeleted(@Nullable CompileContext context, - final File file, - final RootDescriptor rd, - final @Nullable Timestamps tsStorage) throws IOException { - final boolean marked = getDelta(rd.module).markRecompileIfNotDeleted(rd.root, rd.isTestRoot, file); - if (marked && tsStorage != null) { - tsStorage.removeStamp(file); - } - return marked; - } - - public void registerDeleted(final String moduleName, final File file, final boolean forTests, @Nullable Timestamps tsStorage) throws IOException { - getDelta(moduleName).addDeleted(file, forTests); - if (tsStorage != null) { - tsStorage.removeStamp(file); - } - } - - public Map> getSourcesToRecompile(@NotNull CompileContext context, final String moduleName, boolean forTests) { - return getDelta(moduleName).getSourcesToRecompile(forTests); - } - - public void clearDeletedPaths(final String moduleName, final boolean forTests) { - final FilesDelta delta = myDeltas.get(moduleName); - if (delta != null) { - delta.clearDeletedPaths(forTests); - } - } - - public Collection getAndClearDeletedPaths(final String moduleName, final boolean forTests) { - final FilesDelta delta = myDeltas.get(moduleName); - if (delta != null) { - return delta.getAndClearDeletedPaths(forTests); - } - return Collections.emptyList(); - } - - @NotNull - protected final FilesDelta getDelta(final String moduleName) { - synchronized (myDeltas) { - FilesDelta delta = myDeltas.get(moduleName); - if (delta == null) { - delta = new FilesDelta(); - myDeltas.put(moduleName, delta); - } - return delta; - } - } - - public boolean hasWorkToDo() { - for (Map.Entry entry : myDeltas.entrySet()) { - final String moduleName = entry.getKey(); - if (!myInitialProductionScanPerformed.contains(moduleName) || !myInitialTestsScanPerformed.contains(moduleName)) { - return true; - } - final FilesDelta delta = entry.getValue(); - if (delta.hasPathsToDelete() || delta.hasSourcesToRecompile()) { - return true; - } - } - return false; - } - - public boolean markInitialScanPerformed(final String moduleName, boolean forTests) { - return (forTests ? myInitialTestsScanPerformed : myInitialProductionScanPerformed).add(moduleName); - } -} +package org.jetbrains.jps.incremental.fs; + +import com.intellij.util.io.IOUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jps.incremental.CompileContext; +import org.jetbrains.jps.incremental.storage.Timestamps; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.File; +import java.io.IOException; +import java.util.*; + +/** + * @author Eugene Zhuravlev + * Date: 4/20/12 + */ +public class FSState { + private final Map myProductionDeltas = Collections.synchronizedMap(new HashMap()); + private final Map myTestDeltas = Collections.synchronizedMap(new HashMap()); + protected final Set myInitialTestsScanPerformed = Collections.synchronizedSet(new HashSet()); + protected final Set myInitialProductionScanPerformed = Collections.synchronizedSet(new HashSet()); + + public void save(DataOutput out) throws IOException { + out.writeInt(myInitialTestsScanPerformed.size()); + for (String moduleName : myInitialTestsScanPerformed) { + IOUtil.writeString(moduleName, out); + getDelta(moduleName, true).save(out); + } + + out.writeInt(myInitialProductionScanPerformed.size()); + for (String moduleName : myInitialProductionScanPerformed) { + IOUtil.writeString(moduleName, out); + getDelta(moduleName, false).save(out); + } + } + + public void load(DataInput in) throws IOException { + int testsDeltaCount = in.readInt(); + while (testsDeltaCount-- > 0) { + final String moduleName = IOUtil.readString(in); + getDelta(moduleName, true).load(in); + myInitialTestsScanPerformed.add(moduleName); + } + + int productionDeltaCount = in.readInt(); + while (productionDeltaCount-- > 0) { + final String moduleName = IOUtil.readString(in); + getDelta(moduleName, false).load(in); + myInitialProductionScanPerformed.add(moduleName); + } + } + + public void clearAll() { + myProductionDeltas.clear(); + myTestDeltas.clear(); + } + + public void init(String moduleName, final Collection deleteProduction, final Collection deletedTests, final Map> recompileProduction, final Map> recompileTests) { + getDelta(moduleName, true).init(deletedTests, recompileTests); + getDelta(moduleName, false).init(deleteProduction, recompileProduction); + myInitialTestsScanPerformed.add(moduleName); + myInitialProductionScanPerformed.add(moduleName); + } + + public final void clearRecompile(final RootDescriptor rd) { + getDelta(rd.module, rd.isTestRoot).clearRecompile(rd.root); + } + + public boolean markDirty(@Nullable CompileContext context, final File file, final RootDescriptor rd, final @Nullable Timestamps tsStorage) throws IOException { + final FilesDelta mainDelta = getDelta(rd.module, rd.isTestRoot); + final boolean marked = mainDelta.markRecompile(rd.root, file); + if (marked && tsStorage != null) { + tsStorage.removeStamp(file); + } + return marked; + } + + public boolean markDirtyIfNotDeleted(@Nullable CompileContext context, + final File file, + final RootDescriptor rd, + final @Nullable Timestamps tsStorage) throws IOException { + final boolean marked = getDelta(rd.module, rd.isTestRoot).markRecompileIfNotDeleted(rd.root, rd.isTestRoot, file); + if (marked && tsStorage != null) { + tsStorage.removeStamp(file); + } + return marked; + } + + public void registerDeleted(final String moduleName, final File file, final boolean forTests, @Nullable Timestamps tsStorage) throws IOException { + getDelta(moduleName, forTests).addDeleted(file); + if (tsStorage != null) { + tsStorage.removeStamp(file); + } + } + + public Map> getSourcesToRecompile(@NotNull CompileContext context, final String moduleName, boolean forTests) { + return getDelta(moduleName, forTests).getSourcesToRecompile(); + } + + public void clearDeletedPaths(final String moduleName, final boolean forTests) { + final FilesDelta delta = getDeltas(forTests).get(moduleName); + if (delta != null) { + delta.clearDeletedPaths(); + } + } + + private Map getDeltas(boolean forTests) { + return forTests ? myTestDeltas : myProductionDeltas; + } + + public Collection getAndClearDeletedPaths(final String moduleName, final boolean forTests) { + final FilesDelta delta = getDeltas(forTests).get(moduleName); + if (delta != null) { + return delta.getAndClearDeletedPaths(); + } + return Collections.emptyList(); + } + + @NotNull + protected final FilesDelta getDelta(final String moduleName, boolean forTests) { + Map deltas = getDeltas(forTests); + synchronized (deltas) { + FilesDelta delta = deltas.get(moduleName); + if (delta == null) { + delta = new FilesDelta(); + deltas.put(moduleName, delta); + } + return delta; + } + } + + public boolean hasWorkToDo() { + return hasWorkToDo(false) || hasWorkToDo(true); + } + + private boolean hasWorkToDo(final boolean forTests) { + for (Map.Entry entry : getDeltas(forTests).entrySet()) { + final String moduleName = entry.getKey(); + if (!myInitialProductionScanPerformed.contains(moduleName) || !myInitialTestsScanPerformed.contains(moduleName)) { + return true; + } + final FilesDelta delta = entry.getValue(); + if (delta.hasPathsToDelete() || delta.hasSourcesToRecompile()) { + return true; + } + } + return false; + } + + public boolean markInitialScanPerformed(final String moduleName, boolean forTests) { + return (forTests ? myInitialTestsScanPerformed : myInitialProductionScanPerformed).add(moduleName); + } +} diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FilesDelta.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FilesDelta.java index 6b1c5ec1a5f1..142ea03c047c 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FilesDelta.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FilesDelta.java @@ -1,216 +1,188 @@ -package org.jetbrains.jps.incremental.fs; - -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.util.io.IOUtil; -import gnu.trove.THashSet; -import gnu.trove.TObjectHashingStrategy; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.jps.incremental.Utils; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.File; -import java.io.IOException; -import java.util.*; - -/** @noinspection SynchronizationOnLocalVariableOrMethodParameter*/ -final class FilesDelta { - private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.fs.FilesDelta"); - - private final Set myDeletedProduction = Collections.synchronizedSet(new HashSet()); - private final Set myDeletedTests = Collections.synchronizedSet(new HashSet()); - private final Map> mySourcesToRecompile = Collections.synchronizedMap(new HashMap>()); // srcRoot -> set of sources - private final Map> myTestsToRecompile = Collections.synchronizedMap(new HashMap>()); // srcRoot -> set of sources - - public void save(DataOutput out, final boolean tests) throws IOException { - final Set deleted = tests? myDeletedTests : myDeletedProduction; - out.writeInt(deleted.size()); - for (String path : deleted) { - IOUtil.writeString(path, out); - } - final Map> recompile = tests? myTestsToRecompile : mySourcesToRecompile; - out.writeInt(recompile.size()); - for (Map.Entry> entry : recompile.entrySet()) { - final File root = entry.getKey(); - IOUtil.writeString(FileUtil.toSystemIndependentName(root.getPath()), out); - final Set files = entry.getValue(); - out.writeInt(files.size()); - for (File file : files) { - IOUtil.writeString(FileUtil.toSystemIndependentName(file.getPath()), out); - } - } - } - - public void load(DataInput in, final boolean tests) throws IOException { - final Set deleted = tests? myDeletedTests : myDeletedProduction; - deleted.clear(); - int deletedCount = in.readInt(); - while (deletedCount-- > 0) { - deleted.add(IOUtil.readString(in)); - } - final Map> recompile = tests? myTestsToRecompile : mySourcesToRecompile; - recompile.clear(); - int recompileCount = in.readInt(); - while (recompileCount-- > 0) { - final File root = new File(IOUtil.readString(in)); - Set files = recompile.get(root); - if (files == null) { - files = createSetOfFiles(); - recompile.put(root, files); - } - int filesCount = in.readInt(); - while (filesCount-- > 0) { - final File file = new File(IOUtil.readString(in)); - if (Utils.IS_TEST_MODE) { - LOG.info("Loaded " + file.getPath()); - } - files.add(file); - } - } - } - - - private static Set createSetOfFiles() { - return new THashSet(new TObjectHashingStrategy() { - @Override - public int computeHashCode(File file) { - return FileUtil.fileHashCode(file); - } - - @Override - public boolean equals(File f1, File f2) { - return FileUtil.filesEqual(f1, f2); - } - }); - } - - public void init(Collection deletedProduction, Collection deletedTests, Map> recompileProduction, Map> recompileTests) { - myDeletedProduction.clear(); - myDeletedProduction.addAll(deletedProduction); - myDeletedTests.clear(); - myDeletedTests.addAll(deletedTests); - mySourcesToRecompile.clear(); - mySourcesToRecompile.putAll(recompileProduction); - myTestsToRecompile.clear(); - myTestsToRecompile.putAll(recompileTests); - } - - - public boolean markRecompile(File root, boolean isTestRoot, File file) { - final boolean added = _addToRecompiled(root, isTestRoot, file); - if (added) { - final Set deleted = isTestRoot? myDeletedTests : myDeletedProduction; - synchronized (deleted) { - if (!deleted.isEmpty()) { // optimization - deleted.remove(FileUtil.toCanonicalPath(file.getPath())); - } - } - } - return added; - } - - public boolean markRecompileIfNotDeleted(File root, boolean isTestRoot, File file) { - final Set deleted = isTestRoot? myDeletedTests : myDeletedProduction; - final boolean isMarkedDeleted; - synchronized (deleted) { - isMarkedDeleted = !deleted.isEmpty() && deleted.contains(FileUtil.toCanonicalPath(file.getPath())); - } - if (!isMarkedDeleted) { - return _addToRecompiled(root, isTestRoot, file); - } - return false; - } - - private boolean _addToRecompiled(File root, boolean isTestRoot, File file) { - if (Utils.IS_TEST_MODE) { - LOG.info("Marking dirty: " + file.getPath()); - } - - final Map> toRecompile = isTestRoot ? myTestsToRecompile : mySourcesToRecompile; - Set files; - synchronized (toRecompile) { - files = toRecompile.get(root); - if (files == null) { - files = createSetOfFiles(); - toRecompile.put(root, files); - } - return files.add(file); - } - } - - public void addDeleted(File file, boolean isTest) { - // ensure the file is no more marked to recompilation - final Map> toRecompile = isTest ? myTestsToRecompile : mySourcesToRecompile; - synchronized (toRecompile) { - for (Set files : toRecompile.values()) { - files.remove(file); - } - } - final Set deleted = isTest? myDeletedTests : myDeletedProduction; - final String path = FileUtil.toCanonicalPath(file.getPath()); - deleted.add(path); - if (Utils.IS_TEST_MODE) { - LOG.info("Marking deleted: " + path); - } - } - - public void clearDeletedPaths(boolean isTest) { - final Set deleted = isTest? myDeletedTests : myDeletedProduction; - deleted.clear(); - } - - public Set getAndClearDeletedPaths(boolean isTest) { - final Set deleted = isTest? myDeletedTests : myDeletedProduction; - synchronized (deleted) { - try { - return new HashSet(deleted); - } - finally { - deleted.clear(); - } - } - } - - public Map> getSourcesToRecompile(boolean forTests) { - return forTests? myTestsToRecompile : mySourcesToRecompile; - } - - public boolean hasSourcesToRecompile() { - synchronized (myTestsToRecompile) { - if(!myTestsToRecompile.isEmpty()) { - for (Set files : myTestsToRecompile.values()) { - if (!files.isEmpty()) { - return true; - } - } - } - } - synchronized (mySourcesToRecompile) { - if(!mySourcesToRecompile.isEmpty()) { - for (Set files : mySourcesToRecompile.values()) { - if (!files.isEmpty()) { - return true; - } - } - } - } - return false; - } - - public boolean hasPathsToDelete() { - return !myDeletedTests.isEmpty() || !myDeletedProduction.isEmpty(); - } - - public Set getDeletedPaths(boolean isTest) { - final Set deleted = isTest ? myDeletedTests : myDeletedProduction; - synchronized (deleted) { - return deleted.isEmpty()? Collections.emptySet() : new HashSet(deleted); - } - } - - @Nullable - public Set clearRecompile(File root, boolean isTestRoot) { - return isTestRoot? myTestsToRecompile.remove(root) : mySourcesToRecompile.remove(root); - } -} +package org.jetbrains.jps.incremental.fs; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.util.io.IOUtil; +import gnu.trove.THashSet; +import gnu.trove.TObjectHashingStrategy; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jps.incremental.Utils; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.File; +import java.io.IOException; +import java.util.*; + +/** @noinspection SynchronizationOnLocalVariableOrMethodParameter*/ +final class FilesDelta { + private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.fs.FilesDelta"); + + private final Set myDeletedPaths = Collections.synchronizedSet(new HashSet()); + private final Map> myFilesToRecompile = Collections.synchronizedMap(new HashMap>()); // srcRoot -> set of sources + + public void save(DataOutput out) throws IOException { + out.writeInt(myDeletedPaths.size()); + for (String path : myDeletedPaths) { + IOUtil.writeString(path, out); + } + out.writeInt(myFilesToRecompile.size()); + for (Map.Entry> entry : myFilesToRecompile.entrySet()) { + final File root = entry.getKey(); + IOUtil.writeString(FileUtil.toSystemIndependentName(root.getPath()), out); + final Set files = entry.getValue(); + out.writeInt(files.size()); + for (File file : files) { + IOUtil.writeString(FileUtil.toSystemIndependentName(file.getPath()), out); + } + } + } + + public void load(DataInput in) throws IOException { + myDeletedPaths.clear(); + int deletedCount = in.readInt(); + while (deletedCount-- > 0) { + myDeletedPaths.add(IOUtil.readString(in)); + } + myFilesToRecompile.clear(); + int recompileCount = in.readInt(); + while (recompileCount-- > 0) { + final File root = new File(IOUtil.readString(in)); + Set files = myFilesToRecompile.get(root); + if (files == null) { + files = createSetOfFiles(); + myFilesToRecompile.put(root, files); + } + int filesCount = in.readInt(); + while (filesCount-- > 0) { + final File file = new File(IOUtil.readString(in)); + if (Utils.IS_TEST_MODE) { + LOG.info("Loaded " + file.getPath()); + } + files.add(file); + } + } + } + + + private static Set createSetOfFiles() { + return new THashSet(new TObjectHashingStrategy() { + @Override + public int computeHashCode(File file) { + return FileUtil.fileHashCode(file); + } + + @Override + public boolean equals(File f1, File f2) { + return FileUtil.filesEqual(f1, f2); + } + }); + } + + public void init(Collection deleted, Map> recompile) { + myDeletedPaths.clear(); + myDeletedPaths.addAll(deleted); + myFilesToRecompile.clear(); + myFilesToRecompile.putAll(recompile); + } + + public boolean markRecompile(File root, File file) { + final boolean added = _addToRecompiled(root, file); + if (added) { + synchronized (myDeletedPaths) { + if (!myDeletedPaths.isEmpty()) { // optimization + myDeletedPaths.remove(FileUtil.toCanonicalPath(file.getPath())); + } + } + } + return added; + } + + public boolean markRecompileIfNotDeleted(File root, boolean isTestRoot, File file) { + final boolean isMarkedDeleted; + synchronized (myDeletedPaths) { + isMarkedDeleted = !myDeletedPaths.isEmpty() && myDeletedPaths.contains(FileUtil.toCanonicalPath(file.getPath())); + } + if (!isMarkedDeleted) { + return _addToRecompiled(root, file); + } + return false; + } + + private boolean _addToRecompiled(File root, File file) { + if (Utils.IS_TEST_MODE) { + LOG.info("Marking dirty: " + file.getPath()); + } + + Set files; + synchronized (myFilesToRecompile) { + files = myFilesToRecompile.get(root); + if (files == null) { + files = createSetOfFiles(); + myFilesToRecompile.put(root, files); + } + return files.add(file); + } + } + + public void addDeleted(File file) { + // ensure the file is no more marked to recompilation + synchronized (myFilesToRecompile) { + for (Set files : myFilesToRecompile.values()) { + files.remove(file); + } + } + final String path = FileUtil.toCanonicalPath(file.getPath()); + myDeletedPaths.add(path); + if (Utils.IS_TEST_MODE) { + LOG.info("Marking deleted: " + path); + } + } + + public void clearDeletedPaths() { + myDeletedPaths.clear(); + } + + public Set getAndClearDeletedPaths() { + synchronized (myDeletedPaths) { + try { + return new HashSet(myDeletedPaths); + } + finally { + myDeletedPaths.clear(); + } + } + } + + public Map> getSourcesToRecompile() { + return myFilesToRecompile; + } + + public boolean hasSourcesToRecompile() { + synchronized (myFilesToRecompile) { + if(!myFilesToRecompile.isEmpty()) { + for (Set files : myFilesToRecompile.values()) { + if (!files.isEmpty()) { + return true; + } + } + } + } + return false; + } + + public boolean hasPathsToDelete() { + return !myDeletedPaths.isEmpty(); + } + + public Set getDeletedPaths() { + synchronized (myDeletedPaths) { + return myDeletedPaths.isEmpty()? Collections.emptySet() : new HashSet(myDeletedPaths); + } + } + + @Nullable + public Set clearRecompile(File root) { + return myFilesToRecompile.remove(root); + } +}