| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.commons.vfs2.provider.ram; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.Serializable; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import org.apache.commons.vfs2.Capability; |
| import org.apache.commons.vfs2.FileContent; |
| import org.apache.commons.vfs2.FileName; |
| import org.apache.commons.vfs2.FileObject; |
| import org.apache.commons.vfs2.FileSystemException; |
| import org.apache.commons.vfs2.FileSystemOptions; |
| import org.apache.commons.vfs2.FileType; |
| import org.apache.commons.vfs2.provider.AbstractFileName; |
| import org.apache.commons.vfs2.provider.AbstractFileSystem; |
| |
| /** |
| * A RAM File System. |
| */ |
| public class RamFileSystem extends AbstractFileSystem implements Serializable { |
| |
| private static final int BUFFER_SIZE = 512; |
| |
| /** |
| * serialVersionUID format is YYYYMMDD for the date of the last binary change. |
| */ |
| private static final long serialVersionUID = 20101208L; |
| |
| /** |
| * Cache of RAM File Data |
| */ |
| private final Map<FileName, RamFileData> cache; |
| |
| /** |
| * @param rootName The root file name. |
| * @param fileSystemOptions The FileSystem options. |
| */ |
| protected RamFileSystem(final FileName rootName, final FileSystemOptions fileSystemOptions) { |
| super(rootName, null, fileSystemOptions); |
| this.cache = Collections.synchronizedMap(new HashMap<FileName, RamFileData>()); |
| // create root |
| final RamFileData rootData = new RamFileData(rootName); |
| rootData.setType(FileType.FOLDER); |
| rootData.setLastModified(System.currentTimeMillis()); |
| this.cache.put(rootName, rootData); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.commons.vfs2.provider.AbstractFileSystem#createFile(org.apache.commons.vfs2.FileName) |
| */ |
| @Override |
| protected FileObject createFile(final AbstractFileName name) throws Exception { |
| return new RamFileObject(name, this); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.apache.commons.vfs2.provider.AbstractFileSystem#addCapabilities(java.util.Collection) |
| */ |
| @Override |
| protected void addCapabilities(final Collection<Capability> caps) { |
| caps.addAll(RamFileProvider.capabilities); |
| } |
| |
| /** |
| * @param name The name of the file. |
| * @return children The names of the children. |
| */ |
| String[] listChildren(final FileName name) { |
| final RamFileData data = this.cache.get(name); |
| if (data == null || !data.getType().hasChildren()) { |
| return null; |
| } |
| final Collection<RamFileData> children = data.getChildren(); |
| |
| synchronized (children) { |
| return children.stream().map(childData -> childData.getName().getBaseName()).toArray(String[]::new); |
| } |
| } |
| |
| /** |
| * Delete a file |
| * |
| * @param file |
| * @throws FileSystemException |
| */ |
| void delete(final RamFileObject file) throws FileSystemException { |
| // root is read only check |
| FileSystemException.requireNonNull(file.getParent(), "unable to delete root"); |
| |
| // Remove reference from cache |
| this.cache.remove(file.getName()); |
| // Notify the parent |
| final RamFileObject parent = (RamFileObject) this.resolveFile(file.getParent().getName()); |
| parent.getData().removeChild(file.getData()); |
| parent.close(); |
| // Close the file |
| file.getData().clear(); |
| file.close(); |
| } |
| |
| /** |
| * Saves a file |
| * |
| * @param file |
| * @throws FileSystemException |
| */ |
| void save(final RamFileObject file) throws FileSystemException { |
| |
| // Validate name |
| if (file.getData().getName() == null) { |
| throw new FileSystemException(new IllegalStateException("The data has no name. " + file)); |
| } |
| |
| // Add to the parent |
| if (file.getName().getDepth() > 0) { |
| final RamFileData parentData = this.cache.get(file.getParent().getName()); |
| // Only if not already added |
| if (!parentData.hasChildren(file.getData())) { |
| final RamFileObject parent = (RamFileObject) file.getParent(); |
| parent.getData().addChild(file.getData()); |
| parent.close(); |
| } |
| } |
| // Store in cache |
| cache.put(file.getName(), file.getData()); |
| file.getData().updateLastModified(); |
| file.close(); |
| } |
| |
| /** |
| * @param from The original file. |
| * @param to The new file. |
| * @throws FileSystemException if an error occurs. |
| */ |
| void rename(final RamFileObject from, final RamFileObject to) throws FileSystemException { |
| if (!this.cache.containsKey(from.getName())) { |
| throw new FileSystemException("File does not exist: " + from.getName()); |
| } |
| // Copy data |
| |
| to.getData().setContent(from.getData().getContent()); |
| to.getData().setLastModified(from.getData().getLastModified()); |
| to.getData().setType(from.getData().getType()); |
| |
| this.save(to); |
| this.delete(from); |
| } |
| |
| public void attach(final RamFileObject fo) { |
| if (fo.getName() == null) { |
| throw new IllegalArgumentException("Null argument"); |
| } |
| RamFileData data = this.cache.get(fo.getName()); |
| if (data == null) { |
| data = new RamFileData(fo.getName()); |
| } |
| fo.setData(data); |
| } |
| |
| /** |
| * Import a Tree. |
| * |
| * @param file The File |
| * @throws FileSystemException if an error occurs. |
| */ |
| public void importTree(final File file) throws FileSystemException { |
| final FileObject fileFo = getFileSystemManager().toFileObject(file); |
| this.toRamFileObject(fileFo, fileFo); |
| } |
| |
| /** |
| * Import the given file with the name relative to the given root |
| * |
| * @param fo |
| * @param root |
| * @throws FileSystemException |
| */ |
| private void toRamFileObject(final FileObject fo, final FileObject root) throws FileSystemException { |
| final RamFileObject memFo = (RamFileObject) this |
| .resolveFile(fo.getName().getPath().substring(root.getName().getPath().length())); |
| if (fo.getType().hasChildren()) { |
| // Create Folder |
| memFo.createFolder(); |
| // Import recursively |
| final FileObject[] fos = fo.getChildren(); |
| for (final FileObject child : fos) { |
| this.toRamFileObject(child, root); |
| } |
| } else if (fo.isFile()) { |
| // Copy bytes |
| try (final FileContent content = fo.getContent()) { |
| content.write(memFo); |
| } catch (final IOException e) { |
| throw new FileSystemException(e.getClass().getName() + " " + e.getMessage()); |
| } |
| } else { |
| throw new FileSystemException("File is not a folder nor a file " + memFo); |
| } |
| } |
| |
| /** |
| * @return Returns the size of the FileSystem |
| */ |
| long size() { |
| long size = 0; |
| synchronized (cache) { |
| for (final RamFileData data : cache.values()) { |
| size += data.size(); |
| } |
| } |
| return size; |
| } |
| |
| /** |
| * Close the RAMFileSystem. |
| */ |
| @Override |
| public void close() { |
| this.cache.clear(); |
| super.close(); |
| } |
| } |