You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

CopyCompare.java 8.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. /* ====================================================================
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements. See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. package org.apache.poi.examples.hpsf;
  16. import java.io.File;
  17. import java.io.FileOutputStream;
  18. import java.io.IOException;
  19. import java.io.InputStream;
  20. import java.io.OutputStream;
  21. import java.io.PrintStream;
  22. import java.io.UnsupportedEncodingException;
  23. import org.apache.poi.hpsf.DocumentSummaryInformation;
  24. import org.apache.poi.hpsf.HPSFException;
  25. import org.apache.poi.hpsf.HPSFRuntimeException;
  26. import org.apache.poi.hpsf.PropertySet;
  27. import org.apache.poi.hpsf.PropertySetFactory;
  28. import org.apache.poi.hpsf.SummaryInformation;
  29. import org.apache.poi.hpsf.WritingNotSupportedException;
  30. import org.apache.poi.poifs.eventfilesystem.POIFSReader;
  31. import org.apache.poi.poifs.eventfilesystem.POIFSReaderEvent;
  32. import org.apache.poi.poifs.filesystem.DirectoryEntry;
  33. import org.apache.poi.poifs.filesystem.DocumentInputStream;
  34. import org.apache.poi.poifs.filesystem.EntryUtils;
  35. import org.apache.poi.poifs.filesystem.POIFSDocumentPath;
  36. import org.apache.poi.poifs.filesystem.POIFSFileSystem;
  37. import org.apache.poi.util.TempFile;
  38. /**
  39. * <p>This class copies a POI file system to a new file and compares the copy
  40. * with the original.</p>
  41. * <p>
  42. * <p>Property set streams are copied logically, i.e. the application
  43. * establishes a {@link org.apache.poi.hpsf.PropertySet} of an original property
  44. * set, creates a {@link org.apache.poi.hpsf.PropertySet} and writes the
  45. * {@link org.apache.poi.hpsf.PropertySet} to the destination POI file
  46. * system. - Streams which are no property set streams are copied bit by
  47. * bit.</p>
  48. * <p>
  49. * <p>The comparison of the POI file systems is done logically. That means that
  50. * the two disk files containing the POI file systems do not need to be
  51. * exactly identical. However, both POI file systems must contain the same
  52. * files, and most of these files must be bitwise identical. Property set
  53. * streams, however, are compared logically: they must have the same sections
  54. * with the same attributes, and the sections must contain the same properties.
  55. * Details like the ordering of the properties do not matter.</p>
  56. */
  57. @SuppressWarnings({"java:S106","java:S4823"})
  58. public final class CopyCompare {
  59. private CopyCompare() {}
  60. private static final ThreadLocal<PrintStream> out = ThreadLocal.withInitial(() -> System.out);
  61. /**
  62. * Runs the example program. The application expects one or two arguments:
  63. *
  64. * <ol>
  65. * <li>The first argument is the disk file name of the POI filesystem to copy.</li>
  66. * <li>The second argument is optional. If it is given, it is the name of
  67. * a disk file the copy of the POI filesystem will be written to. If it is
  68. * not given, the copy will be written to a temporary file which will be
  69. * deleted at the end of the program.</li>
  70. * </ol>
  71. *
  72. * @param args Command-line arguments.
  73. * @throws IOException if any I/O exception occurs.
  74. * @throws UnsupportedEncodingException if a character encoding is not
  75. * supported.
  76. */
  77. public static void main(final String[] args) throws IOException {
  78. String originalFileName = null;
  79. String copyFileName = null;
  80. // Check the command-line arguments.
  81. if (args.length == 1) {
  82. originalFileName = args[0];
  83. File f = TempFile.createTempFile("CopyOfPOIFileSystem-", ".ole2");
  84. f.deleteOnExit();
  85. copyFileName = f.getAbsolutePath();
  86. } else if (args.length == 2) {
  87. originalFileName = args[0];
  88. copyFileName = args[1];
  89. } else {
  90. System.err.println("Usage: CopyCompare originPOIFS [copyPOIFS]");
  91. System.exit(1);
  92. }
  93. // Read the origin POIFS using the eventing API.
  94. final POIFSReader r = new POIFSReader();
  95. try (final POIFSFileSystem poiFs = new POIFSFileSystem();
  96. OutputStream fos = new FileOutputStream(copyFileName)) {
  97. r.registerListener(e -> handleEvent(poiFs, e));
  98. r.setNotifyEmptyDirectories(true);
  99. r.read(new File(originalFileName));
  100. // Write the new POIFS to disk.
  101. poiFs.writeFilesystem(fos);
  102. }
  103. // Read all documents from the original POI file system and compare them with
  104. // the equivalent document from the copy.
  105. try (POIFSFileSystem opfs = new POIFSFileSystem(new File(originalFileName));
  106. POIFSFileSystem cpfs = new POIFSFileSystem(new File(copyFileName))) {
  107. final DirectoryEntry oRoot = opfs.getRoot();
  108. final DirectoryEntry cRoot = cpfs.getRoot();
  109. out.get().println(EntryUtils.areDirectoriesIdentical(oRoot, cRoot) ? "Equal" : "Not equal");
  110. }
  111. }
  112. public static void setOut(PrintStream ps) {
  113. out.set(ps);
  114. }
  115. private interface InputStreamSupplier {
  116. InputStream get() throws IOException, WritingNotSupportedException;
  117. }
  118. /**
  119. * The method is called by POI's eventing API for each file in the origin POIFS.
  120. */
  121. public static void handleEvent(final POIFSFileSystem poiFs, final POIFSReaderEvent event) {
  122. // The following declarations are shortcuts for accessing the "event" object.
  123. final DocumentInputStream stream = event.getStream();
  124. try {
  125. // Find out whether the current document is a property set stream or not.
  126. InputStreamSupplier su;
  127. if (stream != null && PropertySet.isPropertySetStream(stream)) {
  128. // Yes, the current document is a property set stream. Let's create
  129. // a PropertySet instance from it.
  130. PropertySet ps = PropertySetFactory.create(stream);
  131. // Copy the property set to the destination POI file system.
  132. final PropertySet mps;
  133. if (ps instanceof DocumentSummaryInformation) {
  134. mps = new DocumentSummaryInformation(ps);
  135. } else if (ps instanceof SummaryInformation) {
  136. mps = new SummaryInformation(ps);
  137. } else {
  138. mps = new PropertySet(ps);
  139. }
  140. su = mps::toInputStream;
  141. } else {
  142. // No, the current document is not a property set stream.
  143. // We copy it unmodified to the destination POIFS.
  144. su = event::getStream;
  145. }
  146. try (InputStream is = su.get()) {
  147. final POIFSDocumentPath path = event.getPath();
  148. // Ensures that the directory hierarchy for a document in a POI fileystem is in place.
  149. // Get the root directory. It does not have to be created since it always exists in a POIFS.
  150. DirectoryEntry de = poiFs.getRoot();
  151. if (File.separator.equals(path.toString())) {
  152. de.setStorageClsid(event.getStorageClassId());
  153. }
  154. for (int i=0; i<path.length(); i++) {
  155. String subDir = path.getComponent(i);
  156. if (de.hasEntry(subDir)) {
  157. de = (DirectoryEntry)de.getEntry(subDir);
  158. } else {
  159. de = de.createDirectory(subDir);
  160. if (i == path.length()-1) {
  161. de.setStorageClsid(event.getStorageClassId());
  162. }
  163. }
  164. }
  165. if (event.getName() != null) {
  166. de.createDocument(event.getName(), is);
  167. }
  168. }
  169. } catch (HPSFException | IOException ex) {
  170. // According to the definition of the processPOIFSReaderEvent method we cannot pass checked
  171. // exceptions to the caller.
  172. throw new HPSFRuntimeException("Could not read file " + event.getPath() + "/" + event.getName(), ex);
  173. }
  174. }
  175. }