Package org.sugarj.driver

Source Code of org.sugarj.driver.Result

package org.sugarj.driver;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.spoofax.interpreter.terms.IStrategoTerm;
import org.spoofax.jsglr.shared.BadTokenException;
import org.sugarj.common.ATermCommands;
import org.sugarj.common.Environment;
import org.sugarj.common.FileCommands;
import org.sugarj.common.path.Path;
import org.sugarj.common.path.RelativePath;
import org.sugarj.util.AppendableObjectOutputStream;
import org.sugarj.util.Pair;

/**
* @author Sebastian Erdweg <seba at informatik uni-marburg de>
*/
public class Result {
  /**
   * Caching for results
   */
  private static HashMap<Path, Result> results = new HashMap<Path, Result>();
   
  /**
   * Path and hash of the disk-stored version of this result.
   * If the result was not stored yet, both variables are null.
   */
  private Path persistentPath;
  private Integer persistentHash = null;
 
  /**
   *  This is a parse result if it was produced during parsing.
   */
  private Path parseResultPath;
 
  private Map<Path, Integer> dependencies = new HashMap<Path, Integer>();
  private Set<Path> circularDependencies = new HashSet<Path>();
  private Map<Path, Integer> generatedFileHashes = new HashMap<Path, Integer>();
  private Map<RelativePath, Integer> dependingFileHashes = new HashMap<RelativePath, Integer>();
  private Set<IStrategoTerm> editorServices = new HashSet<IStrategoTerm>();
  private List<String> collectedErrors = new LinkedList<String>();
  private Set<BadTokenException> parseErrors = new HashSet<BadTokenException>();
  private IStrategoTerm sugaredSyntaxTree = null;
  private IStrategoTerm desugaredSyntaxTree;
  private Path parseTableFile;
  private Path desugaringsFile;
  private RelativePath sourceFile;
  private Integer sourceFileHash;
  private Map<Path, Integer> allDependentFiles = new HashMap<Path, Integer>();
  private boolean failed = false;
  private Path generationLog;
 
  /**
   * deferred source files (*.sugj) ->
   * to-be-compiled source files (e.g., *.java + generated SourceFileContent)
   */
  private Map<Path, Pair<Path, String>> deferredSourceFiles = new HashMap<Path, Pair<Path, String>>();
 
  public Result() {
  }
 
  public Result(Path parseResultPath) {
    this.parseResultPath = parseResultPath;
  }
 
  public void addFileDependency(RelativePath file) {
    int hash;
    try {
      hash = FileCommands.fileHash(file);
    } catch (IOException e) {
      e.printStackTrace();
      hash = -1;
    }
    dependingFileHashes.put(file, hash);
    allDependentFiles.put(file, hash);
  }
 
  void addCircularDependency(Path depFile) throws IOException {
    circularDependencies.add(depFile);
  }
 
  public void addDependency(Result result) throws IOException {
    if (result.persistentPath != null && !result.hasPersistentVersionChanged())
      dependencies.put(result.persistentPath, result.persistentHash);
    allDependentFiles.putAll(result.getTransitiveFileDependencies());
  }
 
  public void addDependency(Path depFile) throws IOException {
    dependencies.put(depFile, FileCommands.fileHash(depFile));
    Result result = readDependencyFile(depFile);
    allDependentFiles.putAll(result.getTransitiveFileDependencies());
  }
 
  public boolean hasDependency(Path otherDep, Environment env) throws IOException {
    if (dependencies.containsKey(otherDep))
      return true;
    for (Path dep : dependencies.keySet())
      if (Result.readDependencyFile(dep).hasDependency(otherDep, env))
        return true;
    return false;
  }
 
  public Map<RelativePath, Integer> getFileDependencies() {
    return dependingFileHashes;
  }

  public Map<Path, Integer> getTransitiveFileDependencies() throws IOException {
    if (allDependentFiles == null) {
      Map<Path, Integer> deps = new HashMap<Path, Integer>();
      deps.putAll(generatedFileHashes);
      deps.putAll(dependingFileHashes);
      for (Path depFile : dependencies.keySet())
        deps.putAll(readDependencyFile(depFile).getTransitiveFileDependencies());
      synchronized(this) { allDependentFiles = deps; }
    }

    return allDependentFiles;
  }

