--- /dev/null
+
+/* ====================================================================
+ 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 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.apache.poi.poifs.filesystem;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.poi.hpsf.ClassID;
+
+/**
+ * A DirectoryEntry filter, which exposes another
+ * DirectoryEntry less certain parts.
+ * This is typically used when copying or comparing
+ * Filesystems.
+ */
+public class FilteringDirectoryNode implements DirectoryEntry
+{
+ /**
+ * The names of our entries to exclude
+ */
+ private Set<String> excludes;
+ /**
+ * Excludes of our child directories
+ */
+ private Map<String,List<String>> childExcludes;
+
+ private DirectoryEntry directory;
+
+ /**
+ * Creates a filter round the specified directory, which
+ * will exclude entries such as "MyNode" and "MyDir/IgnoreNode".
+ * The excludes can stretch into children, if they contain a /.
+ *
+ * @param entry The Directory to filter
+ * @param excludes The Entries to exclude
+ */
+ public FilteringDirectoryNode(DirectoryEntry directory, Collection<String> excludes) {
+ this.directory = directory;
+
+ // Process the excludes
+ this.excludes = new HashSet<String>();
+ this.childExcludes = new HashMap<String, List<String>>();
+ for (String excl : excludes) {
+ int splitAt = excl.indexOf('/');
+ if (splitAt == -1) {
+ // Applies to us
+ this.excludes.add(excl);
+ } else {
+ // Applies to a child
+ String child = excl.substring(0, splitAt);
+ String childExcl = excl.substring(splitAt+1);
+ if (! this.childExcludes.containsKey(child)) {
+ this.childExcludes.put(child, new ArrayList<String>());
+ }
+ this.childExcludes.get(child).add(childExcl);
+ }
+ }
+ }
+
+ public DirectoryEntry createDirectory(String name) throws IOException {
+ return directory.createDirectory(name);
+ }
+
+ public DocumentEntry createDocument(String name, InputStream stream)
+ throws IOException {
+ return directory.createDocument(name, stream);
+ }
+
+ public DocumentEntry createDocument(String name, int size,
+ POIFSWriterListener writer) throws IOException {
+ return directory.createDocument(name, size, writer);
+ }
+
+ public Iterator<Entry> getEntries() {
+ return new FilteringIterator();
+ }
+
+ public Iterator<Entry> iterator() {
+ return getEntries();
+ }
+
+ public int getEntryCount() {
+ int size = directory.getEntryCount();
+ for (String excl : excludes) {
+ if (directory.hasEntry(excl)) {
+ size--;
+ }
+ }
+ return size;
+ }
+
+ public boolean isEmpty() {
+ return (getEntryCount() == 0);
+ }
+
+ public boolean hasEntry(String name) {
+ if (excludes.contains(name)) {
+ return false;
+ }
+ return directory.hasEntry(name);
+ }
+
+ public Entry getEntry(String name) throws FileNotFoundException {
+ if (excludes.contains(name)) {
+ throw new FileNotFoundException(name);
+ }
+
+ Entry entry = directory.getEntry(name);
+ return wrapEntry(entry);
+ }
+ private Entry wrapEntry(Entry entry) {
+ String name = entry.getName();
+ if (childExcludes.containsKey(name) && entry instanceof DirectoryEntry) {
+ return new FilteringDirectoryNode(
+ (DirectoryEntry)entry, childExcludes.get(name));
+ }
+ return entry;
+ }
+
+ public ClassID getStorageClsid() {
+ return directory.getStorageClsid();
+ }
+
+ public void setStorageClsid(ClassID clsidStorage) {
+ directory.setStorageClsid(clsidStorage);
+ }
+
+ public boolean delete() {
+ return directory.delete();
+ }
+
+ public boolean renameTo(String newName) {
+ return directory.renameTo(newName);
+ }
+
+ public String getName() {
+ return directory.getName();
+ }
+
+ public DirectoryEntry getParent() {
+ return directory.getParent();
+ }
+
+ public boolean isDirectoryEntry() {
+ return true;
+ }
+
+ public boolean isDocumentEntry() {
+ return false;
+ }
+
+ private class FilteringIterator implements Iterator<Entry> {
+ private Iterator<Entry> parent;
+ private Entry next;
+
+ private FilteringIterator() {
+ parent = directory.getEntries();
+ locateNext();
+ }
+ private void locateNext() {
+ next = null;
+ Entry e;
+ while (parent.hasNext() && next == null) {
+ e = parent.next();
+ if (! excludes.contains(e.getName())) {
+ next = wrapEntry(e);
+ }
+ }
+ }
+
+ public boolean hasNext() {
+ return (next != null);
+ }
+
+ public Entry next() {
+ Entry e = next;
+ locateNext();
+ return e;
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("Remove not supported");
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+
+/* ====================================================================
+ 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 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.apache.poi.poifs.filesystem;
+
+import java.io.ByteArrayInputStream;
+import java.io.FileNotFoundException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+/**
+ * Class to test FilteringDirectoryNode functionality
+ */
+public final class TestFilteringDirectoryNode extends TestCase {
+ private POIFSFileSystem fs;
+ private DirectoryEntry dirA;
+ private DirectoryEntry dirAA;
+ private DirectoryEntry dirB;
+ private DocumentEntry eRoot;
+ private DocumentEntry eA;
+ private DocumentEntry eAA;
+
+ @Override
+ protected void setUp() throws Exception {
+ fs = new POIFSFileSystem();
+ dirA = fs.createDirectory("DirA");
+ dirB = fs.createDirectory("DirB");
+ dirAA = dirA.createDirectory("DirAA");
+ eRoot = fs.getRoot().createDocument("Root", new ByteArrayInputStream(new byte[] {}));
+ eA = dirA.createDocument("NA", new ByteArrayInputStream(new byte[] {}));
+ eAA = dirAA.createDocument("NAA", new ByteArrayInputStream(new byte[] {}));
+ }
+
+ public void testNoFiltering() throws Exception {
+ FilteringDirectoryNode d = new FilteringDirectoryNode(fs.getRoot(), new HashSet<String>());
+ assertEquals(3, d.getEntryCount());
+ assertEquals(dirA.getName(), d.getEntry(dirA.getName()).getName());
+
+ assertEquals(true, d.getEntry(dirA.getName()).isDirectoryEntry());
+ assertEquals(false, d.getEntry(dirA.getName()).isDocumentEntry());
+
+ assertEquals(true, d.getEntry(dirB.getName()).isDirectoryEntry());
+ assertEquals(false, d.getEntry(dirB.getName()).isDocumentEntry());
+
+ assertEquals(false, d.getEntry(eRoot.getName()).isDirectoryEntry());
+ assertEquals(true, d.getEntry(eRoot.getName()).isDocumentEntry());
+
+ Iterator<Entry> i = d.getEntries();
+ assertEquals(dirA, i.next());
+ assertEquals(dirB, i.next());
+ assertEquals(eRoot, i.next());
+ assertEquals(null, i.next());
+ }
+
+ public void testChildFiltering() throws Exception {
+ List<String> excl = Arrays.asList(new String[] {"NotThere","AlsoNotThere", eRoot.getName()});
+ FilteringDirectoryNode d = new FilteringDirectoryNode(fs.getRoot(), excl);
+
+ assertEquals(2, d.getEntryCount());
+ assertEquals(true, d.hasEntry(dirA.getName()));
+ assertEquals(true, d.hasEntry(dirB.getName()));
+ assertEquals(false, d.hasEntry(eRoot.getName()));
+
+ assertEquals(dirA, d.getEntry(dirA.getName()));
+ assertEquals(dirB, d.getEntry(dirB.getName()));
+ try {
+ d.getEntry(eRoot.getName());
+ fail("Should be filtered");
+ } catch(FileNotFoundException e) {}
+
+ Iterator<Entry> i = d.getEntries();
+ assertEquals(dirA, i.next());
+ assertEquals(dirB, i.next());
+ assertEquals(null, i.next());
+
+
+ // Filter more
+ excl = Arrays.asList(new String[] {"NotThere","AlsoNotThere", eRoot.getName(), dirA.getName()});
+ d = new FilteringDirectoryNode(fs.getRoot(), excl);
+
+ assertEquals(1, d.getEntryCount());
+ assertEquals(false, d.hasEntry(dirA.getName()));
+ assertEquals(true, d.hasEntry(dirB.getName()));
+ assertEquals(false, d.hasEntry(eRoot.getName()));
+
+ try {
+ d.getEntry(dirA.getName());
+ fail("Should be filtered");
+ } catch(FileNotFoundException e) {}
+ assertEquals(dirB, d.getEntry(dirB.getName()));
+ try {
+ d.getEntry(eRoot.getName());
+ fail("Should be filtered");
+ } catch(FileNotFoundException e) {}
+
+ i = d.getEntries();
+ assertEquals(dirB, i.next());
+ assertEquals(null, i.next());
+
+
+ // Filter everything
+ excl = Arrays.asList(new String[] {"NotThere", eRoot.getName(), dirA.getName(), dirB.getName()});
+ d = new FilteringDirectoryNode(fs.getRoot(), excl);
+
+ assertEquals(0, d.getEntryCount());
+ assertEquals(false, d.hasEntry(dirA.getName()));
+ assertEquals(false, d.hasEntry(dirB.getName()));
+ assertEquals(false, d.hasEntry(eRoot.getName()));
+
+ try {
+ d.getEntry(dirA.getName());
+ fail("Should be filtered");
+ } catch(FileNotFoundException e) {}
+ try {
+ d.getEntry(dirB.getName());
+ fail("Should be filtered");
+ } catch(FileNotFoundException e) {}
+ try {
+ d.getEntry(eRoot.getName());
+ fail("Should be filtered");
+ } catch(FileNotFoundException e) {}
+
+ i = d.getEntries();
+ assertEquals(null, i.next());
+ }
+
+ public void testNestedFiltering() throws Exception {
+ List<String> excl = Arrays.asList(new String[] {
+ dirA.getName()+"/"+"MadeUp",
+ dirA.getName()+"/"+eA.getName(),
+ dirA.getName()+"/"+dirAA.getName()+"/Test",
+ eRoot.getName()
+ });
+ FilteringDirectoryNode d = new FilteringDirectoryNode(fs.getRoot(), excl);
+
+ // Check main
+ assertEquals(2, d.getEntryCount());
+ assertEquals(true, d.hasEntry(dirA.getName()));
+ assertEquals(true, d.hasEntry(dirB.getName()));
+ assertEquals(false, d.hasEntry(eRoot.getName()));
+
+ // Check filtering down
+ assertEquals(true, d.getEntry(dirA.getName()) instanceof FilteringDirectoryNode);
+ assertEquals(false, d.getEntry(dirB.getName()) instanceof FilteringDirectoryNode);
+
+ DirectoryEntry fdA = (DirectoryEntry)d.getEntry(dirA.getName());
+ assertEquals(false, fdA.hasEntry(eA.getName()));
+ assertEquals(true, fdA.hasEntry(dirAA.getName()));
+
+ DirectoryEntry fdAA = (DirectoryEntry)fdA.getEntry(dirAA.getName());
+ assertEquals(true, fdAA.hasEntry(eAA.getName()));
+ }
+}