org.chromium.debug.core/src/org/chromium/debug/core/efs/ChromiumScriptStorage.java
changeset 2 e4420d2515f1
equal deleted inserted replaced
1:ef76fc2ac88c 2:e4420d2515f1
       
     1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
       
     2 // Use of this source code is governed by a BSD-style license that can be
       
     3 // found in the LICENSE file.
       
     4 
       
     5 package org.chromium.debug.core.efs;
       
     6 
       
     7 import java.io.ByteArrayInputStream;
       
     8 import java.io.ByteArrayOutputStream;
       
     9 import java.io.IOException;
       
    10 import java.io.InputStream;
       
    11 import java.io.OutputStream;
       
    12 import java.util.Collections;
       
    13 import java.util.HashMap;
       
    14 import java.util.Map;
       
    15 
       
    16 import org.chromium.debug.core.ChromiumDebugPlugin;
       
    17 import org.eclipse.core.filesystem.EFS;
       
    18 import org.eclipse.core.filesystem.IFileInfo;
       
    19 import org.eclipse.core.filesystem.provider.FileInfo;
       
    20 import org.eclipse.core.runtime.CoreException;
       
    21 import org.eclipse.core.runtime.IPath;
       
    22 import org.eclipse.core.runtime.Path;
       
    23 import org.eclipse.core.runtime.Status;
       
    24 
       
    25 /**
       
    26  * A memory-based storage for browser scripts. All resource-related EFS
       
    27  * operations are delegated into here.
       
    28  */
       
    29 public class ChromiumScriptStorage {
       
    30 
       
    31   /**
       
    32    * The filesystem root path.
       
    33    */
       
    34   // This one should go before INSTANCE.
       
    35   private static final IPath ROOT_PATH = new Path(null, ""); //$NON-NLS-1$
       
    36 
       
    37   private static final ChromiumScriptStorage INSTANCE = new ChromiumScriptStorage();
       
    38 
       
    39   public static ChromiumScriptStorage getInstance() {
       
    40     return INSTANCE;
       
    41   }
       
    42 
       
    43   private static abstract class CommonNode {
       
    44     final IPath path;
       
    45 
       
    46     final FileInfo info;
       
    47 
       
    48     final CommonNode parent;
       
    49 
       
    50     CommonNode(IPath path, FolderNode parent, boolean isDirectory) {
       
    51       this.path = path;
       
    52       this.parent = parent;
       
    53       this.info = new FileInfo(path.lastSegment());
       
    54       this.info.setDirectory(isDirectory);
       
    55       this.info.setExists(true);
       
    56       if (parent != null) {
       
    57         parent.add(this);
       
    58       }
       
    59     }
       
    60 
       
    61     String getName() {
       
    62       return info.getName();
       
    63     }
       
    64 
       
    65     boolean isFile() {
       
    66       return !info.isDirectory();
       
    67     }
       
    68 
       
    69   }
       
    70 
       
    71   private static class RootNode extends FolderNode {
       
    72     RootNode() {
       
    73       super(ROOT_PATH, null);
       
    74       if (parent != null) {
       
    75         throw new IllegalArgumentException("Parent must be null, was: " + parent); //$NON-NLS-1$
       
    76       }
       
    77     }
       
    78 
       
    79     @Override
       
    80     synchronized void add(CommonNode node) {
       
    81       if (node.isFile()) {
       
    82         throw new IllegalArgumentException("Cannot add files to the root"); //$NON-NLS-1$
       
    83       }
       
    84       super.add(node);
       
    85     }
       
    86 
       
    87   }
       
    88 
       
    89   /**
       
    90    * Contains other nodes.
       
    91    */
       
    92   private static class FolderNode extends CommonNode {
       
    93     private final Map<String, CommonNode> children =
       
    94         Collections.synchronizedMap(new HashMap<String, CommonNode>());
       
    95 
       
    96     FolderNode(IPath path, FolderNode parent) {
       
    97       super(path, parent, true);
       
    98     }
       
    99 
       
   100     void add(CommonNode node) {
       
   101       children.put(node.getName(), node);
       
   102     }
       
   103 
       
   104     void remove(String name) {
       
   105       // System.out.println(this.hashCode() + " removing " + name);
       
   106       CommonNode removedNode = children.remove(name);
       
   107       if (removedNode != null) {
       
   108         removedNode.info.setExists(false);
       
   109       }
       
   110     }
       
   111   }
       
   112 
       
   113   private static class FileNode extends CommonNode {
       
   114     private static final byte[] EMPTY_BYTES = new byte[0];
       
   115 
       
   116     protected volatile byte[] contents = EMPTY_BYTES;
       
   117 
       
   118     FileNode(IPath path, FolderNode parent) {
       
   119       super(path, parent, false);
       
   120     }
       
   121 
       
   122     synchronized InputStream getInputStream() {
       
   123       return new ByteArrayInputStream(contents);
       
   124     }
       
   125 
       
   126     synchronized OutputStream getOutputStream(final int options) {
       
   127       return new ByteArrayOutputStream() {
       
   128         @Override
       
   129         public void close() throws IOException {
       
   130           super.close();
       
   131           byte[] data;
       
   132           if ((options & EFS.APPEND) == 0) {
       
   133             data = this.toByteArray();
       
   134           } else {
       
   135             byte[] outputData = this.toByteArray();
       
   136             data = new byte[contents.length + this.size()];
       
   137             System.arraycopy(contents, 0, data, 0, contents.length);
       
   138             System.arraycopy(outputData, 0, data, contents.length, outputData.length);
       
   139           }
       
   140           setFileContents(data);
       
   141         }
       
   142       };
       
   143 
       
   144     }
       
   145 
       
   146     protected synchronized void setFileContents(byte[] data) {
       
   147       contents = data;
       
   148       info.setLength(data.length);
       
   149       info.setLastModified(System.currentTimeMillis());
       
   150       info.setExists(true);
       
   151     }
       
   152 
       
   153   }
       
   154 
       
   155   private static final String[] EMPTY_NAMES = new String[0];
       
   156 
       
   157   private final RootNode ROOT = new RootNode();
       
   158 
       
   159   private CommonNode find(IPath path) {
       
   160     if (path == null) {
       
   161       return null;
       
   162     }
       
   163     CommonNode currentNode = ROOT;
       
   164     // invariant: node(path[i]) is a folder
       
   165     for (int i = 0, length = path.segmentCount(); i < length; i++) {
       
   166       // > 1 segments
       
   167       if (currentNode == null || currentNode.isFile()) {
       
   168         // currentNode is not an existing folder
       
   169         return null;
       
   170       }
       
   171       // currentNode is a folder
       
   172       currentNode = ((FolderNode) currentNode).children.get(path.segment(i));
       
   173     }
       
   174     return currentNode;
       
   175   }
       
   176 
       
   177   String[] childNames(IPath path) {
       
   178     Map<String, CommonNode> childrenMap = childNodes(path);
       
   179     if (childrenMap == null) {
       
   180       return EMPTY_NAMES;
       
   181     }
       
   182     return childrenMap.keySet().toArray(EMPTY_NAMES);
       
   183   }
       
   184 
       
   185   OutputStream openOutputStream(IPath path, int options) throws CoreException {
       
   186     CommonNode node = find(path);
       
   187     if (node == null) { // file does not exist
       
   188       if (path.segmentCount() > 0) {
       
   189         CommonNode parent = find(getParentPath(path));
       
   190         if (parent != null && !parent.isFile()) {
       
   191           FileNode fileNode = createFile(path, parent);
       
   192           return fileNode.getOutputStream(options);
       
   193         } else {
       
   194           throw newCoreException("Bad store path (no parent folder), child=" + path, null); //$NON-NLS-1$
       
   195         }
       
   196       } else {
       
   197         throw newCoreException("Cannot open OutputStream for the Root", null); //$NON-NLS-1$
       
   198       }
       
   199     }
       
   200     if (node.isFile()) {
       
   201       return ((FileNode) node).getOutputStream(options);
       
   202     } else {
       
   203       throw newCoreException("Cannot open a directory for writing: " + path, null); //$NON-NLS-1$
       
   204     }
       
   205   }
       
   206 
       
   207   void mkdir(IPath path, int options) throws CoreException {
       
   208     CommonNode node = find(path);
       
   209     if (node != null || path.segmentCount() == 0) { // folder exists
       
   210       return;
       
   211     }
       
   212     IPath parentPath = getParentPath(path);
       
   213     // parentPath will not be null due to the check above
       
   214     CommonNode parentNode = find(parentPath);
       
   215     if ((options & EFS.SHALLOW) != 0) {
       
   216       IPath chainPath = ROOT_PATH;
       
   217       CommonNode childNode = null;
       
   218       parentNode = find(ROOT_PATH);
       
   219       for (int i = 0, length = path.segmentCount(); i < length; i++) {
       
   220         chainPath = chainPath.append(path.segment(i));
       
   221         childNode = find(chainPath);
       
   222         if (childNode == null) {
       
   223           createFolder(chainPath, parentNode);
       
   224           parentNode = childNode;
       
   225           continue;
       
   226         }
       
   227         if (childNode.isFile()) {
       
   228           throw newCoreException("File encountered in the path: " + chainPath, null); //$NON-NLS-1$
       
   229         }
       
   230       }
       
   231     } else {
       
   232       if (parentNode == null) {
       
   233         throw newCoreException("Parent does not exist, child=" + path, null); //$NON-NLS-1$
       
   234       }
       
   235       // not shallow and parent exists
       
   236       if (!parentNode.isFile()) {
       
   237         createFolder(path, parentNode);
       
   238       } else {
       
   239         throw newCoreException("Parent is a file: " + parentNode.path, null); //$NON-NLS-1$
       
   240       }
       
   241     }
       
   242   }
       
   243 
       
   244   void delete(IPath path, int options) throws CoreException {
       
   245     CommonNode parent = find(getParentPath(path));
       
   246     if (parent == null) {
       
   247       return;
       
   248     }
       
   249     if (parent.isFile()) {
       
   250       throw newCoreException("Parent is not a directory: " + getParentPath(path), null); //$NON-NLS-1$
       
   251     }
       
   252     FolderNode parentFolder = (FolderNode) parent;
       
   253     parentFolder.remove(path.lastSegment());
       
   254   }
       
   255 
       
   256   InputStream openInputStream(IPath path, int options) throws CoreException {
       
   257     CommonNode node = find(path);
       
   258     if (node == null) {
       
   259       throw newCoreException("File not found: " + path, null); //$NON-NLS-1$
       
   260     }
       
   261     if (!node.isFile()) {
       
   262       throw newCoreException("Cannot open InputStream on directory: " + path, null); //$NON-NLS-1$
       
   263     }
       
   264     return ((FileNode) node).getInputStream();
       
   265   }
       
   266 
       
   267   IFileInfo fetchInfo(IPath path, int options) {
       
   268     CommonNode node = find(path);
       
   269     if (node == null) {
       
   270       FileInfo fileInfo = new FileInfo(path.lastSegment());
       
   271       fileInfo.setExists(false);
       
   272       return fileInfo;
       
   273     } else {
       
   274       return node.info;
       
   275     }
       
   276   }
       
   277 
       
   278   void putInfo(IPath path, IFileInfo info, int options) throws CoreException {
       
   279     CommonNode node = find(path);
       
   280     if (node == null) {
       
   281       throw newCoreException("The store does not exist: " + path, null); //$NON-NLS-1$
       
   282     } else {
       
   283       if ((options & EFS.SET_ATTRIBUTES) != 0) {
       
   284         copyAttribute(info, node.info, EFS.ATTRIBUTE_ARCHIVE);
       
   285         copyAttribute(info, node.info, EFS.ATTRIBUTE_EXECUTABLE);
       
   286         copyAttribute(info, node.info, EFS.ATTRIBUTE_HIDDEN);
       
   287         copyAttribute(info, node.info, EFS.ATTRIBUTE_LINK_TARGET);
       
   288         copyAttribute(info, node.info, EFS.ATTRIBUTE_READ_ONLY);
       
   289       }
       
   290       if ((options & EFS.SET_LAST_MODIFIED) != 0) {
       
   291         node.info.setLastModified(info.getLastModified());
       
   292       }
       
   293     }
       
   294   }
       
   295 
       
   296   private static void copyAttribute(IFileInfo from, IFileInfo to, int attribute) {
       
   297     to.setAttribute(attribute, from.getAttribute(attribute));
       
   298   }
       
   299 
       
   300   private static CoreException newCoreException(String message, Throwable cause) {
       
   301     return new CoreException(
       
   302         new Status(Status.ERROR, ChromiumDebugPlugin.PLUGIN_ID, message, cause));
       
   303   }
       
   304 
       
   305   private static IPath getParentPath(IPath path) {
       
   306     if (path.segmentCount() == 0) {
       
   307       return null;
       
   308     }
       
   309     return path.removeLastSegments(1);
       
   310   }
       
   311 
       
   312   private static void createFolder(IPath path, CommonNode parentNode) {
       
   313     new FolderNode(path, (FolderNode) parentNode);
       
   314   }
       
   315 
       
   316   private static FileNode createFile(IPath path, CommonNode parent) {
       
   317     return new FileNode(path, (FolderNode) parent);
       
   318   }
       
   319 
       
   320   private Map<String, CommonNode> childNodes(IPath parent) {
       
   321     CommonNode node = find(parent);
       
   322     if (node == null || node.isFile()) {
       
   323       return null;
       
   324     }
       
   325     return ((FolderNode) node).children;
       
   326   }
       
   327 
       
   328 }