View Javadoc

1   ////////////////////////////////////////////////////////////////////////////////
2   // MillScript-Excel: an Open Spice interpreter and batch website creation tool
3   // Copyright (C) 2006 Open World Ltd, Kevin Rogers
4   //
5   // This file is part of MillScript-Excel.
6   //
7   // MillScript-Excel is free software; you can redistribute it and/or modify it under
8   // the terms of the GNU General Public License as published by the Free
9   // Software Foundation; either version 2 of the License, or (at your option)
10  // any later version.
11  //
12  // MillScript-Excel is distributed in the hope that it will be useful, but WITHOUT
13  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15  // more details.
16  //
17  // You should have received a copy of the GNU General Public License along with
18  // MillScript-Excel; if not, write to the Free Software Foundation, Inc., 59 Temple
19  // Place, Suite 330, Boston, MA  02111-1307  USA
20  ////////////////////////////////////////////////////////////////////////////////
21  package org.millscript.office.compound.vfs;
22  
23  import org.millscript.commons.alert.alerts.IOAlert;
24  import org.millscript.commons.vfs.AbstractVFile;
25  import org.millscript.commons.vfs.VFile;
26  import org.millscript.commons.vfs.VFolder;
27  import org.millscript.commons.vfs.alerts.VCannotOpenEntryAlert;
28  import org.millscript.commons.vfs.alerts.VEntryNotFoundAlert;
29  import org.millscript.commons.vfs.mime.AbstractMIMEVVolume;
30  import org.millscript.office.compound.DirectoryEntry;
31  import org.millscript.office.compound.Header;
32  import org.millscript.office.endianness.EndianDecoder;
33  
34  import java.io.FileNotFoundException;
35  import java.io.IOException;
36  import java.io.RandomAccessFile;
37  
38  /**
39   * 
40   */
41  public class CompoundDocumentVolume extends AbstractMIMEVVolume {
42  
43      final RandomAccessFile compoundDocument;
44  
45      final DirectoryEntry[] directory;
46  
47      final EndianDecoder endianDecoder;
48  
49      final Header header;
50  
51      /**
52       * @param backingFile
53       */
54      public CompoundDocumentVolume( final AbstractVFile< ? > sourceFile ) {
55          super( sourceFile );
56          try {
57              this.compoundDocument = new RandomAccessFile( this.getTempBackingFile().getFile(), "r" );
58              this.header = new Header( this.compoundDocument );
59              this.endianDecoder = this.header.getEndianDecoder();
60          } catch ( FileNotFoundException ex ) {
61              if ( this.getTempBackingFile().getFile().isDirectory() ) {
62                  // The file already exists and is a directory!
63                  throw VCannotOpenEntryAlert.entryIsNotAFile( this.getTempBackingFile() ).setParentThrowable( ex ).mishap();
64              }
65              // The entry doesn't exist at all.
66              throw VCannotOpenEntryAlert.fileNotFound( this.getTempBackingFile() ).setParentThrowable( ex ).mishap();
67          } catch ( IOException ex ) {
68              throw new IOAlert(
69                  "Could not read compound document header"
70              ).setParentThrowable( ex ).mishap();
71          }
72          this.directory = this.readDirectory();
73      }
74  
75      /**
76       * @return Returns the directory.
77       */
78      public DirectoryEntry[] getDirectory() {
79          return directory;
80      }
81  
82      DirectoryEntry getDirectoryEntry( final int did ) {
83          if ( did <= this.directory.length ) {
84              return this.directory[ did ];
85          } else {
86              throw VEntryNotFoundAlert.entryDoesNotExist().culprit(
87                  "did",
88                  did
89              ).culprit(
90                  "directory entries",
91                  this.directory.length
92              ).mishap();
93          }
94      }
95  
96      private DirectoryEntry[] readDirectory() {
97          // Fetch the SID chain for the directory
98          final int[] directorySIDChain = this.header.getSectorChain( this.header.getDirectorySID() );
99          final byte[] rawData = this.readStream(
100             directorySIDChain.length * this.header.getSectorSize(),
101             directorySIDChain
102         );
103         final DirectoryEntry[] parsedData = new DirectoryEntry[ rawData.length / 128 ]; 
104         for ( int did = 0; did < parsedData.length; did++ ) {
105             parsedData[ did ] = new DirectoryEntry( this.endianDecoder, rawData, did );
106         }
107         return parsedData;
108     }
109 
110     byte[] readStream( final DirectoryEntry entry ) {
111         // Check the entries stream size against the minimum standard stream
112         // size in the header
113         if ( entry.getStreamSize() < this.header.getMinimumStandardStreamSize() ) {
114             // It's a short stream
115             return this.readShortStream(
116                 entry.getStreamSize(),
117                 this.header.getShortSectorChain( entry.getStreamSID() )
118             );
119         } else {
120             // It's a normal stream
121             return this.readStream(
122                 entry.getStreamSize(),
123                 this.header.getSectorChain( entry.getStreamSID() )
124             );
125         }
126     }
127 
128     byte[] readStream( final int streamSize, final int[] sidChain ) {
129         // Make the buffer for the stream
130         final byte[] buffer = new byte[ streamSize ];
131         try {
132             // Read the stream into the buffer
133             for ( int i = 0, offset = 0; i < sidChain.length; i++, offset += this.header.getSectorSize() ) {
134                 // Seek to the sector for the current SID
135                 this.compoundDocument.seek(
136                     this.header.getSectorOffset( sidChain[ i ] )
137                 );
138                 // Now read the sector
139                 if ( streamSize - offset < this.header.getSectorSize() ) {
140                     this.compoundDocument.readFully( buffer, offset, streamSize - offset );
141                 } else {
142                     this.compoundDocument.readFully( buffer, offset, this.header.getSectorSize() );
143                 }
144             }
145         } catch ( IOException e ) {
146             throw new IOAlert(
147                 "Could not read the compound document stream"
148             ).mishap();
149         }
150         return buffer;
151     }
152 
153     byte[] readShortStream( final int streamSize, final int[] sidChain ) {
154           // Make the buffer for the stream
155         final byte[] buffer = new byte[ streamSize ];
156         // The short stream is made up of short sectors held in a normal stream
157         // made of normal sectors.
158         // Get the short stream container SID chain
159         final int[] shortStreamContainerSidChain = this.header.getSectorChain( this.directory[ 0 ].getStreamSID() );
160         try {
161             // For each short sector we need to work out the offset within the
162             // short stream container, then work out the sector which contains
163             // that part of the short stream container and the offset within
164             // that sector. Phew.
165             // Read the short stream into the buffer
166             for ( int i = 0, offset = 0; i < sidChain.length; i++, offset += this.header.getShortSectorSize() ) {
167                 // Now for each short sector we need to work out where it is in
168                 // overall document, e.g. the sector which contains it and the
169                 // offset within that sector...
170                 final int requiredSectorIndex = offset / this.header.getSectorSize();
171                 final int offsetWithinSector = offset % this.header.getSectorSize();
172                 // Seek to the short sector for the current SID
173                 this.compoundDocument.seek(
174                     offsetWithinSector + this.header.getSectorOffset(
175                         shortStreamContainerSidChain[ requiredSectorIndex ]
176                     )
177                 );
178                 // Now read the sector
179                 if ( streamSize - offset < this.header.getShortSectorSize() ) {
180                     this.compoundDocument.readFully( buffer, offset, streamSize - offset );
181                 } else {
182                     this.compoundDocument.readFully( buffer, offset, this.header.getShortSectorSize() );
183                 }
184             }
185         } catch ( IOException ex ) {
186             throw new IOAlert(
187                 "Could not read the compound document short stream"
188             ).mishap();
189         }
190         return buffer;
191     }
192 
193     /**
194      * @see org.millscript.commons.vfs.mime.AbstractMIMEVVolume#makeRootVFolder(org.millscript.commons.vfs.VFile)
195      */
196     @Override
197     protected VFolder makeRootVFolder( final VFile sourceFile ) {
198         return new CompoundDocumentFolder( this, sourceFile.getParent(), 0 );
199     }
200 
201 }