dtool.engine.ModuleParseCache.java Source code

Java tutorial

Introduction

Here is the source code for dtool.engine.ModuleParseCache.java

Source

/*******************************************************************************
 * 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 dtool.ast.definitions.Module;
import dtool.parser.DeeParser;
import dtool.parser.DeeParserResult.ParsedModule;
import melnorme.utilbox.misc.FileUtil;
import melnorme.utilbox.misc.StringUtil;
import org.apache.commons.io.monitor.FileEntry;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;

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

/**
 * 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<String, ModuleEntry>();

    /* -----------------  ----------------- */

    public ParsedModule getParsedModule(File filePath) throws ParseSourceException {
        ModuleEntry entry = getEntry(filePath);
        try {
            return assertNotNull(entry.getParsedModule());
        } catch (IOException e) {
            throw new ParseSourceException(e);
        }
    }

    public ParsedModule getExistingParsedModule(File filePath) {
        // TODO: don't create entry
        return getEntry(filePath).parsedModule;
    }

    // util method
    public Module getExistingParsedModuleNode(File filePath) {
        ParsedModule parsedModule = getExistingParsedModule(filePath);
        return parsedModule == null ? null : parsedModule.module;
    }

    public ParsedModule setWorkingCopyAndGetParsedModule(File 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 File validatePath(File filePath) {
        assertNotNull(filePath);
        //filePath can be relative
        //assertTrue(filePath.isAbsolute());
        assertTrue(FileUtil.getNameCount(filePath) > 0);
        filePath = new File(filePath.toURI().normalize());
        return filePath;
    }

    protected String getKeyFromPath(File filePath) {
        return filePath.toString();
    }

    public ModuleEntry getEntry(File 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(File filePath, String source) {
        assertNotNull(source);

        return getEntry(filePath).getParsedModuleWithWorkingCopySource(source);
    }

    public void discardWorkingCopy(File 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 = 1000000;

        protected final File filePath;

        protected String source = null;
        protected volatile boolean isWorkingCopy = false;
        protected FileEntry sourceFileSyncAttributes;
        protected volatile ParsedModule parsedModule = null;

        public ModuleEntry(File 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;
            }

            FileEntry newAttributes;
            newAttributes = new FileEntry(filePath);

            if (!filePath.exists()) {
                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 = new FileEntry(filePath);
            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(FileEntry originalAttributes, FileEntry newAttributes) {
        return originalAttributes == null || originalAttributes.getLastModified() != newAttributes.getLastModified()
                || originalAttributes.getLength() != newAttributes.getLength();
    }

}