All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jboss.vfs.VirtualFile Maven / Gradle / Ivy

There is a newer version: 3.3.2.Final
Show newest version
/*
 * JBoss, Home of Professional Open Source
 * Copyright 2005, JBoss Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed 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.jboss.vfs;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.lang.reflect.UndeclaredThrowableException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.AccessController;
import java.security.CodeSigner;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import org.jboss.vfs.util.FilterVirtualFileVisitor;
import org.jboss.vfs.util.MatchAllVirtualFileFilter;
import org.jboss.vfs.util.PathTokenizer;

/**
 * A virtual file.  This is a symbolic reference to a location in the virtual file system hierarchy.  Holding a {@code
 * VirtualFile} instance gives no guarantees as to the presence or immutability of the referenced file or any of its
 * parent path elements.
 *
 * @author [email protected]
 * @author [email protected]
 * @author [email protected]
 * @author David M. Lloyd
 * @version $Revision: 44334 $
 */
public final class VirtualFile implements Serializable {

    private static final long serialVersionUID = 1L;
    private final String name;
    private final String lcname;
    private final VirtualFile parent;
    private final int hashCode;
    private String pathName;

    VirtualFile(String name, VirtualFile parent) {
        this.name = name;
        lcname = name.toLowerCase();
        this.parent = parent;
        int result = parent == null ? 1 : parent.hashCode();
        result = 31 * result + name.hashCode();
        hashCode = result;
    }

    /**
     * Get the simple VF name (X.java)
     *
     * @return the simple file name
     */
    public String getName() {
        return name;
    }

    /**
     * Get the simple VF name mapped to lowercase (x.java) (used by case-insensitive filesystems like ZIP).
     *
     * @return the lowercase simple file name
     * @deprecated should not be used anymore, as the code is case-sensitive from JBVFS-170
     */
    public String getLowerCaseName() {
        return lcname;
    }

    /**
     * Get the absolute VFS full path name (/xxx/yyy/foo.ear/baz.jar/org/jboss/X.java)
     *
     * @return the VFS full path name
     */
    public String getPathName() {
        return getPathName(false);
    }

    /**
     * Get the path name relative to a parent virtual file.  If the given virtual file is not a parent of
     * this virtual file, then an {@code IllegalArgumentException} is thrown.
     *
     * @param parent the parent virtual file
     * @return the relative path name as a string
     * @throws IllegalArgumentException if the given virtual file is not a parent of this virtual file
     */
    public String getPathNameRelativeTo(VirtualFile parent) throws IllegalArgumentException {
        final StringBuilder builder = new StringBuilder(160);
        getPathNameRelativeTo(parent, builder);
        return builder.toString();
    }

    private void getPathNameRelativeTo(VirtualFile parent, StringBuilder builder) {
        if (this.parent == null) {
            throw VFSMessages.MESSAGES.parentIsNotAncestor(parent);
        }
        if (this.equals(parent)) {
            return;
        }
        if (!this.parent.equals(parent)) {
            this.parent.getPathNameRelativeTo(parent, builder);
            builder.append('/');
        }
        builder.append(name);
    }

    /**
     * Get the absolute VFS full path name. If this is a URL then directory entries will have a trailing slash.
     *
     * @param url whether or not this path is being used for a URL
     * @return the VFS full path name
     */
    String getPathName(boolean url) {
        if (pathName ==null || pathName.isEmpty()) {
            VirtualFile[] path = getParentFiles();
            final StringBuilder builder = new StringBuilder(path.length*30+50);
            for (int i=path.length-1;i>-1;i--) {
                final VirtualFile parent = path[i].parent;
                if (parent == null){
                    builder.append(path[i].name);
                }else{
                    if (parent.parent != null) {
                        builder.append('/');
                    }
                    builder.append(path[i].name);
                }
            }
            pathName = builder.toString();
        }
        // Perhaps this should be cached to avoid the fs stat call?
        return (url && isDirectory())? pathName.concat("/"): pathName;
    }

