001/*-------------------------------------------------------------------------+
002|                                                                          |
003| Copyright 2005-2011 The ConQAT Project                                   |
004|                                                                          |
005| Licensed under the Apache License, Version 2.0 (the "License");          |
006| you may not use this file except in compliance with the License.         |
007| You may obtain a copy of the License at                                  |
008|                                                                          |
009|    http://www.apache.org/licenses/LICENSE-2.0                            |
010|                                                                          |
011| Unless required by applicable law or agreed to in writing, software      |
012| distributed under the License is distributed on an "AS IS" BASIS,        |
013| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
014| See the License for the specific language governing permissions and      |
015| limitations under the License.                                           |
016+-------------------------------------------------------------------------*/
017package org.conqat.lib.commons.filesystem;
018
019import java.io.Closeable;
020import java.io.File;
021import java.io.IOException;
022import java.io.InputStream;
023import java.util.ArrayList;
024import java.util.Collection;
025
026/**
027 * Base class for objects that provide access to the content of various elements
028 * using a path for access. This basically abstracts the differences between
029 * plain directories and ZIP files (containing multiple entries).
030 */
031public abstract class PathBasedContentProviderBase implements Closeable {
032
033        /**
034         * Returns the relative paths to all available elements that carry content.
035         * These correspond to file names or names of ZIP file entries. The returned
036         * paths are guaranteed to use a forward slash as the path separator.
037         */
038        public abstract Collection<String> getPaths() throws IOException;
039
040        /**
041         * {@inheritDoc}
042         * <p>
043         * This closes the content provider itself, i.e. after closing, no new streams
044         * may be opened. Whether existing streams are closed depends on the
045         * implementation, but it is recommended to close any streams returned from
046         * {@link #openStream(String)} yourself and to not use any of these streams
047         * after calling this method.
048         */
049        @Override
050        public abstract void close() throws IOException;
051
052        /**
053         * Opens a stream to the element identified by the given path. This should be
054         * one of the paths returned by the {@link #getPaths()} method. The returned
055         * stream must be closed by the caller.
056         */
057        public abstract InputStream openStream(String relativePath) throws IOException;
058
059        /** Returns all paths starting with a given prefix. */
060        public Collection<String> getPathsWithPrefix(String prefix) throws IOException {
061                Collection<String> result = new ArrayList<>();
062                for (String path : getPaths()) {
063                        if (path.startsWith(prefix)) {
064                                result.add(path);
065                        }
066                }
067                return result;
068        }
069
070        /**
071         * Creates a suitable provider for a file. This works for directories as well as
072         * ZIP/JAR files. The returned provider has to be closed at the end.
073         *
074         * @throws IOException
075         *             if accessing the file fails or the file is not of suitable format
076         *             (i.e. no directory or ZIP file).
077         */
078        public static PathBasedContentProviderBase createProvider(File file) throws IOException {
079                if (file.isDirectory()) {
080                        return new DirectoryContentProvider(file);
081                }
082
083                return new ZipContentProvider(new ZipFile(file));
084        }
085}