Package dtool.engine

Source Code of dtool.engine.ModuleParseCache$ModuleEntry

/*******************************************************************************
* Copyright (c) 2014, 2014 Bruno Medeiros and other Contributors.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     Bruno Medeiros - initial API and implementation
*******************************************************************************/
package dtool.engine;

import static melnorme.utilbox.core.Assert.AssertNamespace.assertNotNull;
import static melnorme.utilbox.core.Assert.AssertNamespace.assertTrue;
import static melnorme.utilbox.core.CoreUtil.areEqual;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;

import melnorme.utilbox.misc.FileUtil;
import melnorme.utilbox.misc.StringUtil;
import dtool.ast.definitions.Module;
import dtool.parser.DeeParser;
import dtool.parser.DeeParserResult.ParsedModule;

/**
* Manages a cache of parsed modules, indexed by file path
*/
public class ModuleParseCache {
 
  protected final DToolServer dtoolServer;
 
  public ModuleParseCache(DToolServer dtoolServer) {
    this.dtoolServer = dtoolServer;
  }
 
  protected final HashMap<String, ModuleEntry> cache = new HashMap<>();
 
  /* -----------------  ----------------- */
 
  public ParsedModule getParsedModule(Path filePath) throws ParseSourceException {
    ModuleEntry entry = getEntry(filePath);
    try {
      return assertNotNull(entry.getParsedModule());
    } catch (IOException e) {
      throw new ParseSourceException(e);
    }
  }
 
  public ParsedModule getExistingParsedModule(Path filePath) {
    // TODO: don't create entry
    return getEntry(filePath).parsedModule;
  }
 
  // util method
  public Module getExistingParsedModuleNode(Path filePath) {
    ParsedModule parsedModule = getExistingParsedModule(filePath);
    return parsedModule == null ? null : parsedModule.module;
  }
 
  public ParsedModule setWorkingCopyAndGetParsedModule(Path filePath, String source) {
    return parseModuleWithNewSource(filePath, source);
  }
 
  public static class ParseSourceException extends Exception {
   
    private static final long serialVersionUID = 1L;
   
    public ParseSourceException() {
    }
   
    public ParseSourceException(Throwable cause) {
      super(cause);
    }
  }
 
  /* -----------------  ----------------- */
 
  protected Path validatePath(Path filePath) {
    assertNotNull(filePath);
    //filePath can be relative
    //assertTrue(filePath.isAbsolute());
    assertTrue(filePath.getNameCount() > 0);
    filePath = filePath.normalize();
    return filePath;
  }
 
  protected String getKeyFromPath(Path filePath) {
    return filePath.toString();
  }
 
  public ModuleEntry getEntry(Path filePath) {
    filePath = validatePath(filePath);
    String key = getKeyFromPath(filePath);
   
    synchronized(this) {
      ModuleEntry entry = cache.get(key);
      if(entry == null) {
        entry = new ModuleEntry(filePath);
        cache.put(key, entry);
      }
      return entry;
    }
  }
 
  protected ParsedModule parseModuleWithNewSource(Path filePath, String source) {
    assertNotNull(source);
   
    return getEntry(filePath).getParsedModuleWithWorkingCopySource(source);
  }
 
  public void discardWorkingCopy(Path filePath) {
    filePath = validatePath(filePath);
    String key = getKeyFromPath(filePath);
    ModuleEntry entry = cache.get(key);
    if(entry != null) {
      entry.discardWorkingCopy();
    }
  }
 
  public class ModuleEntry {
   
    public static final int FILE_MODIFY_TIME__GRANULARITY_Millis = 1_000_000;
   
    protected final Path filePath;
   
    protected String source = null;
    protected volatile boolean isWorkingCopy = false;
    protected BasicFileAttributes sourceFileSyncAttributes;
    protected volatile ParsedModule parsedModule = null;
   
    public ModuleEntry(Path filePath) {
      this.filePath = filePath;
      assertTrue(filePath != null);
    }
   
    public boolean isWorkingCopy() {
      return isWorkingCopy;
    }
   
    protected synchronized boolean isStale() {
      if(isWorkingCopy) {
        assertNotNull(source);
        return false;
      }
      if(source == null || parsedModule == null) {
        return true;
      }
     
      BasicFileAttributes newAttributes;
      try {
        newAttributes = Files.readAttributes(filePath, BasicFileAttributes.class);
      } catch (IOException e) {
        return true;
      }
     
      return hasBeenModified(sourceFileSyncAttributes, newAttributes);
    }
   
    public synchronized ParsedModule getParsedModule() throws FileNotFoundException, IOException {
      if(isStale()) {
        readSource();
      }     
      return doGetParsedModule(source);
    }
   
    protected void readSource() throws IOException, FileNotFoundException {
      sourceFileSyncAttributes = Files.readAttributes(filePath, BasicFileAttributes.class);
      String fileContents = FileUtil.readStringFromFile(filePath, StringUtil.UTF8); // TODO: detect encoding
      setNewSource(fileContents);
    }
   
    protected void setNewSource(String newSource) {
      if(!areEqual(source, newSource)) {
        source = newSource;
        parsedModule = null;
      }
    }
   
    public synchronized ParsedModule getParsedModuleIfNotStale(boolean attemptSourceRefresh) {
      if(!isStale()) {
        return parsedModule;   
      }
     
      if(attemptSourceRefresh) {
        try {
          readSource();
          return parsedModule; // parsedModule will remain the same if the source didn't change.
        } catch (IOException e) {
          return null;
        }
      } else {
        return null;
      }
    }
   
    public synchronized ParsedModule getParsedModuleWithWorkingCopySource(String newSource) {
      assertNotNull(newSource);
      setNewSource(newSource);
      isWorkingCopy = true;
      dtoolServer.logMessage("ParseCache: Set working copy: " + filePath);
      return doGetParsedModule(newSource);
    }
   
    protected synchronized void discardWorkingCopy() {
      if(isWorkingCopy) {
        isWorkingCopy = false;
        sourceFileSyncAttributes = null; // This will invalidate current source
        dtoolServer.logMessage("ParseCache: Discarded working copy: " + filePath);
      }
    }
   
    protected ParsedModule doGetParsedModule(String source) {
      if(parsedModule == null) {
        parsedModule = DeeParser.parseSource(source, filePath);
        dtoolServer.logMessage("ParseCache: Parsed module " + filePath +
          (isWorkingCopy ? " [WorkingCopy]" : ""));
      }
      return parsedModule;
    }
   
  }
 
  public static boolean hasBeenModified(BasicFileAttributes originalAttributes, BasicFileAttributes newAttributes) {
    return
        originalAttributes == null ||
        originalAttributes.lastModifiedTime().toMillis() != newAttributes.lastModifiedTime().toMillis() ||
        originalAttributes.size() != newAttributes.size();
  }

}
TOP

Related Classes of dtool.engine.ModuleParseCache$ModuleEntry

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.