package jetbrains.mps.internal.make.runtime.util; /*Generated by MPS */ import jetbrains.mps.vfs.IFile; import java.util.Map; import java.util.List; import jetbrains.mps.internal.collections.runtime.MapSequence; import java.util.HashMap; import jetbrains.mps.internal.collections.runtime.Sequence; import jetbrains.mps.internal.collections.runtime.ISelector; import jetbrains.mps.internal.collections.runtime.ListSequence; import java.util.ArrayList; import java.util.Queue; import jetbrains.mps.internal.collections.runtime.QueueSequence; import java.util.LinkedList; import java.util.Arrays; import jetbrains.mps.baseLanguage.tuples.runtime.Tuples; import jetbrains.mps.baseLanguage.tuples.runtime.MultiTuple; import org.jetbrains.mps.openapi.model.SModel; import jetbrains.mps.generator.info.GeneratorPathsComponent; import java.util.Set; import jetbrains.mps.internal.collections.runtime.SetSequence; import java.util.HashSet; public class StaleFilesCollector { private IFile rootDir; private Map<IFile, List<IFile>> generatedChildren = MapSequence.fromMap(new HashMap<IFile, List<IFile>>()); public StaleFilesCollector(IFile rootDir) { this.rootDir = rootDir; } private List<IFile> collectFilesToDelete(Iterable<IFile> filesToKeep) { String[] pathsToKeep = Sequence.fromIterable(filesToKeep).select(new ISelector<IFile, String>() { public String select(IFile f) { return (f.isDirectory() ? DirUtil.normalizeAsDir(f.getPath()) : DirUtil.normalize(f.getPath())); } }).sort(new ISelector<String, String>() { public String select(String p) { return p; } }, true).toListSequence().toGenericArray(String.class); List<IFile> filesToDelete = ListSequence.fromList(new ArrayList<IFile>()); Queue<IFile> dirQueue = QueueSequence.fromQueueAndArray(new LinkedList<IFile>(), rootDir); while (QueueSequence.fromQueue(dirQueue).isNotEmpty()) { IFile dir = QueueSequence.fromQueue(dirQueue).removeFirstElement(); String dirpath = DirUtil.normalizeAsDir(dir.getPath()); int diridx = Arrays.binarySearch(pathsToKeep, dirpath); for (Tuples._2<IFile, String> fileAndPath : Sequence.fromIterable(getChildren(dir)).select(new ISelector<IFile, Tuples._2<IFile, String>>() { public Tuples._2<IFile, String> select(IFile f) { return MultiTuple.<IFile,String>from(f, DirUtil.normalize(f.getPath())); } }).sort(new ISelector<Tuples._2<IFile, String>, String>() { public String select(Tuples._2<IFile, String> t) { return t._1(); } }, true)) { if (fileAndPath._0().isDirectory()) { int fidx = Arrays.binarySearch(pathsToKeep, DirUtil.normalizeAsDir(fileAndPath._1())); fidx = (fidx < 0 ? -1 - fidx : fidx); if (fidx >= pathsToKeep.length || !(DirUtil.startsWith(pathsToKeep[fidx], fileAndPath._1()))) { ListSequence.fromList(filesToDelete).addElement(fileAndPath._0()); if (fidx >= pathsToKeep.length) { break; } } else if (fidx < pathsToKeep.length) { QueueSequence.fromQueue(dirQueue).addLastElement(fileAndPath._0()); } } else { int fidx = Arrays.binarySearch(pathsToKeep, fileAndPath._1()); if (fidx < 0 && diridx < 0) { ListSequence.fromList(filesToDelete).addElement(fileAndPath._0()); } } } } return filesToDelete; } /** * Read cached state of generated files, if any, assuming files were generated under rootDir. * * The code is intended to handle case when we generate into a root with foreign files we shall keep. * Generally, all the files under rootDir might need deletion (except those explicitly written/kept). * Files left after excluding those touched are additionally filtered through 'foreign' roots in a way * that we consider only generated files under output root (intersect in getChildren). */ public void recordGeneratedChildren(SModel model) { List<IFile> genChildren = GeneratorPathsComponent.getInstance().getGeneratedChildren(rootDir, model); if (ListSequence.fromList(genChildren).isNotEmpty()) { MapSequence.fromMap(generatedChildren).put(rootDir, ListSequence.fromListWithValues(new ArrayList<IFile>(), genChildren)); } } public void updateDelta(FilesDelta delta) { final Set<IFile> filesToKeep = SetSequence.fromSet(new HashSet<IFile>()); delta.acceptVisitor(new FilesDelta.Visitor() { @Override public boolean acceptWritten(IFile file) { SetSequence.fromSet(filesToKeep).addElement(file); return true; } @Override public boolean acceptKept(IFile file) { SetSequence.fromSet(filesToKeep).addElement(file); return true; } }); for (IFile f : collectFilesToDelete(filesToKeep)) { delta.stale(f); } } private Iterable<IFile> getChildren(IFile dir) { Iterable<IFile> realChilren = (Iterable<IFile>) dir.getChildren(); if (GeneratorPathsComponent.getInstance().isForeign(dir)) { List<IFile> genChildren = MapSequence.fromMap(generatedChildren).get(dir); return ListSequence.fromList(genChildren).intersect(Sequence.fromIterable(realChilren)); } return realChilren; } }