  public Collection<Path> getCircularFileDependencies(Environment env) throws IOException {
    assert persistentPath != null;
   
    Set<Path> dependencies = new HashSet<Path>();
    Set<Path> visited = new HashSet<Path>();
    LinkedList<Path> queue = new LinkedList<Path>();
    queue.add(persistentPath);
    visited.add(persistentPath);
   
    while (!queue.isEmpty()) {
      Path dep = queue.pop();
      Result res = readDependencyFile(dep);
     
      for (Path p : res.generatedFileHashes.keySet())
        if (!dependencies.contains(p) && FileCommands.exists(p))
          dependencies.add(p);
      for (Path p : res.dependingFileHashes.keySet())
        if (!dependencies.contains(p) && FileCommands.exists(p))
          dependencies.add(p);
     
      for (Path nextDep : res.dependencies.keySet())
        if (!visited.contains(nextDep)) {
          queue.addFirst(nextDep);
          visited.add(dep);
        }
      for (Path nextDep : res.circularDependencies)
        if (!visited.contains(nextDep)) {
          queue.addFirst(nextDep);
          visited.add(dep);
        }
    }
   
    return dependencies;
  }
 
  public Set<Path> getDirectlyGeneratedFiles() {
    return generatedFileHashes.keySet();
  }
 
  public void setGenerationLog(Path file) {
    this.generationLog = file;
  }
 
  public Path getGenerationLog() {
    return generationLog;
  }
 
  private void logGeneration(Object o) throws IOException {
    if (generationLog != null) {
      boolean exists = FileCommands.exists(generationLog);
      if (!exists)
        FileCommands.createFile(generationLog);
      ObjectOutputStream oos = exists ? new AppendableObjectOutputStream(new FileOutputStream(generationLog.getFile(), true))
      : new ObjectOutputStream(new FileOutputStream(generationLog.getFile()));
      try {
        oos.writeObject(o);
      } finally {
        oos.close();
      }
    }
  }

  public void generateFile(RelativePath file, String content) throws IOException {
    FileCommands.writeToFile(file, content);
    int hash = FileCommands.fileHash(file);
    logFileGeneration(file, hash);
  }

  private void logFileGeneration(Path file, int hash) throws IOException {
    generatedFileHashes.put(file, hash);
    allDependentFiles.put(file, hash);
    if (file instanceof RelativePath)
      dependingFileHashes.put((RelativePath) file, hash);
    logGeneration(file);
  }

  public void addEditorService(IStrategoTerm service) {
    editorServices.add(service);
  }
 
  public Set<IStrategoTerm> getEditorServices() {
    return editorServices;
  }
 
  public boolean hasPersistentVersionChanged() throws IOException {
    return persistentPath != null &&
           persistentHash != null &&
           FileCommands.fileHash(persistentPath) != persistentHash;
  }
 
  public boolean hasSourceFileChanged(Path inputFile) throws IOException {
    return inputFile == null || hasSourceFileChanged(FileCommands.fileHash(inputFile));
  }
 
  private boolean hasSourceFileChanged(int inputHash) {
    return sourceFileHash == null || inputHash != sourceFileHash;
  }
 
  public boolean isUpToDateShallow(Path inputFile, Environment env) throws IOException {
    return isUpToDateShallow(FileCommands.fileHash(inputFile), env);
  }
 
  public boolean isUpToDate(Environment env) throws IOException {
    return isUpToDate(persistentPath, env);
  }
 
  public boolean isUpToDate(Path inputFile, Environment env) throws IOException {
    return isUpToDate(FileCommands.fileHash(inputFile), env);
  }

  private boolean isUpToDateShallow(int inputHash, Environment env) throws IOException {
     if (hasPersistentVersionChanged())
      return false;
   
    if (hasSourceFileChanged(inputHash))
      return false;
   
    if (desugaringsFile != null && !FileCommands.exists(desugaringsFile))
      return false;
   
    for (Entry<Path, Integer> entry : generatedFileHashes.entrySet())
      if (FileCommands.fileHash(entry.getKey()) != entry.getValue())
        return false;

    for (Entry<RelativePath, Integer> entry : dependingFileHashes.entrySet())
      if (FileCommands.fileHash(entry.getKey()) != entry.getValue())
        return false;

    for (Entry<Path, Integer> entry : dependencies.entrySet())
      if (FileCommands.fileHash(entry.getKey()) != entry.getValue())
        return false;

    return true;
  }

  public boolean isUpToDate(int inputHash, Environment env) throws IOException {
    if (!isUpToDateShallow(inputHash, env))
      return false;

    Set<Path> checked = new HashSet<Path>();
    Set<Path> dependends = new HashSet<Path>(dependencies.keySet());
   
    while (!dependends.isEmpty()) {
      Path path = dependends.iterator().next();
      dependends.remove(path);
     
      if (checked.contains(path))
        continue;
     
      Result r = Result.readDependencyFile(path);
      if (!isParseResult() && r.isParseResult())
        // rebuild if compiled-result dependency to parsed-result dependency
        return false;
      if (r == null || !r.isUpToDateShallow(r.getSourceFile(), env))
        return false;
     
      dependends.addAll(r.dependencies.keySet());
      checked.add(path);
    }

    return true;
  }
 
  public void logError(String error) {
    collectedErrors.add(error);
  }
 
  public List<String> getCollectedErrors() {
    return collectedErrors;
  }
 
  public void logParseError(BadTokenException e) {
    parseErrors.add(e)
  }
 
  public Set<BadTokenException> getParseErrors() {
    return parseErrors;
  }
 
  public void setSugaredSyntaxTree(IStrategoTerm sugaredSyntaxTree) {
    this.sugaredSyntaxTree = sugaredSyntaxTree;
  }
 
  public IStrategoTerm getSugaredSyntaxTree() {
    return sugaredSyntaxTree;
  }
 
  public void setDesugaredSyntaxTree(IStrategoTerm desugaredSyntaxTree) {
    this.desugaredSyntaxTree = desugaredSyntaxTree;
  }
 
  public IStrategoTerm getDesugaredSyntaxTree() {
    return desugaredSyntaxTree;
  }
 
  void delegateCompilation(Result delegate, Path compileFile, String source, boolean hasNonBaseDec) {
    delegate.deferredSourceFiles.putAll(deferredSourceFiles);
    if (!source.isEmpty() || hasNonBaseDec)
      delegate.deferredSourceFiles.put(sourceFile, Pair.create(compileFile, source));
  }
 
  boolean isDelegateOf(Path compileFile) {
    return deferredSourceFiles.containsKey(compileFile);
  }
 
  void resetDelegation() {
    deferredSourceFiles.clear();
  }
 
  public void registerParseTable(Path tbl) {
    this.parseTableFile = tbl;
  }
 
  public Path getParseTable() {
    return parseTableFile;
  }
 
  public void registerEditorDesugarings(Path jarfile) throws IOException {
    desugaringsFile = jarfile;
    editorServices = new HashSet<IStrategoTerm>(ATermCommands.registerSemanticProvider(editorServices, jarfile));
  }
 
  public Path getDesugaringsFile() {
    return desugaringsFile;
  }
 
  private void setPersistentPath(Path dep) throws IOException {
    persistentPath = dep;
    persistentHash = FileCommands.fileHash(dep);
  }
 
  public Path getPersistentPath() {
    return persistentPath;
  }
 
  public void setSourceFile(RelativePath sourceFile, int sourceFileHash) {
    this.sourceFile = sourceFile;
    this.sourceFileHash = sourceFileHash;
  }

  public RelativePath getSourceFile() {
    return sourceFile;
  }
 
  public boolean hasFailed() {
    return failed;
  }
 
  public void setFailed(boolean hasFailed) {
    this.failed = hasFailed;
  }
 
  public boolean isGenerated() {
    return sourceFile != null && "model".equals(FileCommands.getExtension(sourceFile));
  }

  public Map<Path, Pair<Path, String>> getDeferredSourceFiles() {
    return deferredSourceFiles;
  }

  public Map<Path, Integer> getGeneratedFileHashes() {
    return generatedFileHashes;
  }

  public boolean isParseResult() {
    return parseResultPath != null;
  }
 
  public Path getParseResultPath() {
    return parseResultPath;
  }
 
  public void rewriteDependencyFile() throws IOException {
    if (persistentPath == null)
      throw new IllegalStateException("Result not previously written to file.");
    writeDependencyFile(persistentPath);
  }
 
