3 * $Date: 2006-03-18 15:59:33 -0600 (Sat, 18 Mar 2006) $
6 * Copyright (C) 2003-2005 Miguel, Jmol Development, www.jmol.org
8 * Contact: hansonr@stolaf.edu
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
27 import java.io.DataInputStream;
28 import java.io.BufferedInputStream;
33 import javajs.api.GenericZipTools;
37 /* a simple compound document reader.
38 * See http://sc.openoffice.org/compdocfileformat.pdf
40 * random access file info:
41 * http://java.sun.com/docs/books/tutorial/essential/io/rafs.html
43 * SHOOT! random access is only for applications, not applets!
45 * With a bit more work, this could be set up to deliver binary files, but
46 * right now I've only implemented it for string-based data. All Jmol is using
51 public class CompoundDocument extends BinaryDocument{
53 // RandomAccessFile file;
54 CompoundDocHeader header = new CompoundDocHeader(this);
55 Lst<CompoundDocDirEntry> directory = new Lst<CompoundDocDirEntry>();
56 CompoundDocDirEntry rootEntry;
62 int nShortSectorsPerStandardSector;
64 int nDirEntriesperSector;
66 // called by reflection
68 public CompoundDocument(){
70 this.isBigEndian = true;
74 public void setStream(GenericZipTools jzt, BufferedInputStream bis, boolean isBigEndian) {
75 // isBigEndian is ignored here; it must be true
77 file = new RandomAccessFile(fileName, "r");
79 } catch (Exception e) {
85 stream = new DataInputStream(bis);
87 stream.mark(Integer.MAX_VALUE);
90 getSectorAllocationTable();
91 getShortSectorAllocationTable();
95 public Lst<CompoundDocDirEntry> getDirectory() {
99 public String getDirectoryListing(String separator) {
101 for (int i = 0; i < directory.size(); i++) {
102 CompoundDocDirEntry thisEntry = directory.get(i);
103 if (!thisEntry.isEmpty)
105 + thisEntry.entryName
107 + thisEntry.lenStream
109 + thisEntry.SIDfirstSector
110 + (thisEntry.isStandard ? "\tfileOffset="
111 + getOffset(thisEntry.SIDfirstSector) : "");
118 public SB getAllData() {
119 return getAllDataFiles(null, null);
123 * reads a compound document directory and saves all data in a Hashtable
124 * so that the files may be organized later in a different order. Also adds
125 * a #Directory_Listing entry.
127 * Files are bracketed by BEGIN Directory Entry and END Directory Entry lines,
128 * similar to ZipUtil.getAllData.
131 * @param binaryFileList |-separated list of files that should be saved
132 * as xx xx xx hex byte strings. The directory listing
133 * is appended with ":asBinaryString"
137 public void getAllDataMapped(String prefix,
138 String binaryFileList, Map<String, String> fileData) {
139 fileData.put("#Directory_Listing", getDirectoryListing("|"));
140 binaryFileList = "|" + binaryFileList + "|";
141 for (int i = 0; i < directory.size(); i++) {
142 CompoundDocDirEntry thisEntry = directory.get(i);
143 if (!thisEntry.isEmpty && thisEntry.entryType != 5) {
144 String name = thisEntry.entryName;
145 System.out.println("CompoundDocument file " + name);
146 boolean isBinary = (binaryFileList.indexOf("|" + name + "|") >= 0);
148 name += ":asBinaryString";
150 data.append("BEGIN Directory Entry ").append(name).append("\n");
151 data.appendSB(getEntryAsString(thisEntry, isBinary));
152 data.append("\nEND Directory Entry ").append(name).append("\n");
153 fileData.put(prefix + "/" + name, data.toString());
160 public SB getAllDataFiles(String binaryFileList, String firstFile) {
161 if (firstFile != null) {
162 for (int i = 0; i < directory.size(); i++) {
163 CompoundDocDirEntry thisEntry = directory.get(i);
164 if (thisEntry.entryName.equals(firstFile)) {
166 directory.add(1, thisEntry); // after ROOT_ENTRY
172 data.append("Compound Document File Directory: ");
173 data.append(getDirectoryListing("|"));
175 binaryFileList = "|" + binaryFileList + "|";
176 for (int i = 0; i < directory.size(); i++) {
177 CompoundDocDirEntry thisEntry = directory.get(i);
178 //System.out.println("CompoundDocument reading " + thisEntry.entryName);
179 if (!thisEntry.isEmpty && thisEntry.entryType != 5) {
180 String name = thisEntry.entryName;
181 if (name.endsWith(".gz"))
182 name = name.substring(0, name.length() - 3);
183 data.append("BEGIN Directory Entry ").append(name).append("\n");
184 data.appendSB(getEntryAsString(thisEntry, binaryFileList.indexOf("|" + thisEntry.entryName + "|") >= 0));
186 data.append("END Directory Entry ").append(thisEntry.entryName).append("\n");
193 public SB getFileAsString(String entryName) {
194 for (int i = 0; i < directory.size(); i++) {
195 CompoundDocDirEntry thisEntry = directory.get(i);
196 if (thisEntry.entryName.equals(entryName))
197 return getEntryAsString(thisEntry, false);
202 private long getOffset(int SID) {
203 return (SID + 1) * sectorSize;
206 private void gotoSector(int SID) {
207 seek(getOffset(SID));
210 private boolean readHeader() {
211 if (!header.readData())
213 sectorSize = 1 << header.sectorPower;
214 shortSectorSize = 1 << header.shortSectorPower;
215 nShortSectorsPerStandardSector = sectorSize / shortSectorSize; // e.g. 512 / 64 = 8
216 nIntPerSector = sectorSize / 4; // e.g. 512 / 4 = 128
217 nDirEntriesperSector = sectorSize / 128; // e.g. 512 / 128 = 4
218 // System.out.println(
219 // "compound document: revNum=" + header.revNumber +
220 // " verNum=" + header.verNumber + " isBigEndian=" + isBigEndian +
221 // " bytes per standard/short sector=" + sectorSize + "/" + shortSectorSize);
225 private void getSectorAllocationTable() {
228 SAT = new int[header.nSATsectors * nIntPerSector + 109];
231 for (int i = 0; i < 109; i++) {
232 thisSID = header.MSAT0[i];
236 for (int j = 0; j < nIntPerSector; j++) {
237 SAT[nSID++] = readInt();
238 //Logger.debug(thisSID+"."+j + "/" + (nSID - 1) + " : " + SAT[nSID - 1]);
241 int nMaster = header.nAdditionalMATsectors;
242 thisSID = header.SID_MSAT_next;
243 int[] MSAT = new int[nIntPerSector];
244 out: while (nMaster-- > 0 && thisSID >= 0) {
245 // read a page of sector identifiers pointing to SAT sectors
247 for (int i = 0; i < nIntPerSector; i++)
249 // read each page of SAT sector identifiers
250 // last entry is pointer to next master sector allocation table page
251 for (int i = 0; i < nIntPerSector - 1; i++) {
256 for (int j = nIntPerSector; --j >= 0;)
257 SAT[nSID++] = readInt();
259 thisSID = MSAT[nIntPerSector - 1];
261 } catch (Exception e) {
262 System.out.println(e.toString());
266 private void getShortSectorAllocationTable() {
268 int thisSID = header.SID_SSAT_start;
269 int nMax = header.nSSATsectors * nIntPerSector;
270 SSAT = new int[nMax];
272 while (thisSID > 0 && nSSID < nMax) {
274 for (int j = 0; j < nIntPerSector; j++) {
275 SSAT[nSSID++] = readInt();
276 //System.out.println("short: " + thisSID+"."+j+" SSID=" +(nSSID-1)+" "+SSAT[nSSID-1]);
278 thisSID = SAT[thisSID];
280 } catch (Exception e) {
281 System.out.println(e.toString());
285 private void getDirectoryTable() {
286 int thisSID = header.SID_DIR_start;
287 CompoundDocDirEntry thisEntry;
290 while (thisSID > 0) {
292 for (int j = nDirEntriesperSector; --j >= 0;) {
293 thisEntry = new CompoundDocDirEntry(this);
294 thisEntry.readData();
295 if (thisEntry.lenStream > 0) {
296 directory.addLast(thisEntry);
297 //System.out.println(thisEntry.entryName);
299 if (thisEntry.entryType == 5)
300 rootEntry = thisEntry;
302 thisSID = SAT[thisSID];
304 } catch (Exception e) {
305 System.out.println(e.toString());
307 // System.out.println("CompoundDocument directory entry: \n"
308 // + getDirectoryListing("\n"));
311 private SB getEntryAsString(CompoundDocDirEntry thisEntry, boolean asBinaryString) {
312 if(thisEntry.isEmpty)
314 //System.out.println(thisEntry.entryName + " " + thisEntry.entryType + " " + thisEntry.lenStream + " " + thisEntry.isStandard + " " + thisEntry.SIDfirstSector);
315 return (thisEntry.isStandard ? getStandardStringData(
316 thisEntry.SIDfirstSector, thisEntry.lenStream, asBinaryString)
317 : getShortStringData(thisEntry.SIDfirstSector, thisEntry.lenStream, asBinaryString));
319 private SB getStandardStringData(int thisSID, int nBytes,
320 boolean asBinaryString) {
322 byte[] byteBuf = new byte[sectorSize];
323 ZipData gzipData = new ZipData(nBytes);
325 while (thisSID > 0 && nBytes > 0) {
327 nBytes = getSectorData(data, byteBuf, sectorSize, nBytes, asBinaryString, gzipData);
328 thisSID = SAT[thisSID];
332 } catch (Exception e) {
333 System.out.println(e.toString());
335 if (gzipData.isEnabled)
336 gzipData.addTo(jzt, data);
340 private int getSectorData(SB data, byte[] byteBuf,
341 int nSectorBytes, int nBytes,
342 boolean asBinaryString, ZipData gzipData)
344 readByteArray(byteBuf, 0, byteBuf.length);
345 int n = gzipData.addBytes(byteBuf, nSectorBytes, nBytes);
348 if (asBinaryString) {
349 for (int i = 0; i < nSectorBytes; i++) {
350 data.append(Integer.toHexString(byteBuf[i] & 0xFF)).appendC(' ');
355 for (int i = 0; i < nSectorBytes; i++) {
357 return -9999; // don't allow binary data
358 data.appendC((char) byteBuf[i]);
366 private SB getShortStringData(int shortSID, int nBytes, boolean asBinaryString) {
368 if (rootEntry == null)
370 int thisSID = rootEntry.SIDfirstSector;
372 byte[] byteBuf = new byte[shortSectorSize];
373 ZipData gzipData = new ZipData(nBytes);
375 //System.out.println("CD shortSID=" + shortSID);
376 // point to correct short data sector, 512/64 = 4 per page
377 while (thisSID >= 0 && shortSID >= 0 && nBytes > 0) {
378 while (shortSID - ptShort >= nShortSectorsPerStandardSector) {
379 ptShort += nShortSectorsPerStandardSector;
380 thisSID = SAT[thisSID];
382 seek(getOffset(thisSID) + (shortSID - ptShort) * shortSectorSize);
383 nBytes = getSectorData(data, byteBuf, shortSectorSize, nBytes, asBinaryString, gzipData);
384 shortSID = SSAT[shortSID];
385 //System.out.println("CD shortSID=" + shortSID);
387 } catch (Exception e) {
388 System.out.println(data.toString());
389 System.out.println("reader error in CompoundDocument " + e.toString());
391 if (gzipData.isEnabled)
392 gzipData.addTo(jzt, data);