--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/efs/ChromiumScriptStorage.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,328 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.efs;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.chromium.debug.core.ChromiumDebugPlugin;
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileInfo;
+import org.eclipse.core.filesystem.provider.FileInfo;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+
+/**
+ * A memory-based storage for browser scripts. All resource-related EFS
+ * operations are delegated into here.
+ */
+public class ChromiumScriptStorage {
+
+ /**
+ * The filesystem root path.
+ */
+ // This one should go before INSTANCE.
+ private static final IPath ROOT_PATH = new Path(null, ""); //$NON-NLS-1$
+
+ private static final ChromiumScriptStorage INSTANCE = new ChromiumScriptStorage();
+
+ public static ChromiumScriptStorage getInstance() {
+ return INSTANCE;
+ }
+
+ private static abstract class CommonNode {
+ final IPath path;
+
+ final FileInfo info;
+
+ final CommonNode parent;
+
+ CommonNode(IPath path, FolderNode parent, boolean isDirectory) {
+ this.path = path;
+ this.parent = parent;
+ this.info = new FileInfo(path.lastSegment());
+ this.info.setDirectory(isDirectory);
+ this.info.setExists(true);
+ if (parent != null) {
+ parent.add(this);
+ }
+ }
+
+ String getName() {
+ return info.getName();
+ }
+
+ boolean isFile() {
+ return !info.isDirectory();
+ }
+
+ }
+
+ private static class RootNode extends FolderNode {
+ RootNode() {
+ super(ROOT_PATH, null);
+ if (parent != null) {
+ throw new IllegalArgumentException("Parent must be null, was: " + parent); //$NON-NLS-1$
+ }
+ }
+
+ @Override
+ synchronized void add(CommonNode node) {
+ if (node.isFile()) {
+ throw new IllegalArgumentException("Cannot add files to the root"); //$NON-NLS-1$
+ }
+ super.add(node);
+ }
+
+ }
+
+ /**
+ * Contains other nodes.
+ */
+ private static class FolderNode extends CommonNode {
+ private final Map<String, CommonNode> children =
+ Collections.synchronizedMap(new HashMap<String, CommonNode>());
+
+ FolderNode(IPath path, FolderNode parent) {
+ super(path, parent, true);
+ }
+
+ void add(CommonNode node) {
+ children.put(node.getName(), node);
+ }
+
+ void remove(String name) {
+ // System.out.println(this.hashCode() + " removing " + name);
+ CommonNode removedNode = children.remove(name);
+ if (removedNode != null) {
+ removedNode.info.setExists(false);
+ }
+ }
+ }
+
+ private static class FileNode extends CommonNode {
+ private static final byte[] EMPTY_BYTES = new byte[0];
+
+ protected volatile byte[] contents = EMPTY_BYTES;
+
+ FileNode(IPath path, FolderNode parent) {
+ super(path, parent, false);
+ }
+
+ synchronized InputStream getInputStream() {
+ return new ByteArrayInputStream(contents);
+ }
+
+ synchronized OutputStream getOutputStream(final int options) {
+ return new ByteArrayOutputStream() {
+ @Override
+ public void close() throws IOException {
+ super.close();
+ byte[] data;
+ if ((options & EFS.APPEND) == 0) {
+ data = this.toByteArray();
+ } else {
+ byte[] outputData = this.toByteArray();
+ data = new byte[contents.length + this.size()];
+ System.arraycopy(contents, 0, data, 0, contents.length);
+ System.arraycopy(outputData, 0, data, contents.length, outputData.length);
+ }
+ setFileContents(data);
+ }
+ };
+
+ }
+
+ protected synchronized void setFileContents(byte[] data) {
+ contents = data;
+ info.setLength(data.length);
+ info.setLastModified(System.currentTimeMillis());
+ info.setExists(true);
+ }
+
+ }
+
+ private static final String[] EMPTY_NAMES = new String[0];
+
+ private final RootNode ROOT = new RootNode();
+
+ private CommonNode find(IPath path) {
+ if (path == null) {
+ return null;
+ }
+ CommonNode currentNode = ROOT;
+ // invariant: node(path[i]) is a folder
+ for (int i = 0, length = path.segmentCount(); i < length; i++) {
+ // > 1 segments
+ if (currentNode == null || currentNode.isFile()) {
+ // currentNode is not an existing folder
+ return null;
+ }
+ // currentNode is a folder
+ currentNode = ((FolderNode) currentNode).children.get(path.segment(i));
+ }
+ return currentNode;
+ }
+
+ String[] childNames(IPath path) {
+ Map<String, CommonNode> childrenMap = childNodes(path);
+ if (childrenMap == null) {
+ return EMPTY_NAMES;
+ }
+ return childrenMap.keySet().toArray(EMPTY_NAMES);
+ }
+
+ OutputStream openOutputStream(IPath path, int options) throws CoreException {
+ CommonNode node = find(path);
+ if (node == null) { // file does not exist
+ if (path.segmentCount() > 0) {
+ CommonNode parent = find(getParentPath(path));
+ if (parent != null && !parent.isFile()) {
+ FileNode fileNode = createFile(path, parent);
+ return fileNode.getOutputStream(options);
+ } else {
+ throw newCoreException("Bad store path (no parent folder), child=" + path, null); //$NON-NLS-1$
+ }
+ } else {
+ throw newCoreException("Cannot open OutputStream for the Root", null); //$NON-NLS-1$
+ }
+ }
+ if (node.isFile()) {
+ return ((FileNode) node).getOutputStream(options);
+ } else {
+ throw newCoreException("Cannot open a directory for writing: " + path, null); //$NON-NLS-1$
+ }
+ }
+
+ void mkdir(IPath path, int options) throws CoreException {
+ CommonNode node = find(path);
+ if (node != null || path.segmentCount() == 0) { // folder exists
+ return;
+ }
+ IPath parentPath = getParentPath(path);
+ // parentPath will not be null due to the check above
+ CommonNode parentNode = find(parentPath);
+ if ((options & EFS.SHALLOW) != 0) {
+ IPath chainPath = ROOT_PATH;
+ CommonNode childNode = null;
+ parentNode = find(ROOT_PATH);
+ for (int i = 0, length = path.segmentCount(); i < length; i++) {
+ chainPath = chainPath.append(path.segment(i));
+ childNode = find(chainPath);
+ if (childNode == null) {
+ createFolder(chainPath, parentNode);
+ parentNode = childNode;
+ continue;
+ }
+ if (childNode.isFile()) {
+ throw newCoreException("File encountered in the path: " + chainPath, null); //$NON-NLS-1$
+ }
+ }
+ } else {
+ if (parentNode == null) {
+ throw newCoreException("Parent does not exist, child=" + path, null); //$NON-NLS-1$
+ }
+ // not shallow and parent exists
+ if (!parentNode.isFile()) {
+ createFolder(path, parentNode);
+ } else {
+ throw newCoreException("Parent is a file: " + parentNode.path, null); //$NON-NLS-1$
+ }
+ }
+ }
+
+ void delete(IPath path, int options) throws CoreException {
+ CommonNode parent = find(getParentPath(path));
+ if (parent == null) {
+ return;
+ }
+ if (parent.isFile()) {
+ throw newCoreException("Parent is not a directory: " + getParentPath(path), null); //$NON-NLS-1$
+ }
+ FolderNode parentFolder = (FolderNode) parent;
+ parentFolder.remove(path.lastSegment());
+ }
+
+ InputStream openInputStream(IPath path, int options) throws CoreException {
+ CommonNode node = find(path);
+ if (node == null) {
+ throw newCoreException("File not found: " + path, null); //$NON-NLS-1$
+ }
+ if (!node.isFile()) {
+ throw newCoreException("Cannot open InputStream on directory: " + path, null); //$NON-NLS-1$
+ }
+ return ((FileNode) node).getInputStream();
+ }
+
+ IFileInfo fetchInfo(IPath path, int options) {
+ CommonNode node = find(path);
+ if (node == null) {
+ FileInfo fileInfo = new FileInfo(path.lastSegment());
+ fileInfo.setExists(false);
+ return fileInfo;
+ } else {
+ return node.info;
+ }
+ }
+
+ void putInfo(IPath path, IFileInfo info, int options) throws CoreException {
+ CommonNode node = find(path);
+ if (node == null) {
+ throw newCoreException("The store does not exist: " + path, null); //$NON-NLS-1$
+ } else {
+ if ((options & EFS.SET_ATTRIBUTES) != 0) {
+ copyAttribute(info, node.info, EFS.ATTRIBUTE_ARCHIVE);
+ copyAttribute(info, node.info, EFS.ATTRIBUTE_EXECUTABLE);
+ copyAttribute(info, node.info, EFS.ATTRIBUTE_HIDDEN);
+ copyAttribute(info, node.info, EFS.ATTRIBUTE_LINK_TARGET);
+ copyAttribute(info, node.info, EFS.ATTRIBUTE_READ_ONLY);
+ }
+ if ((options & EFS.SET_LAST_MODIFIED) != 0) {
+ node.info.setLastModified(info.getLastModified());
+ }
+ }
+ }
+
+ private static void copyAttribute(IFileInfo from, IFileInfo to, int attribute) {
+ to.setAttribute(attribute, from.getAttribute(attribute));
+ }
+
+ private static CoreException newCoreException(String message, Throwable cause) {
+ return new CoreException(
+ new Status(Status.ERROR, ChromiumDebugPlugin.PLUGIN_ID, message, cause));
+ }
+
+ private static IPath getParentPath(IPath path) {
+ if (path.segmentCount() == 0) {
+ return null;
+ }
+ return path.removeLastSegments(1);
+ }
+
+ private static void createFolder(IPath path, CommonNode parentNode) {
+ new FolderNode(path, (FolderNode) parentNode);
+ }
+
+ private static FileNode createFile(IPath path, CommonNode parent) {
+ return new FileNode(path, (FolderNode) parent);
+ }
+
+ private Map<String, CommonNode> childNodes(IPath parent) {
+ CommonNode node = find(parent);
+ if (node == null || node.isFile()) {
+ return null;
+ }
+ return ((FolderNode) node).children;
+ }
+
+}