/** * This file is protected by Copyright. * Please refer to the COPYRIGHT file distributed with this source distribution. * * This file is part of REDHAWK IDE. * * 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. * */ package gov.redhawk.core.internal.filemanager; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.commons.io.FilenameUtils; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.jacorb.JacorbUtil; import org.omg.CORBA.Any; import CF.DataType; import CF.ErrorNumberType; import CF.File; import CF.FileException; import CF.FileSystem; import CF.InvalidFileName; import CF.FileManagerPackage.InvalidFileSystem; import CF.FileManagerPackage.MountPointAlreadyExists; import CF.FileManagerPackage.MountType; import CF.FileManagerPackage.NonExistentMount; import CF.FileSystemPackage.FileInformationType; import CF.FileSystemPackage.FileType; /** * Represents a virtual, in-memory directory in the IDE's file manager. It contains other {@link Directory} or * {@link MountPoint} instances, forming a tree that represents a file system hierarchy. An operation on this class * traverses the tree of {@link Directory} until a {@link MountPoint} is encountered, at which point the operation is * delegated to the mount point's file system for the remaining portion of the path. * <p/> * The {@link Directory} class itself will act as a read-only directory. Only the {@link #mount(IPath, FileSystem)} * operation will create new {@link Directory} (as needed), and {@link #unmount(IPath)} will remove them. * <p/> * All methods assume they are called with an absolute {@link IPath}. */ public class Directory implements Node { /** * The sub-directories / mounts inside this directory (name -> object) */ private final Map<String, Node> childNodes = new HashMap<String, Node>(); public Directory() { } /** * Returns true iff this {@link Directory} contains a child {@link Node} with the specified name. * @param childName * @return */ public boolean containsChildNode(String name) { return childNodes.containsKey(name); } @Override public void remove(IPath fileName) throws FileException, InvalidFileName { if (fileName.segmentCount() == 0) { throw new FileException(ErrorNumberType.CF_EISDIR, "Is a directory"); } Node node = childNodes.get(fileName.segment(0)); if (fileName.segmentCount() == 1) { if (node == null) { throw new FileException(ErrorNumberType.CF_ENOENT, "No such file"); } else { throw new FileException(ErrorNumberType.CF_EISDIR, "Is a directory"); } } if (node == null) { throw new FileException(ErrorNumberType.CF_ENOENT, "No such file"); } node.remove(fileName.removeFirstSegments(1).makeAbsolute()); } @Override public void rmdir(IPath directoryName) throws InvalidFileName, FileException { if (directoryName.segmentCount() == 0) { // Deny removal of this virtual directory throw new FileException(ErrorNumberType.CF_EACCES, "Directory is read-only"); } Node node = childNodes.get(directoryName.segment(0)); if (node == null) { throw new FileException(ErrorNumberType.CF_ENOENT, "No such directory"); } node.rmdir(directoryName.removeFirstSegments(1).makeAbsolute()); } @Override public File create(IPath fileName) throws InvalidFileName, FileException { // With one or fewer segments, we're not going to have anything path left to pass to a mount point if (fileName.segmentCount() <= 1) { throw new FileException(ErrorNumberType.CF_EACCES, "Write access to parent directory denied"); } Node node = childNodes.get(fileName.segment(0)); if (node == null) { throw new FileException(ErrorNumberType.CF_ENOENT, "No such directory"); } return node.create(fileName.removeFirstSegments(1).makeAbsolute()); } @Override public void mkdir(IPath directoryName) throws InvalidFileName, FileException { // With one or fewer segments, we're not going to have anything path left to pass to a mount point if (directoryName.segmentCount() <= 1) { throw new FileException(ErrorNumberType.CF_EACCES, "Parent directory is read-only"); } Node node = childNodes.get(directoryName.segment(0)); if (node == null) { throw new FileException(ErrorNumberType.CF_ENOENT, "No such directory"); } node.mkdir(directoryName.removeFirstSegments(1).makeAbsolute()); } public void mount(final IPath mountPoint, final FileSystem fileSystem) throws InvalidFileName, InvalidFileSystem, MountPointAlreadyExists { if (mountPoint.segmentCount() == 0) { throw new InvalidFileName(ErrorNumberType.CF_EINVAL, "Invalid mount point"); } Node node = childNodes.get(mountPoint.segment(0)); if (mountPoint.segmentCount() == 1) { if (node == null) { this.childNodes.put(mountPoint.segment(0), new MountPoint(fileSystem)); } else if (node instanceof Directory) { throw new InvalidFileName(ErrorNumberType.CF_EEXIST, "Target is a directory and mounting would shadow another mount"); } else { throw new MountPointAlreadyExists(); } } else { if (node == null) { Directory subDir = new Directory(); this.childNodes.put(mountPoint.segment(0), subDir); try { subDir.mount(mountPoint.removeFirstSegments(1), fileSystem); } catch (InvalidFileName | InvalidFileSystem | MountPointAlreadyExists e) { this.childNodes.remove(mountPoint.segment(0)); } } else if (node instanceof Directory) { ((Directory) node).mount(mountPoint.removeFirstSegments(1), fileSystem); } else { throw new MountPointAlreadyExists(mountPoint.segment(0)); } } } public void unmount(IPath mountPoint) throws NonExistentMount { if (mountPoint.segmentCount() == 0) { throw new NonExistentMount("Not a mount point"); } Node node = childNodes.get(mountPoint.segment(0)); if (mountPoint.segmentCount() == 1) { if (node == null || node instanceof Directory) { throw new NonExistentMount("Not a mount point"); } else { this.childNodes.remove(mountPoint.segment(0)); } } else { if (node == null) { throw new NonExistentMount("No such parent directory: " + mountPoint.segment(0)); } else if (node instanceof Directory) { Directory dir = (Directory) node; dir.unmount(mountPoint.removeFirstSegments(1)); if (dir.childNodes.isEmpty()) { this.childNodes.remove(mountPoint.segment(0)); } } else { throw new NonExistentMount("Parent directory is a mount point: " + mountPoint.segment(0)); } } } public List<MountType> getMounts() { List<MountType> mounts = new ArrayList<MountType>(); getMounts(new Path("/"), mounts); return mounts; } /** * Internal helper method for {@link #getMounts()}. * @param parent Parent directory of this directory * @param mounts A {@link List} to add mounts to */ private void getMounts(IPath parent, List<MountType> mounts) { for (final Entry<String, Node> entry : this.childNodes.entrySet()) { Node value = entry.getValue(); if (value instanceof Directory) { ((Directory) value).getMounts(parent.append(entry.getKey()), mounts); } else { mounts.add(new MountType(parent.append(entry.getKey()).toString(), ((MountPoint) value).getFileSystem())); } } } @Override public boolean exists(IPath fileName) throws InvalidFileName { if (fileName.segmentCount() == 0) { return true; } Node node = this.childNodes.get(fileName.segment(0)); if (node == null) { return false; } return node.exists(fileName.removeFirstSegments(1).makeAbsolute()); } @Override public List<FileInformationType> list(IPath pattern) throws FileException, InvalidFileName { if (pattern.segmentCount() > 1) { Node node = this.childNodes.get(pattern.segment(0)); if (node == null) { throw new FileException(ErrorNumberType.CF_ENOENT, "No such directory"); } return node.list(pattern.removeFirstSegments(1).makeAbsolute()); } final String filePatternOnly = pattern.lastSegment(); List<FileInformationType> fits = new ArrayList<FileInformationType>(); for (final Entry<String, Node> entry : this.childNodes.entrySet()) { if (FilenameUtils.wildcardMatch(entry.getKey(), filePatternOnly)) { final FileInformationType fileInfo = new FileInformationType(); fileInfo.name = entry.getKey(); fileInfo.size = 0; if (entry.getValue() instanceof Directory) { fileInfo.kind = FileType.DIRECTORY; List<DataType> properties = new ArrayList<DataType>(); Any readOnlyAny = JacorbUtil.init().create_any(); readOnlyAny.insert_boolean(true); properties.add(new DataType("READ_ONLY", readOnlyAny)); Any executableAny = JacorbUtil.init().create_any(); executableAny.insert_boolean(true); properties.add(new DataType("EXECUTABLE", executableAny)); fileInfo.fileProperties = properties.toArray(new DataType[properties.size()]); } else { fileInfo.kind = FileType.FILE_SYSTEM; fileInfo.fileProperties = new DataType[0]; } fits.add(fileInfo); } } return fits; } @Override public File open(IPath fileName, final boolean readOnly) throws InvalidFileName, FileException { if (fileName.segmentCount() == 0) { throw new FileException(ErrorNumberType.CF_EISDIR, "Is a directory"); } Node node = childNodes.get(fileName.segment(0)); if (fileName.segmentCount() == 1) { if (node == null) { throw new FileException(ErrorNumberType.CF_ENOENT, "No such file"); } else { throw new FileException(ErrorNumberType.CF_EISDIR, "Is a directory"); } } if (node == null) { throw new FileException(ErrorNumberType.CF_ENOENT, "No such file"); } return node.open(fileName.removeFirstSegments(1).makeAbsolute(), readOnly); } }