KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > poi > hpsf > examples > CopyCompare


1 /* ====================================================================
2    Copyright 2002-2004 Apache Software Foundation
3
4    Licensed under the Apache License, Version 2.0 (the "License");
5    you may not use this file except in compliance with the License.
6    You may obtain a copy of the License at
7
8        http://www.apache.org/licenses/LICENSE-2.0
9
10    Unless required by applicable law or agreed to in writing, software
11    distributed under the License is distributed on an "AS IS" BASIS,
12    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13    See the License for the specific language governing permissions and
14    limitations under the License.
15 ==================================================================== */

16         
17 package org.apache.poi.hpsf.examples;
18
19 import java.io.ByteArrayInputStream JavaDoc;
20 import java.io.ByteArrayOutputStream JavaDoc;
21 import java.io.File JavaDoc;
22 import java.io.FileInputStream JavaDoc;
23 import java.io.FileNotFoundException JavaDoc;
24 import java.io.FileOutputStream JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.InputStream JavaDoc;
27 import java.io.OutputStream JavaDoc;
28 import java.io.UnsupportedEncodingException JavaDoc;
29 import java.util.HashMap JavaDoc;
30 import java.util.Iterator JavaDoc;
31 import java.util.Map JavaDoc;
32
33 import org.apache.poi.hpsf.HPSFRuntimeException;
34 import org.apache.poi.hpsf.MarkUnsupportedException;
35 import org.apache.poi.hpsf.MutablePropertySet;
36 import org.apache.poi.hpsf.NoPropertySetStreamException;
37 import org.apache.poi.hpsf.PropertySet;
38 import org.apache.poi.hpsf.PropertySetFactory;
39 import org.apache.poi.hpsf.Util;
40 import org.apache.poi.hpsf.WritingNotSupportedException;
41 import org.apache.poi.poifs.eventfilesystem.POIFSReader;
42 import org.apache.poi.poifs.eventfilesystem.POIFSReaderEvent;
43 import org.apache.poi.poifs.eventfilesystem.POIFSReaderListener;
44 import org.apache.poi.poifs.filesystem.DirectoryEntry;
45 import org.apache.poi.poifs.filesystem.DocumentEntry;
46 import org.apache.poi.poifs.filesystem.DocumentInputStream;
47 import org.apache.poi.poifs.filesystem.Entry;
48 import org.apache.poi.poifs.filesystem.POIFSDocumentPath;
49 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
50 import org.apache.poi.util.TempFile;
51
52 /**
53  * <p>This class copies a POI file system to a new file and compares the copy
54  * with the original.</p>
55  *
56  * <p>Property set streams are copied logically, i.e. the application
57  * establishes a {@link org.apache.poi.hpsf.PropertySet} of an original property
58  * set, creates a {@link org.apache.poi.hpsf.MutablePropertySet} from the
59  * {@link org.apache.poi.hpsf.PropertySet} and writes the
60  * {@link org.apache.poi.hpsf.MutablePropertySet} to the destination POI file
61  * system. - Streams which are no property set streams are copied bit by
62  * bit.</p>
63  *
64  * <p>The comparison of the POI file systems is done logically. That means that
65  * the two disk files containing the POI file systems do not need to be
66  * exactly identical. However, both POI file systems must contain the same
67  * files, and most of these files must be bitwise identical. Property set
68  * streams, however, are compared logically: they must have the same sections
69  * with the same attributs, and the sections must contain the same properties.
70  * Details like the ordering of the properties do not matter.</p>
71  *
72  * @author Rainer Klute <a
73  * HREF="mailto:klute@rainer-klute.de">&lt;klute@rainer-klute.de&gt;</a>
74  * @version $Id: CopyCompare.java,v 1.6 2004/10/12 05:49:00 glens Exp $
75  * @since 2003-09-19
76  */

77 public class CopyCompare
78 {
79     /**
80      * <p>Runs the example program. The application expects one or two
81      * arguments:</p>
82      *
83      * <ol>
84      *
85      * <li><p>The first argument is the disk file name of the POI filesystem to
86      * copy.</p></li>
87      *
88      * <li><p>The second argument is optional. If it is given, it is the name of
89      * a disk file the copy of the POI filesystem will be written to. If it is
90      * not given, the copy will be written to a temporary file which will be
91      * deleted at the end of the program.</p></li>
92      *
93      * </ol>
94      *
95      * @param args Command-line arguments.
96      * @exception MarkUnsupportedException if a POI document stream does not
97      * support the mark() operation.
98      * @exception NoPropertySetStreamException if the application tries to
99      * create a property set from a POI document stream that is not a property
100      * set stream.
101      * @exception IOException if any I/O exception occurs.
102      * @exception UnsupportedEncodingException if a character encoding is not
103      * supported.
104      */

105     public static void main(final String JavaDoc[] args)
106     throws NoPropertySetStreamException, MarkUnsupportedException,
107            UnsupportedEncodingException JavaDoc, IOException JavaDoc
108     {
109         String JavaDoc originalFileName = null;
110         String JavaDoc copyFileName = null;
111
112         /* Check the command-line arguments. */
113         if (args.length == 1)
114         {
115             originalFileName = args[0];
116             File JavaDoc f = TempFile.createTempFile("CopyOfPOIFileSystem-", ".ole2");
117             f.deleteOnExit();
118             copyFileName = f.getAbsolutePath();
119         }
120         else if (args.length == 2)
121         {
122             originalFileName = args[0];
123             copyFileName = args[1];
124         }
125         else
126         {
127             System.err.println("Usage: " + CopyCompare.class.getName() +
128                                "originPOIFS [copyPOIFS]");
129             System.exit(1);
130         }
131
132         /* Read the origin POIFS using the eventing API. The real work is done
133          * in the class CopyFile which is registered here as a POIFSReader. */

134         final POIFSReader r = new POIFSReader();
135         final CopyFile cf = new CopyFile(copyFileName);
136         r.registerListener(cf);
137         r.read(new FileInputStream JavaDoc(originalFileName));
138         
139         /* Write the new POIFS to disk. */
140         cf.close();
141
142         /* Read all documents from the original POI file system and compare them
143          * with the equivalent document from the copy. */

144         final POIFSFileSystem opfs =
145             new POIFSFileSystem(new FileInputStream JavaDoc(originalFileName));
146         final POIFSFileSystem cpfs =
147             new POIFSFileSystem(new FileInputStream JavaDoc(copyFileName));
148
149         final DirectoryEntry oRoot = opfs.getRoot();
150         final DirectoryEntry cRoot = cpfs.getRoot();
151         final StringBuffer JavaDoc messages = new StringBuffer JavaDoc();
152         if (equal(oRoot, cRoot, messages))
153             System.out.println("Equal");
154         else
155             System.out.println("Not equal: " + messages.toString());
156     }
157
158
159
160     /**
161      * <p>Compares two {@link DirectoryEntry} instances of a POI file system.
162      * The directories must contain the same streams with the same names and
163      * contents.</p>
164      *
165      * @param d1 The first directory.
166      * @param d2 The second directory.
167      * @param msg The method may append human-readable comparison messages to
168      * this string buffer.
169      * @return <code>true</code> if the directories are equal, else
170      * <code>false</code>.
171      * @exception MarkUnsupportedException if a POI document stream does not
172      * support the mark() operation.
173      * @exception NoPropertySetStreamException if the application tries to
174      * create a property set from a POI document stream that is not a property
175      * set stream.
176      * @exception IOException if any I/O exception occurs.
177      */

178     private static boolean equal(final DirectoryEntry d1,
179                                  final DirectoryEntry d2,
180                                  final StringBuffer JavaDoc msg)
181     throws NoPropertySetStreamException, MarkUnsupportedException,
182            UnsupportedEncodingException JavaDoc, IOException JavaDoc
183     {
184         boolean equal = true;
185         /* Iterate over d1 and compare each entry with its counterpart in d2. */
186         for (final Iterator JavaDoc i = d1.getEntries(); equal && i.hasNext();)
187         {
188             final Entry e1 = (Entry) i.next();
189             final String JavaDoc n1 = e1.getName();
190             Entry e2 = null;
191             try
192             {
193                 e2 = d2.getEntry(n1);
194             }
195             catch (FileNotFoundException JavaDoc ex)
196             {
197                 msg.append("Document \"" + e1 + "\" exists, document \"" +
198                            e2 + "\" does not.\n");
199                 equal = false;
200                 break;
201             }
202
203             if (e1.isDirectoryEntry() && e2.isDirectoryEntry())
204                 equal = equal((DirectoryEntry) e1, (DirectoryEntry) e2, msg);
205             else if (e1.isDocumentEntry() && e2.isDocumentEntry())
206                 equal = equal((DocumentEntry) e1, (DocumentEntry) e2, msg);
207             else
208             {
209                 msg.append("One of \"" + e1 + "\" and \"" + e2 + "\" is a " +
210                            "document while the other one is a directory.\n");
211                 equal = false;
212             }
213         }
214
215         /* Iterate over d2 just to make sure that there are no entries in d2
216          * that are not in d1. */

217         for (final Iterator JavaDoc i = d2.getEntries(); equal && i.hasNext();)
218         {
219             final Entry e2 = (Entry) i.next();
220             final String JavaDoc n2 = e2.getName();
221             Entry e1 = null;
222             try
223             {
224                 e1 = d1.getEntry(n2);
225             }
226             catch (FileNotFoundException JavaDoc ex)
227             {
228                 msg.append("Document \"" + e2 + "\" exitsts, document \"" +
229                            e1 + "\" does not.\n");
230                 equal = false;
231                 break;
232             }
233         }
234         return equal;
235     }
236
237
238
239     /**
240      * <p>Compares two {@link DocumentEntry} instances of a POI file system.
241      * Documents that are not property set streams must be bitwise identical.
242      * Property set streams must be logically equal.</p>
243      *
244      * @param d1 The first document.
245      * @param d2 The second document.
246      * @param msg The method may append human-readable comparison messages to
247      * this string buffer.
248      * @return <code>true</code> if the documents are equal, else
249      * <code>false</code>.
250      * @exception MarkUnsupportedException if a POI document stream does not
251      * support the mark() operation.
252      * @exception NoPropertySetStreamException if the application tries to
253      * create a property set from a POI document stream that is not a property
254      * set stream.
255      * @exception IOException if any I/O exception occurs.
256      */

257     private static boolean equal(final DocumentEntry d1, final DocumentEntry d2,
258                                  final StringBuffer JavaDoc msg)
259     throws NoPropertySetStreamException, MarkUnsupportedException,
260            UnsupportedEncodingException JavaDoc, IOException JavaDoc
261     {
262         boolean equal = true;
263         final DocumentInputStream dis1 = new DocumentInputStream(d1);
264         final DocumentInputStream dis2 = new DocumentInputStream(d2);
265         if (PropertySet.isPropertySetStream(dis1) &&
266             PropertySet.isPropertySetStream(dis2))
267         {
268             final PropertySet ps1 = PropertySetFactory.create(dis1);
269             final PropertySet ps2 = PropertySetFactory.create(dis2);
270             equal = ps1.equals(ps2);
271             if (!equal)
272             {
273                 msg.append("Property sets are not equal.\n");
274                 return equal;
275             }
276         }
277         else
278         {
279             int i1;
280             int i2;
281             do
282             {
283                 i1 = dis1.read();
284                 i2 = dis2.read();
285                 if (i1 != i2)
286                 {
287                     equal = false;
288                     msg.append("Documents are not equal.\n");
289                     break;
290                 }
291             }
292             while (equal && i1 == -1);
293         }
294         return true;
295     }
296
297
298
299     /**
300      * <p>This class does all the work. Its method {@link
301      * #processPOIFSReaderEvent(POIFSReaderEvent)} is called for each file in
302      * the original POI file system. Except for property set streams it copies
303      * everything unmodified to the destination POI filesystem. Property set
304      * streams are copied by creating a new {@link PropertySet} from the
305      * original property set by using the {@link
306      * MutablePropertySet#MutablePropertySet(PropertySet) constructor.</p>
307      */

308     static class CopyFile implements POIFSReaderListener
309     {
310         String JavaDoc dstName;
311         OutputStream JavaDoc out;
312         POIFSFileSystem poiFs;
313
314
315         /**
316          * <p>The constructor of a {@link CopyFile} instance creates the target
317          * POIFS. It also stores the name of the file the POIFS will be written
318          * to once it is complete.</p>
319          *
320          * @param dstName The name of the disk file the destination POIFS is to
321          * be written to.
322          * @throws FileNotFoundException
323          */

324         public CopyFile(final String JavaDoc dstName)
325         {
326             this.dstName = dstName;
327             poiFs = new POIFSFileSystem();
328         }
329
330
331         /**
332          * <p>The method is called by POI's eventing API for each file in the
333          * origin POIFS.</p>
334          */

335         public void processPOIFSReaderEvent(final POIFSReaderEvent event)
336         {
337             /* The following declarations are shortcuts for accessing the
338              * "event" object. */

339             final POIFSDocumentPath path = event.getPath();
340             final String JavaDoc name = event.getName();
341             final DocumentInputStream stream = event.getStream();
342
343             Throwable JavaDoc t = null;
344
345             try
346             {
347                 /* Find out whether the current document is a property set
348                  * stream or not. */

349                 if (PropertySet.isPropertySetStream(stream))
350                 {
351                     /* Yes, the current document is a property set stream.
352                      * Let's create a PropertySet instance from it. */

353                     PropertySet ps = null;
354                     try
355                     {
356                         ps = PropertySetFactory.create(stream);
357                     }
358                     catch (NoPropertySetStreamException ex)
359                     {
360                         /* This exception will not be thrown because we already
361                          * checked above. */

362                     }
363
364                     /* Copy the property set to the destination POI file
365                      * system. */

366                     copy(poiFs, path, name, ps);
367                 }
368                 else
369                     /* No, the current document is not a property set stream. We
370                      * copy it unmodified to the destination POIFS. */

371                     copy(poiFs, event.getPath(), event.getName(), stream);
372             }
373             catch (MarkUnsupportedException ex)
374             {
375                 t = ex;
376             }
377             catch (IOException JavaDoc ex)
378             {
379                 t = ex;
380             }
381             catch (WritingNotSupportedException ex)
382             {
383                 t = ex;
384             }
385
386             /* According to the definition of the processPOIFSReaderEvent method
387              * we cannot pass checked exceptions to the caller. The following
388              * lines check whether a checked exception occured and throws an
389              * unchecked exception. The message of that exception is that of
390              * the underlying checked exception. */

391             if (t != null)
392             {
393                 throw new HPSFRuntimeException
394                     ("Could not read file \"" + path + "/" + name +
395                      "\". Reason: " + Util.toString(t));
396             }
397         }
398
399
400
401         /**
402          * <p>Writes a {@link PropertySet} to a POI filesystem.</p>
403          *
404          * @param poiFs The POI filesystem to write to.
405          * @param path The file's path in the POI filesystem.
406          * @param name The file's name in the POI filesystem.
407          * @param ps The property set to write.
408          */

409         public void copy(final POIFSFileSystem poiFs,
410                          final POIFSDocumentPath path,
411                          final String JavaDoc name,
412                          final PropertySet ps)
413             throws WritingNotSupportedException, IOException JavaDoc
414         {
415             final DirectoryEntry de = getPath(poiFs, path);
416             final MutablePropertySet mps = new MutablePropertySet(ps);
417             de.createDocument(name, mps.toInputStream());
418         }
419
420
421
422         /**
423          * <p>Copies the bytes from a {@link DocumentInputStream} to a new
424          * stream in a POI filesystem.</p>
425          *
426          * @param poiFs The POI filesystem to write to.
427          * @param path The source document's path.
428          * @param stream The stream containing the source document.
429          */

430         public void copy(final POIFSFileSystem poiFs,
431                          final POIFSDocumentPath path,
432                          final String JavaDoc name,
433                          final DocumentInputStream stream) throws IOException JavaDoc
434         {
435             final DirectoryEntry de = getPath(poiFs, path);
436             final ByteArrayOutputStream JavaDoc out = new ByteArrayOutputStream JavaDoc();
437             int c;
438             while ((c = stream.read()) != -1)
439                 out.write(c);
440             stream.close();
441             out.close();
442             final InputStream JavaDoc in =
443                 new ByteArrayInputStream JavaDoc(out.toByteArray());
444             de.createDocument(name, in);
445         }
446
447
448         /**
449          * <p>Writes the POI file system to a disk file.</p>
450          *
451          * @throws FileNotFoundException
452          * @throws IOException
453          */

454         public void close() throws FileNotFoundException JavaDoc, IOException JavaDoc
455         {
456             out = new FileOutputStream JavaDoc(dstName);
457             poiFs.writeFilesystem(out);
458             out.close();
459         }
460
461
462
463         /** Contains the directory paths that have already been created in the
464          * output POI filesystem and maps them to their corresponding
465          * {@link org.apache.poi.poifs.filesystem.DirectoryNode}s. */

466         private final Map JavaDoc paths = new HashMap JavaDoc();
467
468
469
470         /**
471          * <p>Ensures that the directory hierarchy for a document in a POI
472          * fileystem is in place. When a document is to be created somewhere in
473          * a POI filesystem its directory must be created first. This method
474          * creates all directories between the POI filesystem root and the
475          * directory the document should belong to which do not yet exist.</p>
476          *
477          * <p>Unfortunately POI does not offer a simple method to interrogate
478          * the POIFS whether a certain child node (file or directory) exists in
479          * a directory. However, since we always start with an empty POIFS which
480          * contains the root directory only and since each directory in the
481          * POIFS is created by this method we can maintain the POIFS's directory
482          * hierarchy ourselves: The {@link DirectoryEntry} of each directory
483          * created is stored in a {@link Map}. The directories' path names map
484          * to the corresponding {@link DirectoryEntry} instances.</p>
485          *
486          * @param poiFs The POI filesystem the directory hierarchy is created
487          * in, if needed.
488          * @param path The document's path. This method creates those directory
489          * components of this hierarchy which do not yet exist.
490          * @return The directory entry of the document path's parent. The caller
491          * should use this {@link DirectoryEntry} to create documents in it.
492          */

493         public DirectoryEntry getPath(final POIFSFileSystem poiFs,
494                                       final POIFSDocumentPath path)
495         {
496             try
497             {
498                 /* Check whether this directory has already been created. */
499                 final String JavaDoc s = path.toString();
500                 DirectoryEntry de = (DirectoryEntry) paths.get(s);
501                 if (de != null)
502                     /* Yes: return the corresponding DirectoryEntry. */
503                     return de;
504
505                 /* No: We have to create the directory - or return the root's
506                  * DirectoryEntry. */

507                 int l = path.length();
508                 if (l == 0)
509                     /* Get the root directory. It does not have to be created
510                      * since it always exists in a POIFS. */

511                     de = poiFs.getRoot();
512                 else
513                 {
514                     /* Create a subordinate directory. The first step is to
515                      * ensure that the parent directory exists: */

516                     de = getPath(poiFs, path.getParent());
517                     /* Now create the target directory: */
518                     de = de.createDirectory(path.getComponent
519                                             (path.length() - 1));
520                 }
521                 paths.put(s, de);
522                 return de;
523             }
524             catch (IOException JavaDoc ex)
525             {
526                 /* This exception will be thrown if the directory already
527                  * exists. However, since we have full control about directory
528                  * creation we can ensure that this will never happen. */

529                 ex.printStackTrace(System.err);
530                 throw new RuntimeException JavaDoc(ex.toString());
531                 /* FIXME (2): Replace the previous line by the following once we
532                  * no longer need JDK 1.3 compatibility. */

533                 // throw new RuntimeException(ex);
534
}
535         }
536     }
537
538 }
539
Popular Tags