| /* |
| * 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 WARRANTIESOR 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.aries.util.filesystem.impl; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.net.MalformedURLException; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.zip.ZipEntry; |
| import java.util.zip.ZipFile; |
| |
| import org.apache.aries.util.IORuntimeException; |
| import org.apache.aries.util.filesystem.ICloseableDirectory; |
| import org.apache.aries.util.filesystem.IDirectory; |
| import org.apache.aries.util.filesystem.IFile; |
| |
| /** |
| * A directory in the zip. |
| */ |
| public class ZipDirectory extends ZipFileImpl implements IDirectory |
| { |
| /** The root of the zip FS. */ |
| private final IDirectory root; |
| private final boolean zipRoot; |
| |
| /** |
| * Constructs a directory in the zip. |
| * |
| * @param zip1 the zip file. |
| * @param entry1 the entry in the zip representing this dir. |
| * @param parent the parent directory. |
| */ |
| public ZipDirectory(File zip1, ZipEntry entry1, ZipDirectory parent, ZipCloseableDirectory cache) |
| { |
| super(zip1, entry1, parent, cache); |
| zipRoot = false; |
| root = parent.getRoot(); |
| } |
| |
| /** |
| * This constructor creates the root of the zip. |
| * @param fs |
| * @param parent |
| * @throws MalformedURLException |
| */ |
| public ZipDirectory(File fs, IDirectory parent) throws MalformedURLException |
| { |
| super(fs, parent); |
| root = (parent == null) ? this : parent.getRoot(); |
| zipRoot = true; |
| } |
| |
| public ZipDirectory(ZipDirectory other, ZipCloseableDirectory cache) { |
| super(other, cache); |
| root = other.root; |
| zipRoot = other.zipRoot; |
| } |
| |
| public IFile getFile(String name) |
| { |
| IFile result = null; |
| |
| String entryName = isZipRoot() ? name : getNameInZip() + "/" + name; |
| |
| ZipEntry entryFile = getEntry(entryName); |
| |
| if (entryFile != null) { |
| if (!!!entryFile.isDirectory()) { |
| result = new ZipFileImpl(zip, entryFile, buildParent(entryFile), cache); |
| } else { |
| result = new ZipDirectory(zip, entryFile, buildParent(entryFile), cache); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * This method builds the parent directory hierarchy for a file. |
| * @param foundEntry |
| * @return the parent of the entry. |
| */ |
| private ZipDirectory buildParent(ZipEntry foundEntry) |
| { |
| ZipDirectory result = this; |
| |
| String name = foundEntry.getName(); |
| |
| name = name.substring(getNameInZip().length()); |
| |
| String[] paths = name.split("/"); |
| |
| StringBuilder baseBuilderCrapThingToGetRoundFindBugs = new StringBuilder(getNameInZip()); |
| |
| if (!!!isZipRoot()) baseBuilderCrapThingToGetRoundFindBugs.append('/'); |
| // Build 'result' as a chain of ZipDirectories. This will only work if java.util.ZipFile recognises every |
| // directory in the chain as being a ZipEntry in its own right. |
| outer: if (paths != null && paths.length > 1) { |
| for (int i = 0; i < paths.length - 1; i++) { |
| String path = paths[i]; |
| baseBuilderCrapThingToGetRoundFindBugs.append(path); |
| ZipEntry dirEntry = getEntry(baseBuilderCrapThingToGetRoundFindBugs.toString()); |
| if (dirEntry == null) { |
| result = this; |
| break outer; |
| } |
| result = new ZipDirectory(zip, dirEntry, result, cache); |
| baseBuilderCrapThingToGetRoundFindBugs.append('/'); |
| } |
| } |
| return result; |
| } |
| |
| public boolean isRoot() |
| { |
| return getParent() == null; |
| } |
| |
| public List<IFile> listFiles() |
| { |
| return listFiles(false); |
| } |
| |
| public List<IFile> listAllFiles() |
| { |
| return listFiles(true); |
| } |
| |
| private List<IFile> listFiles(boolean includeFilesInNestedSubdirs) |
| { |
| List<IFile> files = new ArrayList<IFile>(); |
| |
| ZipFile z = openZipFile(); |
| List<? extends ZipEntry> entries = Collections.list(z.entries()); |
| |
| for (ZipEntry possibleEntry : entries) { |
| if (isInDir(getNameInZip(), possibleEntry, includeFilesInNestedSubdirs)) { |
| ZipDirectory parent = includeFilesInNestedSubdirs ? buildParent(possibleEntry) : this; |
| if (possibleEntry.isDirectory()) { |
| files.add(new ZipDirectory(zip, possibleEntry, parent, cache)); |
| } else { |
| files.add(new ZipFileImpl(zip, possibleEntry, parent, cache)); |
| } |
| } |
| |
| } |
| |
| closeZipFile(z); |
| return files; |
| } |
| |
| /** |
| * This method works out if the provided entry is inside this directory. It |
| * returns false if it is not, or if it is in a sub-directory. |
| * |
| * @param parentDir |
| * @param possibleEntry |
| * @param allowSubDirs |
| * @return true if it is in this directory. |
| */ |
| protected static boolean isInDir(String parentDir, ZipEntry possibleEntry, boolean allowSubDirs) |
| { |
| boolean result; |
| String name = possibleEntry.getName(); |
| if (name.endsWith("/")) name = name.substring(0, name.length() - 1); |
| result = (name.startsWith(parentDir) && !!!name.equals(parentDir) && (allowSubDirs || name.substring(parentDir.length() + 1).indexOf('/') == -1)); |
| return result; |
| } |
| |
| public Iterator<IFile> iterator() |
| { |
| return listFiles().iterator(); |
| } |
| |
| public IDirectory convert() |
| { |
| return this; |
| } |
| |
| public boolean isDirectory() |
| { |
| return true; |
| } |
| |
| public boolean isFile() |
| { |
| return false; |
| } |
| |
| public InputStream open() |
| { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public IDirectory getRoot() |
| { |
| return root; |
| } |
| |
| public boolean isZipRoot() { |
| return zipRoot; |
| } |
| |
| // Although we only delegate to our super class if we removed this Findbugs |
| // would correctly point out that we add fields in this class, but do not |
| // take them into account for the equals method. In fact this is not a problem |
| // we do not care about the root when doing an equality check, but by including |
| // an equals or hashCode in here we can clearly document that we did this |
| // on purpose. Hence this comment. |
| @Override |
| public boolean equals(Object other) |
| { |
| return super.equals(other); |
| } |
| |
| @Override |
| public int hashCode() |
| { |
| return super.hashCode(); |
| } |
| |
| private ZipEntry getEntry(String entryName) { |
| ZipFile z = openZipFile(); |
| ZipEntry entryFile = null; |
| |
| if (z != null) { |
| entryFile = z.getEntry(entryName); |
| closeZipFile(z); |
| } |
| return entryFile; |
| } |
| |
| public ICloseableDirectory toCloseable() { |
| try { |
| return new ZipCloseableDirectory(zip, this); |
| } catch (IOException e) { |
| throw new IORuntimeException("IOException opening zip file: " + this, e); |
| } |
| } |
| } |
| |
| |