/******************************************************************************* * Copyright (c) 2009-2015 CWI * 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: * * Paul Klint - Paul.Klint@cwi.nl - CWI * * Davy Landman -davy.landman@gmail.com - CWI *******************************************************************************/ package org.rascalmpl.uri.libraries; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.Charset; import java.util.concurrent.ConcurrentNavigableMap; import org.rascalmpl.uri.FileTree; import org.rascalmpl.uri.ISourceLocationInputOutput; import org.rascalmpl.value.ISourceLocation; /** * This resolver is used for example for the scheme "test-modules", which amongst others * generates modules during tests. * These modules are implemented via an in-memory "file system" that guarantees * that "lastModified" is monotone increasing, i.e. after a write to a file lastModified * is ALWAYS larger than for the previous version of the same file. * When files are written at high speeed (e.g. with 10-30 ms intervals), this property is, * unfortunately, not guaranteed on all operating systems. * * So if you are writing temporary files very frequently and use lastModified to mark the fields * as dirty, use an instance of this resolver to guarantee the dirty marking. * * The locations should not use the autority field, as that is ignored. * * BE AWARE that the information in this in-memory file system is volatile and does not survive: * - program execution * - replacement by another in-memory filesystem for the same scheme * */ public abstract class InMemoryResolver implements ISourceLocationInputOutput { private final String scheme; private final class InMemoryFileTree extends FileTree { public ConcurrentNavigableMap<String, FSEntry> getFileSystem() { return fs; } } private InMemoryFileTree fileSystem = new InMemoryFileTree(); public InMemoryResolver(String scheme) { //System.err.println("CREATE InMemoryResolver: " + scheme + ": " + this); this.scheme = scheme; } @Override public String scheme() { return scheme; } private static final class File extends FileTree.FSEntry { byte[] contents; public File() { super(System.currentTimeMillis()); contents = null; } public void newContent(byte[] byteArray) { long newTimestamp = System.currentTimeMillis(); if (newTimestamp <= lastModified) { newTimestamp = lastModified + 1; } lastModified = newTimestamp; contents = byteArray; } public String toString(){ return String.valueOf(lastModified) ;//+ ":\n" +new String(contents, StandardCharsets.UTF_8); } } private File get(ISourceLocation uri) { return (File) fileSystem.getFileSystem().get(uri.getPath()); } @Override public InputStream getInputStream(ISourceLocation uri) throws IOException { File file = get(uri); if (file == null) { throw new FileNotFoundException(); } return new ByteArrayInputStream(file.contents); } @Override public OutputStream getOutputStream(ISourceLocation uri, boolean append) throws IOException { ByteArrayOutputStream result = new ByteArrayOutputStream() { @Override public void close() throws IOException { File file = get(uri); byte[] content = this.toByteArray(); if (file == null) { file = new File(); fileSystem.getFileSystem().put(uri.getPath(), file); } file.newContent(content); super.close(); } }; if (append) { File file = get(uri); if (file == null) { throw new FileNotFoundException(); } // load data to write, makes the closing code simpler result.write(file.contents); } return result; } @Override public long lastModified(ISourceLocation uri) throws IOException { File file = get(uri); if (file == null) { throw new FileNotFoundException(); } return file.lastModified; } @Override public Charset getCharset(ISourceLocation uri) throws IOException { return null; } @Override public boolean exists(ISourceLocation uri) { return fileSystem.exists(uri.getPath()); } @Override public boolean isDirectory(ISourceLocation uri) { return fileSystem.isDirectory(uri.getPath()); } @Override public boolean isFile(ISourceLocation uri) { return fileSystem.isFile(uri.getPath()); } @Override public String[] list(ISourceLocation uri) throws IOException { return fileSystem.directChildren(uri.getPath()); } @Override public boolean supportsHost() { return false; } @Override public void mkDirectory(ISourceLocation uri) throws IOException { } @Override public void remove(ISourceLocation uri) throws IOException { fileSystem.getFileSystem().remove(uri.getPath()); } }