  public void writeDependencyFile(Path dep) throws IOException {
    logGeneration(dep);

    ObjectOutputStream oos = null;
   
    try {
      FileCommands.createFile(dep);
      oos = new ObjectOutputStream(new FileOutputStream(dep.getFile()));
     
      oos.writeObject(parseResultPath);
     
      oos.writeObject(sourceFile);
      oos.writeInt(sourceFileHash);
     
      oos.writeObject(dependencies);
      oos.writeObject(circularDependencies);
      oos.writeObject(generatedFileHashes);
      oos.writeObject(dependingFileHashes);
     
      oos.writeObject(deferredSourceFiles);
     
      cacheInMemory(dep, this);
    } finally {
      if (oos != null)
        oos.close();
    }
   
    setPersistentPath(dep);
  }
 
  @SuppressWarnings("unchecked")
  public static Result readDependencyFile(Path dep) throws IOException {
    Result result = getCachedResult(dep);
    if (result != null)
      return result;
   
    result = new Result();
    result.allDependentFiles = null;
    ObjectInputStream ois = null;
   
    try {
      ois = new ObjectInputStream(new FileInputStream(dep.getFile()));
     
      result.parseResultPath = (Path) ois.readObject();
     
      result.sourceFile = (RelativePath) ois.readObject();
      result.sourceFileHash = ois.readInt();
     
      result.dependencies = (Map<Path, Integer>) ois.readObject();
      result.circularDependencies = (Set<Path>) ois.readObject();
      result.generatedFileHashes = (Map<Path, Integer>) ois.readObject();
      result.dependingFileHashes = (Map<RelativePath, Integer>) ois.readObject();

      result.deferredSourceFiles = (Map<Path, Pair<Path, String>>) ois.readObject();
     
      cacheInMemory(dep, result);
    } catch (FileNotFoundException e) {
      return null;
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
      throw new IOException(e);
    } catch (Exception e) {
      return null;
    } finally {
      if (ois != null)
        ois.close();
    }
   
    result.setPersistentPath(dep);
    return result;
  }
 
  /**
   * Moves this result and the files generated by this result to the given target directory.
   * @return the moved result.
   */
  public Result moveTo(Path targetDir, boolean isParseResult) throws IOException {
    Result res = new Result(isParseResult ? targetDir : null);

    res.dependencies = new HashMap<Path, Integer>();
    for (Path dep : dependencies.keySet()) {
      Result other = readDependencyFile(dep);
      if (other.isParseResult())
        other.moveTo(targetDir, isParseResult);
      res.addDependency(other);
    }
   
    res.circularDependencies = circularDependencies;
    res.dependingFileHashes = dependingFileHashes;
    res.collectedErrors = collectedErrors;
    res.parseErrors = parseErrors;
    res.sugaredSyntaxTree = sugaredSyntaxTree;
    res.sourceFile = sourceFile;
    res.sourceFileHash = sourceFileHash;
    res.allDependentFiles = allDependentFiles;
    res.failed = failed;
    res.deferredSourceFiles = deferredSourceFiles;
    res.editorServices = editorServices;
   
    res.desugaringsFile = FileCommands.tryCopyFile(parseResultPath, targetDir, desugaringsFile);
    res.parseTableFile = FileCommands.tryCopyFile(parseResultPath, targetDir, parseTableFile);
    res.generationLog = FileCommands.tryCopyFile(parseResultPath, targetDir, generationLog);

    res.generatedFileHashes = new HashMap<Path, Integer>(generatedFileHashes.size());
    for (Entry<Path, Integer> e : generatedFileHashes.entrySet()) {
      Path p = FileCommands.tryCopyFile(parseResultPath, targetDir, e.getKey());
      res.logFileGeneration(p, FileCommands.fileHash(p));
    }
   
    RelativePath wasDep = FileCommands.getRelativePath(parseResultPath, persistentPath);
    Path dep = persistentPath;
    if (wasDep != null)
      dep = new RelativePath(targetDir, wasDep.getRelativePath());
   
    res.writeDependencyFile(dep);
   
    return res;
  }
 
  public static void cacheInMemory(Path dep, Result result) {
    synchronized (results) {
      results.put(dep, result);
//      results.put(dep, new SoftReference<Result>(result));
    }
  }
 
  public static Result getCachedResult(Path dep) throws IOException {
    Result result = null;
    synchronized (results) {
      result = results.get(dep);
//      SoftReference<Result> ref = results.get(dep);
//      if (ref != null)
//        result = ref.get();
    }
    if (result != null && !result.hasPersistentVersionChanged())
      return result;
    return null;
  }

}
TOP

Related Classes of org.sugarj.driver.Result

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.