mirror of
https://github.com/apache/poi.git
synced 2026-02-27 20:40:08 +08:00
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1878730 13f79535-47bb-0310-9956-ffa450edef68
268 lines
9.1 KiB
Java
268 lines
9.1 KiB
Java
/* ====================================================================
|
|
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.EOFException;
|
|
import java.io.IOException;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Objects;
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.StreamSupport;
|
|
|
|
import org.apache.poi.hpsf.MarkUnsupportedException;
|
|
import org.apache.poi.hpsf.NoPropertySetStreamException;
|
|
import org.apache.poi.hpsf.PropertySet;
|
|
import org.apache.poi.hpsf.PropertySetFactory;
|
|
import org.apache.poi.util.Internal;
|
|
|
|
@Internal
|
|
public final class EntryUtils {
|
|
private EntryUtils() {}
|
|
|
|
/**
|
|
* Copies an Entry into a target POIFS directory, recursively
|
|
*/
|
|
@Internal
|
|
public static void copyNodeRecursively( Entry entry, DirectoryEntry target )
|
|
throws IOException {
|
|
if ( entry.isDirectoryEntry() ) {
|
|
DirectoryEntry dirEntry = (DirectoryEntry)entry;
|
|
DirectoryEntry newTarget = target.createDirectory( entry.getName() );
|
|
newTarget.setStorageClsid( dirEntry.getStorageClsid() );
|
|
Iterator<Entry> entries = dirEntry.getEntries();
|
|
|
|
while ( entries.hasNext() ) {
|
|
copyNodeRecursively( entries.next(), newTarget );
|
|
}
|
|
} else {
|
|
DocumentEntry dentry = (DocumentEntry) entry;
|
|
DocumentInputStream dstream = new DocumentInputStream( dentry );
|
|
target.createDocument( dentry.getName(), dstream );
|
|
dstream.close();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Copies all the nodes from one POIFS Directory to another
|
|
*
|
|
* @param sourceRoot
|
|
* is the source Directory to copy from
|
|
* @param targetRoot
|
|
* is the target Directory to copy to
|
|
*/
|
|
public static void copyNodes(DirectoryEntry sourceRoot, DirectoryEntry targetRoot)
|
|
throws IOException {
|
|
for (Entry entry : sourceRoot) {
|
|
copyNodeRecursively( entry, targetRoot );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Copies all nodes from one POIFS to the other
|
|
*
|
|
* @param source
|
|
* is the source POIFS to copy from
|
|
* @param target
|
|
* is the target POIFS to copy to
|
|
*/
|
|
public static void copyNodes(POIFSFileSystem source, POIFSFileSystem target )
|
|
throws IOException {
|
|
copyNodes( source.getRoot(), target.getRoot() );
|
|
}
|
|
|
|
/**
|
|
* Copies nodes from one POIFS to the other, minus the excepts.
|
|
* This delegates the filtering work to {@link FilteringDirectoryNode},
|
|
* so excepts can be of the form "NodeToExclude" or
|
|
* "FilteringDirectory/ExcludedChildNode"
|
|
*
|
|
* @param source is the source POIFS to copy from
|
|
* @param target is the target POIFS to copy to
|
|
* @param excepts is a list of Entry Names to be excluded from the copy
|
|
*/
|
|
public static void copyNodes(POIFSFileSystem source, POIFSFileSystem target, List<String> excepts )
|
|
throws IOException {
|
|
copyNodes(
|
|
new FilteringDirectoryNode(source.getRoot(), excepts),
|
|
new FilteringDirectoryNode(target.getRoot(), excepts)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Checks to see if the two Directories hold the same contents.
|
|
* For this to be true ...
|
|
* <ul>
|
|
* <li>they must have entries with the same names</li>
|
|
* <li>no entries in one but not the other</li>
|
|
* <li>the size+contents of each entry must match</li>
|
|
* <li>the storage classid of the directories must match</li>
|
|
* </ul>
|
|
* To exclude certain parts of the Directory from being checked,
|
|
* use a {@link FilteringDirectoryNode}
|
|
*/
|
|
public static boolean areDirectoriesIdentical(DirectoryEntry dirA, DirectoryEntry dirB) {
|
|
return new DirectoryDelegate(dirA).equals(new DirectoryDelegate(dirB));
|
|
}
|
|
|
|
/**
|
|
* Compares two {@link DocumentEntry} instances of a POI file system.
|
|
* Documents that are not property set streams must be bitwise identical.
|
|
* Property set streams must be logically equal.<p>
|
|
*
|
|
* (Their parent directories are not checked)
|
|
*/
|
|
@SuppressWarnings("WeakerAccess")
|
|
public static boolean areDocumentsIdentical(DocumentEntry docA, DocumentEntry docB)
|
|
throws IOException {
|
|
try {
|
|
return new DocumentDelegate(docA).equals(new DocumentDelegate(docB));
|
|
} catch (RuntimeException e) {
|
|
if (e.getCause() instanceof IOException) {
|
|
throw (IOException)e.getCause();
|
|
} else {
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
|
|
private interface POIDelegate {
|
|
}
|
|
|
|
private static class DirectoryDelegate implements POIDelegate {
|
|
final DirectoryEntry dir;
|
|
|
|
DirectoryDelegate(DirectoryEntry dir) {
|
|
this.dir = dir;
|
|
}
|
|
|
|
private Map<String,POIDelegate> entries() {
|
|
return StreamSupport.stream(dir.spliterator(), false)
|
|
.collect(Collectors.toMap(Entry::getName, DirectoryDelegate::toDelegate));
|
|
}
|
|
|
|
private static POIDelegate toDelegate(Entry entry) {
|
|
return (entry.isDirectoryEntry())
|
|
? new DirectoryDelegate((DirectoryEntry)entry)
|
|
: new DocumentDelegate((DocumentEntry)entry);
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return dir.getName().hashCode();
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object other) {
|
|
if (!(other instanceof DirectoryDelegate)) {
|
|
return false;
|
|
}
|
|
|
|
DirectoryDelegate dd = (DirectoryDelegate)other;
|
|
|
|
if (this == dd) {
|
|
return true;
|
|
}
|
|
|
|
// First, check names
|
|
if (!Objects.equals(dir.getName(),dd.dir.getName())) {
|
|
return false;
|
|
}
|
|
|
|
// Next up, check they have the same number of children
|
|
if (dir.getEntryCount() != dd.dir.getEntryCount()) {
|
|
return false;
|
|
}
|
|
|
|
if (!dir.getStorageClsid().equals(dd.dir.getStorageClsid())) {
|
|
return false;
|
|
}
|
|
|
|
return entries().equals(dd.entries());
|
|
}
|
|
}
|
|
|
|
private static class DocumentDelegate implements POIDelegate {
|
|
final DocumentEntry doc;
|
|
|
|
DocumentDelegate(DocumentEntry doc) {
|
|
this.doc = doc;
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return doc.getName().hashCode();
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object other) {
|
|
if (!(other instanceof DocumentDelegate)) {
|
|
return false;
|
|
}
|
|
|
|
DocumentDelegate dd = (DocumentDelegate)other;
|
|
|
|
if (this == dd) {
|
|
return true;
|
|
}
|
|
|
|
|
|
if (!Objects.equals(doc.getName(), dd.doc.getName())) {
|
|
// Names don't match, not the same
|
|
return false;
|
|
}
|
|
|
|
try (DocumentInputStream inpA = new DocumentInputStream(doc);
|
|
DocumentInputStream inpB = new DocumentInputStream(dd.doc)) {
|
|
|
|
if (PropertySet.isPropertySetStream(inpA) &&
|
|
PropertySet.isPropertySetStream(inpB)) {
|
|
final PropertySet ps1 = PropertySetFactory.create(inpA);
|
|
final PropertySet ps2 = PropertySetFactory.create(inpB);
|
|
return ps1.equals(ps2);
|
|
} else {
|
|
return isEqual(inpA, inpB);
|
|
}
|
|
} catch (MarkUnsupportedException | NoPropertySetStreamException | IOException ex) {
|
|
throw new RuntimeException(ex);
|
|
}
|
|
}
|
|
|
|
private static boolean isEqual(DocumentInputStream i1, DocumentInputStream i2)
|
|
throws IOException {
|
|
final byte[] buf1 = new byte[4*1024];
|
|
final byte[] buf2 = new byte[4*1024];
|
|
try {
|
|
int len;
|
|
while ((len = i1.read(buf1)) > 0) {
|
|
i2.readFully(buf2,0,len);
|
|
for(int i=0;i<len;i++) {
|
|
if (buf1[i] != buf2[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
// is the end of the second file also.
|
|
return i2.read() < 0;
|
|
} catch(EOFException | RuntimeException ioe) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|