    /**
     * When the file was last modified
     *
     * @return the last modified time
     */
    public long getLastModified() {
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new VirtualFilePermission(getPathName(), "read"));
        }
        final VFS.Mount mount = VFS.getMount(this);
        if (sm != null) {
            final VirtualFile target = this;
            return AccessController.doPrivileged(new PrivilegedAction() {
                @Override
                public Long run() {
                    return mount.getFileSystem().getLastModified(mount.getMountPoint(), target);
                }
            });
        }
        return mount.getFileSystem().getLastModified(mount.getMountPoint(), this);
    }

    /**
     * Get the size
     *
     * @return the size
     */
    public long getSize() {
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new VirtualFilePermission(getPathName(), "read"));
        }
        final VFS.Mount mount = VFS.getMount(this);
        if (sm != null) {
            final VirtualFile target = this;
            return AccessController.doPrivileged(new PrivilegedAction() {
                @Override
                public Long run() {
                    return mount.getFileSystem().getSize(mount.getMountPoint(), target);
                }
            });
        }
        return mount.getFileSystem().getSize(mount.getMountPoint(), this);
    }

    /**
     * Tests whether the underlying implementation file still exists.
     *
     * @return true if the file exists, false otherwise.
     */
    public boolean exists() {
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new VirtualFilePermission(getPathName(), "read"));
        }
        final VFS.Mount mount = VFS.getMount(this);
        if (sm != null) {
            final VirtualFile target = this;
            return AccessController.doPrivileged(new PrivilegedAction() {
                @Override
                public Boolean run() {
                    return mount.getFileSystem().exists(mount.getMountPoint(), target);
                }
            });
        }
        return mount.getFileSystem().exists(mount.getMountPoint(), this);
    }

    /**
     * Determines whether this virtual file represents a true root of a file system.
     * On UNIX, there is only one root "/". Howevever, on Windows there are an infinite
     * number of roots that correspond to drives, or UNC paths.
     *
     * @return {@code true} if this represents a root.
     */
    public boolean isRoot() {
        return parent == null;
    }

    /**
     * Whether it is a simple leaf of the VFS, i.e. whether it can contain other files
     *
     * @return {@code true} if a simple file
     * @deprecated use {@link #isDirectory()} or {@link #isFile()} instead
     */
    @Deprecated
    public boolean isLeaf() {
        return isFile();
    }

    /**
     * Determine whether the named virtual file is a plain file.
     *
     * @return {@code true} if it is a plain file, {@code false} otherwise
     */
    public boolean isFile() {
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new VirtualFilePermission(getPathName(), "read"));
        }
        final VFS.Mount mount = VFS.getMount(this);
        if (sm != null) {
            final VirtualFile target = this;
            return AccessController.doPrivileged(new PrivilegedAction() {
                @Override
                public Boolean run() {
                    return mount.getFileSystem().isFile(mount.getMountPoint(), target);
                }
            });
        }
        return mount.getFileSystem().isFile(mount.getMountPoint(), this);
    }

    /**
     * Determine whether the named virtual file is a directory.
     *
     * @return {@code true} if it is a directory, {@code false} otherwise
     */
    public boolean isDirectory() {
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new VirtualFilePermission(getPathName(), "read"));
        }
        final VFS.Mount mount = VFS.getMount(this);
        if (sm != null) {
            final VirtualFile target = this;
            return AccessController.doPrivileged(new PrivilegedAction() {
                @Override
                public Boolean run() {
                    return mount.getFileSystem().isDirectory(mount.getMountPoint(), target);
                }
            });
        }
        return mount.getFileSystem().isDirectory(mount.getMountPoint(), this);
    }

    /**
     * Access the file contents.
     *
     * @return an InputStream for the file contents.
     * @throws IOException for any error accessing the file system
     */
    public InputStream openStream() throws IOException {
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new VirtualFilePermission(getPathName(), "read"));
        }
        if (isDirectory()) {
            return new VirtualJarInputStream(this);
        }
        final VFS.Mount mount = VFS.getMount(this);
        if (sm != null) {
            final VirtualFile target = this;
            return doIoPrivileged(new PrivilegedExceptionAction() {
                @Override
                public InputStream run() throws Exception {
                    return mount.getFileSystem().openInputStream(mount.getMountPoint(), target);
                }
            });
        }
        return mount.getFileSystem().openInputStream(mount.getMountPoint(), this);
    }

    /**
     * Delete this virtual file
     *
     * @return {@code true} if file was deleted
     */
    public boolean delete() {
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new VirtualFilePermission(getPathName(), "delete"));
        }
        final VFS.Mount mount = VFS.getMount(this);
        if (sm != null) {
            final VirtualFile target = this;
            return AccessController.doPrivileged(new PrivilegedAction() {
                @Override
                public Boolean run() {
                    return mount.getFileSystem().delete(mount.getMountPoint(), target);
                }
            });
        }
        return mount.getFileSystem().delete(mount.getMountPoint(), this);
    }

    /**
     * Get a physical file for this virtual file.  Depending on the underlying file system type, this may simply return
     * an already-existing file; it may create a copy of a file; or it may reuse a preexisting copy of the file.
     * Furthermore, the returned file may or may not have any relationship to other files from the same or any other
     * virtual directory.
     *
     * @return the physical file
     * @throws IOException if an I/O error occurs while producing the physical file
     */
    public File getPhysicalFile() throws IOException {
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new VirtualFilePermission(getPathName(), "getfile"));
        }
        final VFS.Mount mount = VFS.getMount(this);
        if (sm != null) {
            final VirtualFile target = this;
            return doIoPrivileged(new PrivilegedExceptionAction() {
                @Override
                public File run() throws Exception {
                    return mount.getFileSystem().getFile(mount.getMountPoint(), target);
                }
            });
        }
        return mount.getFileSystem().getFile(mount.getMountPoint(), this);
    }

    private static  T doIoPrivileged(PrivilegedExceptionAction action) throws IOException {
        try {
            return AccessController.doPrivileged(action);
        } catch (PrivilegedActionException pe) {
            try {
                throw pe.getException();
            } catch (IOException e) {
                throw e;
            } catch (RuntimeException e) {
                throw e;
            } catch (Exception e) {
                throw new UndeclaredThrowableException(e);
            }
        }
    }

    /**
     * Get a {@code VirtualFile} which represents the parent of this instance.
     *
     * @return the parent or {@code null} if there is no parent
     */
    public VirtualFile getParent() {
        return parent;
    }

    /**
     * Get the all the parent files of this virtual file from this file to the root.  The leafmost file will be at the
     * start of the array, and the rootmost will be at the end.
     *
     * @return the array of parent files
     */
    public VirtualFile[] getParentFiles() {
        return getParentFiles(0);
    }

    /**
     * Get the all the parent files of this virtual file from this file to the root as a list.  The leafmost file will be
     * at the start of the list, and the rootmost will be at the end.
     *
     * @return the list of parent files
     */
    public List getParentFileList() {
        return Arrays.asList(getParentFiles());
    }

    private VirtualFile[] getParentFiles(int idx) {
        final VirtualFile[] array;
        if (parent == null) {
            array = new VirtualFile[idx + 1];
        } else {
            array = parent.getParentFiles(idx + 1);
        }
        array[idx] = this;
        return array;
    }

    /**
     * Get the children.  This is the combined list of real children within this directory, as well as virtual children
     * created by submounts.
     *
     * @return the children
     */
    public List getChildren() {
        // isDirectory does the read security check
        if (!isDirectory()) { return Collections.emptyList(); }
        final VFS.Mount mount = VFS.getMount(this);
        final Set submounts = VFS.getSubmounts(this);
        final List names = mount.getFileSystem().getDirectoryEntries(mount.getMountPoint(), this);
        final List virtualFiles = new ArrayList(names.size() + submounts.size());
        for (String name : names) {
            final VirtualFile child = new VirtualFile(name, this);
            virtualFiles.add(child);
            submounts.remove(name);
        }
        for (String name : submounts) {
            final VirtualFile child = new VirtualFile(name, this);
            virtualFiles.add(child);
        }
        return virtualFiles;
    }

    /**
     * Get the children
     *
     * @param filter to filter the children
     * @return the children
     * @throws IOException           for any problem accessing the virtual file system
     * @throws IllegalStateException if the file is closed or it is a leaf node
     */
    public List getChildren(VirtualFileFilter filter) throws IOException {
        // isDirectory does the read security check
        if (!isDirectory()) { return Collections.emptyList(); }
        if (filter == null) { filter = MatchAllVirtualFileFilter.INSTANCE; }
        FilterVirtualFileVisitor visitor = new FilterVirtualFileVisitor(filter, null);
        visit(visitor);
        return visitor.getMatched();
    }

    /**
     * Get all the children recursively

*

* This always uses {@link VisitorAttributes#RECURSE} * * @return the children * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed */ public List getChildrenRecursively() throws IOException { return getChildrenRecursively(null); } /** * Get all the children recursively

*

* This always uses {@link VisitorAttributes#RECURSE} * * @param filter to filter the children * @return the children * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed or it is a leaf node */ public List getChildrenRecursively(VirtualFileFilter filter) throws IOException { // isDirectory does the read security check if (!isDirectory()) { return Collections.emptyList(); } if (filter == null) { filter = MatchAllVirtualFileFilter.INSTANCE; } FilterVirtualFileVisitor visitor = new FilterVirtualFileVisitor(filter, VisitorAttributes.RECURSE); visit(visitor); return visitor.getMatched(); } /** * Visit the virtual file system * * @param visitor the visitor * @throws IOException for any problem accessing the virtual file system * @throws IllegalArgumentException if the visitor is null * @throws IllegalStateException if the file is closed */ public void visit(VirtualFileVisitor visitor) throws IOException { visit(visitor, true); } private void visit(VirtualFileVisitor visitor, boolean root) throws IOException { final VisitorAttributes visitorAttributes = visitor.getAttributes(); if (root && visitorAttributes.isIncludeRoot()) { visitor.visit(this); } // isDirectory does the read security check if (!isDirectory()) { return; } for (VirtualFile child : getChildren()) { // Always visit a leaf, and visit directories when leaves only is false if (!child.isDirectory() || !visitorAttributes.isLeavesOnly()) { visitor.visit(child); } if (child.isDirectory() && visitorAttributes.isRecurse(child)) { child.visit(visitor, false); } } } /** * Get a child virtual file. The child may or may not exist in the virtual filesystem. * * @param path the path * @return the child * @throws IllegalArgumentException if the path is null */ public VirtualFile getChild(String path) { if (path == null) { throw VFSMessages.MESSAGES.nullArgument("path"); } final List pathParts = PathTokenizer.getTokens(path); VirtualFile current = this; for (String part : pathParts) { if (PathTokenizer.isReverseToken(part)) { final VirtualFile parent = current.parent; current = parent == null ? current : parent; } else if (PathTokenizer.isCurrentToken(part) == false) { current = new VirtualFile(part, current); } } return current; } /** * Get file's current URL. Note: if this VirtualFile refers to a directory at the time of this * method invocation, a trailing slash will be appended to the URL; this means that invoking * this method may require a filesystem access, and in addition, may not produce consistent results * over time. * * @return the current url * @throws MalformedURLException if the URL is somehow malformed * @see VirtualFile#asDirectoryURL() * @see VirtualFile#asFileURL() */ public URL toURL() throws MalformedURLException { return VFSUtils.getVirtualURL(this); } /** * Get file's current URI. Note: if this VirtualFile refers to a directory at the time of this * method invocation, a trailing slash will be appended to the URI; this means that invoking * this method may require a filesystem access, and in addition, may not produce consistent results * over time. * * @return the current uri * @throws URISyntaxException if the URI is somehow malformed * @see VirtualFile#asDirectoryURI() * @see VirtualFile#asFileURI() */ public URI toURI() throws URISyntaxException { return VFSUtils.getVirtualURI(this); } /** * Get file's URL as a directory. There will always be a trailing {@code "/"} character. * * @return the url * @throws MalformedURLException if the URL is somehow malformed */ public URL asDirectoryURL() throws MalformedURLException { final String pathName = getPathName(false); return new URL(VFSUtils.VFS_PROTOCOL, "", -1, parent == null ? pathName : pathName + "/", VFSUtils.VFS_URL_HANDLER); } /** * Get file's URI as a directory. There will always be a trailing {@code "/"} character. * * @return the uri * @throws URISyntaxException if the URI is somehow malformed */ public URI asDirectoryURI() throws URISyntaxException { final String pathName = getPathName(false); return new URI(VFSUtils.VFS_PROTOCOL, "", parent == null ? pathName : pathName + "/", null); } /** * Get file's URL as a file. There will be no trailing {@code "/"} character unless this {@code VirtualFile} * represents a root. * * @return the url * @throws MalformedURLException if the URL is somehow malformed */ public URL asFileURL() throws MalformedURLException { return new URL(VFSUtils.VFS_PROTOCOL, "", -1, getPathName(false), VFSUtils.VFS_URL_HANDLER); } /** * Get file's URI as a file. There will be no trailing {@code "/"} character unless this {@code VirtualFile} * represents a root. * * @return the url * @throws URISyntaxException if the URI is somehow malformed */ public URI asFileURI() throws URISyntaxException { return new URI(VFSUtils.VFS_PROTOCOL, "", getPathName(false), null); } /** * Get the {@link CodeSigner}s for a the virtual file. * * @return the {@link CodeSigner}s for the virtual file, or {@code null} if not signed */ public CodeSigner[] getCodeSigners() { final SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new VirtualFilePermission(getPathName(), "read")); } final VFS.Mount mount = VFS.getMount(this); return mount.getFileSystem().getCodeSigners(mount.getMountPoint(), this); } /** * Get the {@link Certificate}s for the virtual file. Simply extracts the certificate entries from * the code signers array. * * @return the certificates for the virtual file, or {@code null} if not signed */ public Certificate[] getCertificates() { // getCodeSigners() does the sec check final CodeSigner[] codeSigners = getCodeSigners(); if (codeSigners == null) { return null; } final List certList = new ArrayList(codeSigners.length * 3); for (CodeSigner signer : codeSigners) { certList.addAll(signer.getSignerCertPath().getCertificates()); } return certList.toArray(new Certificate[certList.size()]); } /** * Get a human-readable (but non-canonical) representation of this virtual file. * * @return the string */ public String toString() { return '"' + getPathName() + '"'; } /** * Determine whether the given object is equal to this one. Returns true if the argument is a {@code VirtualFile} * from the same {@code VFS} instance with the same name. * * @param o the other object * @return {@code true} if they are equal */ public boolean equals(Object o) { return o instanceof VirtualFile && equals((VirtualFile) o); } /** * Determine whether the given object is equal to this one. Returns true if the argument is a {@code VirtualFile} * from the same {@code VFS} instance with the same name. * * @param o the other virtual file * @return {@code true} if they are equal */ public boolean equals(VirtualFile o) { if (o == this) { return true; } if (o == null || hashCode != o.hashCode || !name.equals(o.name)) { return false; } final VirtualFile parent = this.parent; final VirtualFile oparent = o.parent; return parent != null && parent.equals(oparent) || oparent == null; } /** * Get a hashcode for this virtual file. * * @return the hash code */ public int hashCode() { return hashCode; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy