1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
63 throw VCannotOpenEntryAlert.entryIsNotAFile( this.getTempBackingFile() ).setParentThrowable( ex ).mishap();
64 }
65
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
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
112
113 if ( entry.getStreamSize() < this.header.getMinimumStandardStreamSize() ) {
114
115 return this.readShortStream(
116 entry.getStreamSize(),
117 this.header.getShortSectorChain( entry.getStreamSID() )
118 );
119 } else {
120
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
130 final byte[] buffer = new byte[ streamSize ];
131 try {
132
133 for ( int i = 0, offset = 0; i < sidChain.length; i++, offset += this.header.getSectorSize() ) {
134
135 this.compoundDocument.seek(
136 this.header.getSectorOffset( sidChain[ i ] )
137 );
138
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
155 final byte[] buffer = new byte[ streamSize ];
156
157
158
159 final int[] shortStreamContainerSidChain = this.header.getSectorChain( this.directory[ 0 ].getStreamSID() );
160 try {
161
162
163
164
165
166 for ( int i = 0, offset = 0; i < sidChain.length; i++, offset += this.header.getShortSectorSize() ) {
167
168
169
170 final int requiredSectorIndex = offset / this.header.getSectorSize();
171 final int offsetWithinSector = offset % this.header.getSectorSize();
172
173 this.compoundDocument.seek(
174 offsetWithinSector + this.header.getSectorOffset(
175 shortStreamContainerSidChain[ requiredSectorIndex ]
176 )
177 );
178
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 }