org.jszip.pseudo.io.PseudoFileSystem.java Source code

Java tutorial

Introduction

Here is the source code for org.jszip.pseudo.io.PseudoFileSystem.java

Source

/*
 * Copyright 2011-2012 Stephen Connolly.
 *
 * 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.jszip.pseudo.io;

import org.apache.commons.lang3.StringUtils;
import org.codehaus.plexus.archiver.zip.ZipEntry;
import org.codehaus.plexus.archiver.zip.ZipFile;
import org.mozilla.javascript.Context;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

public class PseudoFileSystem {
    /**
     * Secret key used to hold the reference to the pseudo filesystem.
     */
    private static final Object KEY = new Object();

    private final PseudoFile root = new VirtualDirectoryPseudoFile(null, "");

    private final Layer[] layers;

    public PseudoFileSystem(Layer... layers) {
        this.layers = layers;
    }

    public PseudoFileSystem(List<Layer> layers) {
        this(layers.toArray(new Layer[layers.size()]));
    }

    public String getPathSeparator() {
        return "/";
    }

    public PseudoFile[] listChildren(PseudoFile dir, PseudoFileFilter filter) {
        TreeMap<String, Layer> names = new TreeMap<String, Layer>();
        final String path = dir.getAbsolutePath(this);
        for (int i = layers.length - 1; i >= 0; i--) {
            for (String name : layers[i].listChildren(path)) {
                names.put(name, layers[i]);
            }
        }
        List<PseudoFile> result = new ArrayList<PseudoFile>(names.size());
        for (Map.Entry<String, Layer> entry : names.entrySet()) {
            if (filter.accept(entry.getKey())) {
                result.add(entry.getValue().makeChild(this, dir, entry.getKey()));
            }
        }
        return result.toArray(new PseudoFile[result.size()]);
    }

    public PseudoFile root() {
        return root;
    }

    public PseudoFile getPseudoFile(String filename) {
        filename = StringUtils.removeEnd(filename, getPathSeparator());
        if (filename.isEmpty()) {
            return root();
        }
        int index = filename.lastIndexOf(getPathSeparator());
        if (index != -1) {
            return getPseudoFile(getPseudoFile(filename.substring(0, index)), filename.substring(index + 1));
        }
        return getPseudoFile(root(), filename);
    }

    public PseudoFile getPseudoFile(PseudoFile parent, String name) {
        if (name.equals(".")) {
            return parent;
        }
        if (name.equals("..")) {
            return parent.getParentFile();
        }
        String parentPath = parent.getAbsolutePath(this);
        for (Layer layer : layers) {
            if (layer.listChildren(parentPath).contains(name)) {
                return layer.makeChild(this, parent, name);
            }
        }
        if (layers.length == 0) {
            return new VirtualDirectoryPseudoFile(parent, name);
        }
        return layers[0].makeChild(this, parent, name);
    }

    public synchronized void installInContext() {
        Context.getCurrentContext().putThreadLocal(KEY, this);
    }

    public synchronized void removeFromContext() {
        final Context context = Context.getCurrentContext();
        if (context != null) {
            context.putThreadLocal(KEY, null);
        }
    }

    public static PseudoFileSystem current() {
        final Context currentContext = Context.getCurrentContext();
        return currentContext != null ? (PseudoFileSystem) currentContext.getThreadLocal(KEY) : null;
    }

    public abstract static class Layer {

        public abstract List<String> listChildren(String relativePath);

        public PseudoFile makeChild(PseudoFile parent, String name) {
            return makeChild(PseudoFileSystem.current(), parent, name);
        }

        public abstract PseudoFile makeChild(PseudoFileSystem fs, PseudoFile parent, String name);

    }

    public static class FileLayer extends Layer {
        private final String prefix;
        private final File root;

        public FileLayer(File root) {
            this("", root);
        }

        public FileLayer(String prefix, File root) {
            this.prefix = StringUtils.isEmpty(prefix) ? "/"
                    : "/" + StringUtils.removeEnd(StringUtils.removeStart(prefix, "/"), "/") + "/";
            this.root = root;
        }

        @Override
        public List<String> listChildren(String relativePath) {
            relativePath = StringUtils.removeEnd(relativePath, "/") + "/";
            if (relativePath.startsWith(prefix)) {
                final String pathFragment = relativePath.substring(prefix.length());
                final String[] list = new File(root, pathFragment).list();
                return list == null ? Collections.<String>emptyList() : Arrays.asList(list);
            }
            if (prefix.startsWith(relativePath)) {
                int index = prefix.indexOf('/', relativePath.length() + 1);
                if (index != -1) {
                    return Collections.singletonList(prefix.substring(relativePath.length(), index));
                }
            }
            return Collections.emptyList();
        }

        @Override
        public PseudoFile makeChild(PseudoFileSystem fs, PseudoFile parent, String name) {
            String relativePath = StringUtils.removeEnd(parent.getAbsolutePath(fs), "/") + "/" + name;
            if (relativePath.startsWith(prefix)) {
                return new FilePseudoFile(parent, new File(root, relativePath.substring(prefix.length())));
            }
            if (prefix.equals(relativePath + "/")) {
                int lastIndex = prefix.lastIndexOf('/');
                int index = prefix.lastIndexOf('/', lastIndex - 1);
                return new AliasFilePseudoFile(parent, root, prefix.substring(index + 1, lastIndex));
            }
            if (!StringUtils.isEmpty(prefix) && prefix.startsWith(relativePath)) {
                return new VirtualDirectoryPseudoFile(parent, name);
            }
            return new NotExistingPseudoFile(parent, name);
        }

        @Override
        public String toString() {
            final StringBuilder sb = new StringBuilder();
            sb.append("FileLayer");
            sb.append("{prefix='").append(prefix).append('\'');
            sb.append(", root=").append(root);
            sb.append('}');
            return sb.toString();
        }
    }

    public static class ZipLayer extends PseudoFileSystem.Layer {
        private final String prefix;
        private final File zipFile;
        private final Map<String, ZipEntry> contents;

        public ZipLayer(String prefix, File zipFile) throws IOException {
            this.prefix = StringUtils.isEmpty(prefix) ? "/"
                    : "/" + StringUtils.removeEnd(StringUtils.removeStart(prefix, "/"), "/") + "/";
            this.zipFile = zipFile;
            ZipFile file = new ZipFile(zipFile);
            Map<String, ZipEntry> contents = new TreeMap<String, ZipEntry>();
            Enumeration<ZipEntry> entries = file.getEntries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = entries.nextElement();
                contents.put(this.prefix + StringUtils.removeStart(entry.getName(), "/"), entry);
            }

            this.contents = contents;
        }

        @Override
        public List<String> listChildren(String relativePath) {
            relativePath = StringUtils.removeEnd(relativePath, "/") + "/";
            if (relativePath.startsWith(prefix) || prefix.equals(relativePath + "/")) {
                final String pathFragment = StringUtils.removeEnd(relativePath, "/") + "/";
                Set<String> result = new LinkedHashSet<String>();
                for (String path : contents.keySet()) {
                    if (path.startsWith(pathFragment)) {
                        int index = path.indexOf('/', pathFragment.length());
                        if (index == -1) {
                            result.add(path.substring(pathFragment.length()));
                        } else {
                            result.add(path.substring(pathFragment.length(), index));
                        }
                    }
                }
                return new ArrayList<String>(result);
            }
            if (prefix.startsWith(relativePath)) {
                int index = prefix.indexOf('/', relativePath.length());
                if (index != -1) {
                    return Collections.singletonList(prefix.substring(relativePath.length(), index));
                }
            }
            return Collections.emptyList();
        }

        @Override
        public PseudoFile makeChild(PseudoFileSystem fs, PseudoFile parent, String name) {
            String relativePath = StringUtils.removeEnd(parent.getAbsolutePath(fs), "/") + "/" + name;
            final ZipEntry entry = contents.get(relativePath);
            if (entry != null) {
                return new ZipPseudoFile(parent, zipFile, entry);
            }
            if (prefix.equals(relativePath + "/")) {
                return new VirtualDirectoryPseudoFile(parent, name);
            }
            if (!StringUtils.isEmpty(prefix) && prefix.startsWith(relativePath)) {
                return new VirtualDirectoryPseudoFile(parent, name);
            }
            for (String childPath : contents.keySet()) {
                if (!StringUtils.isEmpty(childPath) && childPath.startsWith(relativePath)) {
                    return new VirtualDirectoryPseudoFile(parent, name);
                }
            }
            return new NotExistingPseudoFile(parent, name);
        }

        @Override
        public String toString() {
            final StringBuilder sb = new StringBuilder();
            sb.append("ZipLayer");
            sb.append("{prefix='").append(prefix).append('\'');
            sb.append(", zipFile=").append(zipFile);
            sb.append('}');
            return sb.toString();
        }
    }

}