1    
2    /* ====================================================================
3     * The Apache Software License, Version 1.1
4     *
5     * Copyright (c) 2002 The Apache Software Foundation.  All rights
6     * reserved.
7     *
8     * Redistribution and use in source and binary forms, with or without
9     * modification, are permitted provided that the following conditions
10    * are met:
11    *
12    * 1. Redistributions of source code must retain the above copyright
13    *    notice, this list of conditions and the following disclaimer.
14    *
15    * 2. Redistributions in binary form must reproduce the above copyright
16    *    notice, this list of conditions and the following disclaimer in
17    *    the documentation and/or other materials provided with the
18    *    distribution.
19    *
20    * 3. The end-user documentation included with the redistribution,
21    *    if any, must include the following acknowledgment:
22    *       "This product includes software developed by the
23    *        Apache Software Foundation (http://www.apache.org/)."
24    *    Alternately, this acknowledgment may appear in the software itself,
25    *    if and wherever such third-party acknowledgments normally appear.
26    *
27    * 4. The names "Apache" and "Apache Software Foundation" and
28    *    "Apache POI" must not be used to endorse or promote products
29    *    derived from this software without prior written permission. For
30    *    written permission, please contact apache@apache.org.
31    *
32    * 5. Products derived from this software may not be called "Apache",
33    *    "Apache POI", nor may "Apache" appear in their name, without
34    *    prior written permission of the Apache Software Foundation.
35    *
36    * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37    * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38    * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39    * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
40    * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41    * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42    * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43    * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44    * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45    * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46    * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47    * SUCH DAMAGE.
48    * ====================================================================
49    *
50    * This software consists of voluntary contributions made by many
51    * individuals on behalf of the Apache Software Foundation.  For more
52    * information on the Apache Software Foundation, please see
53    * <http://www.apache.org/>.
54    */
55   
56   package org.apache.poi.poifs.filesystem;
57   
58   import java.io.*;
59   
60   import java.util.*;
61   
62   import org.apache.poi.poifs.common.POIFSConstants;
63   import org.apache.poi.poifs.dev.POIFSViewable;
64   import org.apache.poi.poifs.property.DocumentProperty;
65   import org.apache.poi.poifs.property.Property;
66   import org.apache.poi.poifs.storage.BlockWritable;
67   import org.apache.poi.poifs.storage.ListManagedBlock;
68   import org.apache.poi.poifs.storage.DocumentBlock;
69   import org.apache.poi.poifs.storage.RawDataBlock;
70   import org.apache.poi.poifs.storage.SmallDocumentBlock;
71   import org.apache.poi.util.HexDump;
72   
73   /**
74    * This class manages a document in the POIFS filesystem.
75    *
76    * @author Marc Johnson (mjohnson at apache dot org)
77    */
78   
79   public class POIFSDocument
80       implements BATManaged, BlockWritable, POIFSViewable
81   {
82       private DocumentProperty _property;
83       private int              _size;
84   
85       // one of these stores will be valid
86       private SmallBlockStore  _small_store;
87       private BigBlockStore    _big_store;
88   
89       /**
90        * Constructor from large blocks
91        *
92        * @param name the name of the POIFSDocument
93        * @param blocks the big blocks making up the POIFSDocument
94        * @param length the actual length of the POIFSDocument
95        *
96        * @exception IOException
97        */
98   
99       public POIFSDocument(final String name, final RawDataBlock [] blocks,
100                           final int length)
101          throws IOException
102      {
103          _size        = length;
104          _big_store   = new BigBlockStore(blocks);
105          _property    = new DocumentProperty(name, _size);
106          _small_store = new SmallBlockStore(new BlockWritable[ 0 ]);
107          _property.setDocument(this);
108      }
109  
110      /**
111       * Constructor from small blocks
112       *
113       * @param name the name of the POIFSDocument
114       * @param blocks the small blocks making up the POIFSDocument
115       * @param length the actual length of the POIFSDocument
116       */
117  
118      public POIFSDocument(final String name,
119                           final SmallDocumentBlock [] blocks, final int length)
120      {
121          _size = length;
122          try
123          {
124              _big_store = new BigBlockStore(new RawDataBlock[ 0 ]);
125          }
126          catch (IOException ignored)
127          {
128  
129              // can't happen with that constructor
130          }
131          _property    = new DocumentProperty(name, _size);
132          _small_store = new SmallBlockStore(blocks);
133          _property.setDocument(this);
134      }
135  
136      /**
137       * Constructor from small blocks
138       *
139       * @param name the name of the POIFSDocument
140       * @param blocks the small blocks making up the POIFSDocument
141       * @param length the actual length of the POIFSDocument
142       *
143       * @exception IOException
144       */
145  
146      public POIFSDocument(final String name, final ListManagedBlock [] blocks,
147                           final int length)
148          throws IOException
149      {
150          _size     = length;
151          _property = new DocumentProperty(name, _size);
152          _property.setDocument(this);
153          if (Property.isSmall(_size))
154          {
155              _big_store   = new BigBlockStore(new RawDataBlock[ 0 ]);
156              _small_store = new SmallBlockStore(blocks);
157          }
158          else
159          {
160              _big_store   = new BigBlockStore(blocks);
161              _small_store = new SmallBlockStore(new BlockWritable[ 0 ]);
162          }
163      }
164  
165      /**
166       * Constructor
167       *
168       * @param name the name of the POIFSDocument
169       * @param stream the InputStream we read data from
170       *
171       * @exception IOException thrown on read errors
172       */
173  
174      public POIFSDocument(final String name, final InputStream stream)
175          throws IOException
176      {
177          List blocks = new ArrayList();
178  
179          _size = 0;
180          while (true)
181          {
182              DocumentBlock block     = new DocumentBlock(stream);
183              int           blockSize = block.size();
184  
185              if (blockSize > 0)
186              {
187                  blocks.add(block);
188                  _size += blockSize;
189              }
190              if (block.partiallyRead())
191              {
192                  break;
193              }
194          }
195          DocumentBlock[] bigBlocks =
196              ( DocumentBlock [] ) blocks.toArray(new DocumentBlock[ 0 ]);
197  
198          _big_store = new BigBlockStore(bigBlocks);
199          _property  = new DocumentProperty(name, _size);
200          _property.setDocument(this);
201          if (_property.shouldUseSmallBlocks())
202          {
203              _small_store =
204                  new SmallBlockStore(SmallDocumentBlock.convert(bigBlocks,
205                      _size));
206              _big_store   = new BigBlockStore(new DocumentBlock[ 0 ]);
207          }
208          else
209          {
210              _small_store = new SmallBlockStore(new BlockWritable[ 0 ]);
211          }
212      }
213  
214      /**
215       * Constructor
216       *
217       * @param name the name of the POIFSDocument
218       * @param size the length of the POIFSDocument
219       * @param path the path of the POIFSDocument
220       * @param writer the writer who will eventually write the document
221       *               contents
222       *
223       * @exception IOException thrown on read errors
224       */
225  
226      public POIFSDocument(final String name, final int size,
227                           final POIFSDocumentPath path,
228                           final POIFSWriterListener writer)
229          throws IOException
230      {
231          _size     = size;
232          _property = new DocumentProperty(name, _size);
233          _property.setDocument(this);
234          if (_property.shouldUseSmallBlocks())
235          {
236              _small_store = new SmallBlockStore(path, name, size, writer);
237              _big_store   = new BigBlockStore(new Object[ 0 ]);
238          }
239          else
240          {
241              _small_store = new SmallBlockStore(new BlockWritable[ 0 ]);
242              _big_store   = new BigBlockStore(path, name, size, writer);
243          }
244      }
245  
246      /**
247       * return the array of SmallDocumentBlocks used
248       *
249       * @return array of SmallDocumentBlocks; may be empty, cannot be null
250       */
251  
252      public BlockWritable [] getSmallBlocks()
253      {
254          return _small_store.getBlocks();
255      }
256  
257      /**
258       * @return size of the document
259       */
260  
261      public int getSize()
262      {
263          return _size;
264      }
265  
266      /**
267       * read data from the internal stores
268       *
269       * @param buffer the buffer to write to
270       * @param offset the offset into our storage to read from
271       */
272  
273      void read(final byte [] buffer, final int offset)
274      {
275          if (_property.shouldUseSmallBlocks())
276          {
277              SmallDocumentBlock.read(_small_store.getBlocks(), buffer, offset);
278          }
279          else
280          {
281              DocumentBlock.read(_big_store.getBlocks(), buffer, offset);
282          }
283      }
284  
285      /**
286       * Get the DocumentProperty
287       *
288       * @return the instance's DocumentProperty
289       */
290  
291      DocumentProperty getDocumentProperty()
292      {
293          return _property;
294      }
295  
296      /* ********** START implementation of BlockWritable ********** */
297  
298      /**
299       * Write the storage to an OutputStream
300       *
301       * @param stream the OutputStream to which the stored data should
302       *               be written
303       *
304       * @exception IOException on problems writing to the specified
305       *            stream
306       */
307  
308      public void writeBlocks(final OutputStream stream)
309          throws IOException
310      {
311          _big_store.writeBlocks(stream);
312      }
313  
314      /* **********  END  implementation of BlockWritable ********** */
315      /* ********** START implementation of BATManaged ********** */
316  
317      /**
318       * Return the number of BigBlock's this instance uses
319       *
320       * @return count of BigBlock instances
321       */
322  
323      public int countBlocks()
324      {
325          return _big_store.countBlocks();
326      }
327  
328      /**
329       * Set the start block for this instance
330       *
331       * @param index index into the array of blocks making up the
332       *        filesystem
333       */
334  
335      public void setStartBlock(final int index)
336      {
337          _property.setStartBlock(index);
338      }
339  
340      /* **********  END  implementation of BATManaged ********** */
341      /* ********** START begin implementation of POIFSViewable ********** */
342  
343      /**
344       * Get an array of objects, some of which may implement
345       * POIFSViewable
346       *
347       * @return an array of Object; may not be null, but may be empty
348       */
349  
350      public Object [] getViewableArray()
351      {
352          Object[] results = new Object[ 1 ];
353          String   result;
354  
355          try
356          {
357              ByteArrayOutputStream output = new ByteArrayOutputStream();
358              BlockWritable[]       blocks = null;
359  
360              if (_big_store.isValid())
361              {
362                  blocks = _big_store.getBlocks();
363              }
364              else if (_small_store.isValid())
365              {
366                  blocks = _small_store.getBlocks();
367              }
368              if (blocks != null)
369              {
370                  for (int k = 0; k < blocks.length; k++)
371                  {
372                      blocks[ k ].writeBlocks(output);
373                  }
374                  byte[] data = output.toByteArray();
375  
376                  if (data.length > _property.getSize())
377                  {
378                      byte[] tmp = new byte[ _property.getSize() ];
379  
380                      System.arraycopy(data, 0, tmp, 0, tmp.length);
381                      data = tmp;
382                  }
383                  output = new ByteArrayOutputStream();
384                  HexDump.dump(data, 0, output, 0);
385                  result = output.toString();
386              }
387              else
388              {
389                  result = "<NO DATA>";
390              }
391          }
392          catch (IOException e)
393          {
394              result = e.getMessage();
395          }
396          results[ 0 ] = result;
397          return results;
398      }
399  
400      /**
401       * Get an Iterator of objects, some of which may implement
402       * POIFSViewable
403       *
404       * @return an Iterator; may not be null, but may have an empty
405       * back end store
406       */
407  
408      public Iterator getViewableIterator()
409      {
410          return Collections.EMPTY_LIST.iterator();
411      }
412  
413      /**
414       * Give viewers a hint as to whether to call getViewableArray or
415       * getViewableIterator
416       *
417       * @return true if a viewer should call getViewableArray, false if
418       *         a viewer should call getViewableIterator
419       */
420  
421      public boolean preferArray()
422      {
423          return true;
424      }
425  
426      /**
427       * Provides a short description of the object, to be used when a
428       * POIFSViewable object has not provided its contents.
429       *
430       * @return short description
431       */
432  
433      public String getShortDescription()
434      {
435          StringBuffer buffer = new StringBuffer();
436  
437          buffer.append("Document: \"").append(_property.getName())
438              .append("\"");
439          buffer.append(" size = ").append(getSize());
440          return buffer.toString();
441      }
442  
443      /* **********  END  begin implementation of POIFSViewable ********** */
444      private class SmallBlockStore
445      {
446          private SmallDocumentBlock[] smallBlocks;
447          private POIFSDocumentPath    path;
448          private String               name;
449          private int                  size;
450          private POIFSWriterListener  writer;
451  
452          /**
453           * Constructor
454           *
455           * @param blocks blocks to construct the store from
456           */
457  
458          SmallBlockStore(final Object [] blocks)
459          {
460              smallBlocks = new SmallDocumentBlock[ blocks.length ];
461              for (int j = 0; j < blocks.length; j++)
462              {
463                  smallBlocks[ j ] = ( SmallDocumentBlock ) blocks[ j ];
464              }
465              this.path   = null;
466              this.name   = null;
467              this.size   = -1;
468              this.writer = null;
469          }
470  
471          /**
472           * Constructor for a small block store that will be written
473           * later
474           *
475           * @param path path of the document
476           * @param name name of the document
477           * @param size length of the document
478           * @param writer the object that will eventually write the document
479           */
480  
481          SmallBlockStore(final POIFSDocumentPath path, final String name,
482                          final int size, final POIFSWriterListener writer)
483          {
484              smallBlocks = new SmallDocumentBlock[ 0 ];
485              this.path   = path;
486              this.name   = name;
487              this.size   = size;
488              this.writer = writer;
489          }
490  
491          /**
492           * @return true if this store is a valid source of data
493           */
494  
495          boolean isValid()
496          {
497              return ((smallBlocks.length > 0) || (writer != null));
498          }
499  
500          /**
501           * @return the SmallDocumentBlocks
502           */
503  
504          BlockWritable [] getBlocks()
505          {
506              if (isValid() && (writer != null))
507              {
508                  ByteArrayOutputStream stream  =
509                      new ByteArrayOutputStream(size);
510                  DocumentOutputStream  dstream =
511                      new DocumentOutputStream(stream, size);
512  
513                  writer.processPOIFSWriterEvent(new POIFSWriterEvent(dstream,
514                          path, name, size));
515                  smallBlocks = SmallDocumentBlock.convert(stream.toByteArray(),
516                                                           size);
517              }
518              return smallBlocks;
519          }
520      }   // end private class SmallBlockStore
521  
522      private class BigBlockStore
523      {
524          private DocumentBlock[]     bigBlocks;
525          private POIFSDocumentPath   path;
526          private String              name;
527          private int                 size;
528          private POIFSWriterListener writer;
529  
530          /**
531           * Constructor
532           *
533           * @param blocks the blocks making up the store
534           *
535           * @exception IOException on I/O error
536           */
537  
538          BigBlockStore(final Object [] blocks)
539              throws IOException
540          {
541              bigBlocks = new DocumentBlock[ blocks.length ];
542              for (int j = 0; j < blocks.length; j++)
543              {
544                  if (blocks[ j ] instanceof DocumentBlock)
545                  {
546                      bigBlocks[ j ] = ( DocumentBlock ) blocks[ j ];
547                  }
548                  else
549                  {
550                      bigBlocks[ j ] =
551                          new DocumentBlock(( RawDataBlock ) blocks[ j ]);
552                  }
553              }
554              this.path   = null;
555              this.name   = null;
556              this.size   = -1;
557              this.writer = null;
558          }
559  
560          /**
561           * Constructor for a big block store that will be written
562           * later
563           *
564           * @param path path of the document
565           * @param name name of the document
566           * @param size length of the document
567           * @param writer the object that will eventually write the
568           *               document
569           */
570  
571          BigBlockStore(final POIFSDocumentPath path, final String name,
572                        final int size, final POIFSWriterListener writer)
573          {
574              bigBlocks   = new DocumentBlock[ 0 ];
575              this.path   = path;
576              this.name   = name;
577              this.size   = size;
578              this.writer = writer;
579          }
580  
581          /**
582           * @return true if this store is a valid source of data
583           */
584  
585          boolean isValid()
586          {
587              return ((bigBlocks.length > 0) || (writer != null));
588          }
589  
590          /**
591           * @return the DocumentBlocks
592           */
593  
594          DocumentBlock [] getBlocks()
595          {
596              if (isValid() && (writer != null))
597              {
598                  ByteArrayOutputStream stream  =
599                      new ByteArrayOutputStream(size);
600                  DocumentOutputStream  dstream =
601                      new DocumentOutputStream(stream, size);
602  
603                  writer.processPOIFSWriterEvent(new POIFSWriterEvent(dstream,
604                          path, name, size));
605                  bigBlocks = DocumentBlock.convert(stream.toByteArray(), size);
606              }
607              return bigBlocks;
608          }
609  
610          /**
611           * write the blocks to a stream
612           *
613           * @param stream the stream to which the data is to be written
614           *
615           * @exception IOException on error
616           */
617  
618          void writeBlocks(OutputStream stream)
619              throws IOException
620          {
621              if (isValid())
622              {
623                  if (writer != null)
624                  {
625                      DocumentOutputStream dstream =
626                          new DocumentOutputStream(stream, size);
627  
628                      writer.processPOIFSWriterEvent(
629                          new POIFSWriterEvent(dstream, path, name, size));
630                      dstream.writeFiller(countBlocks()
631                                          * POIFSConstants
632                                              .BIG_BLOCK_SIZE, DocumentBlock
633                                              .getFillByte());
634                  }
635                  else
636                  {
637                      for (int k = 0; k < bigBlocks.length; k++)
638                      {
639                          bigBlocks[ k ].writeBlocks(stream);
640                      }
641                  }
642              }
643          }
644  
645          /**
646           * @return number of big blocks making up this document
647           */
648  
649          int countBlocks()
650          {
651              int rval = 0;
652  
653              if (isValid())
654              {
655                  if (writer != null)
656                  {
657                      rval = (size + POIFSConstants.BIG_BLOCK_SIZE - 1)
658                             / POIFSConstants.BIG_BLOCK_SIZE;
659                  }
660                  else
661                  {
662                      rval = bigBlocks.length;
663                  }
664              }
665              return rval;
666          }
667      }   // end private class BigBlockStore
668  }       // end class POIFSDocument
669  
670  ￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿BIG_BLOCK_SIZE￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿getFillByte￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿DocumentBlock￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿k￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿bigBlocks￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿k￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿bigBlocks￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿k￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿writeBlocks￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿stream￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿countBlocks￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿isValid￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿writer￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿rval￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿size￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿POIFSConstants￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿BIG_BLOCK_SIZE￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿POIFSConstants￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿BIG_BLOCK_SIZE￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿rval￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿bigBlocks￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿rval￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿￿