Merge branch 'features/r2_11_2/JAL-3821_reinstate_patch' into develop
[jalview.git] / unused / javajs / util / CompoundDocument.java
1 /* $RCSfile$
2  * $Author: egonw $
3  * $Date: 2006-03-18 15:59:33 -0600 (Sat, 18 Mar 2006) $
4  * $Revision: 4652 $
5  *
6  * Some portions of this file have been modified by Robert Hanson hansonr.at.stolaf.edu 2012-2017
7  * for use in SwingJS via transpilation into JavaScript using Java2Script.
8  *
9  * Copyright (C) 2003-2005  Miguel, Jmol Development, www.jmol.org
10  *
11  * Contact: hansonr@stolaf.edu
12  *
13  *  This library is free software; you can redistribute it and/or
14  *  modify it under the terms of the GNU Lesser General Public
15  *  License as published by the Free Software Foundation; either
16  *  version 2.1 of the License, or (at your option) any later version.
17  *
18  *  This library is distributed in the hope that it will be useful,
19  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
20  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21  *  Lesser General Public License for more details.
22  *
23  *  You should have received a copy of the GNU Lesser General Public
24  *  License along with this library; if not, write to the Free Software
25  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
26  */
27 package javajs.util;
28
29
30 import java.io.DataInputStream;
31 import java.io.BufferedInputStream;
32
33
34 import java.util.Map;
35
36 import javajs.api.GenericZipTools;
37
38
39
40 /* a simple compound document reader. 
41  * 
42  * DIRECTORY STRUCTURE IS NOT REGENERATED
43  * 
44  * See http://sc.openoffice.org/compdocfileformat.pdf
45  * 
46  * random access file info: 
47  * http://java.sun.com/docs/books/tutorial/essential/io/rafs.html
48  * 
49  * SHOOT! random access is only for applications, not applets!
50  * 
51  * With a bit more work, this could be set up to deliver binary files, but
52  * right now I've only implemented it for string-based data. 
53  * 
54  */
55
56 public class CompoundDocument extends BinaryDocument{
57
58 //  RandomAccessFile file;
59   CompoundDocHeader header = new CompoundDocHeader(this);
60   Lst<CompoundDocDirEntry> directory = new  Lst<CompoundDocDirEntry>();
61   CompoundDocDirEntry rootEntry;
62
63   protected GenericZipTools jzt;
64
65   int[] SAT;
66   int[] SSAT;
67   int sectorSize;
68   int shortSectorSize;
69   int nShortSectorsPerStandardSector;
70   int nIntPerSector;
71   int nDirEntriesperSector;
72
73   // called by reflection
74   
75   public CompoundDocument(){
76     super();
77     this.isBigEndian = true;
78   }
79   
80   public void setDocStream(GenericZipTools jzt, BufferedInputStream bis) {
81     this.jzt = jzt;
82     if (!isRandom) {
83       stream = new DataInputStream(bis);
84     }
85     stream.mark(Integer.MAX_VALUE);
86     if (!readHeader())
87       return;
88     getSectorAllocationTable();
89     getShortSectorAllocationTable();
90     getDirectoryTable();
91   }
92
93   public Lst<CompoundDocDirEntry> getDirectory() {
94     return directory;
95   }
96
97   public String getDirectoryListing(String separator) {
98     SB sb = new SB();
99     for (int i = 0; i < directory.size(); i++) {
100       CompoundDocDirEntry thisEntry = directory.get(i);
101       if (!thisEntry.isEmpty)
102         sb.append(separator).append(thisEntry.entryName)
103         .append("\tlen=").appendI(thisEntry.lenStream)
104         .append("\tSID=").appendI(thisEntry.SIDfirstSector)
105         .append(thisEntry.isStandard ? "\tfileOffset="
106                 + getOffset(thisEntry.SIDfirstSector) : "");
107     }
108     return sb.toString();
109   }
110
111   public SB getAllData() {
112     return getAllDataFiles(null, null);
113   }
114
115   /**
116    * reads a compound document directory and saves all data in a Hashtable
117    * so that the files may be organized later in a different order. Also adds
118    * a #Directory_Listing entry.
119    * 
120    * Files are bracketed by BEGIN Directory Entry and END Directory Entry lines, 
121    * similar to ZipUtil.getAllData.
122    * 
123    * @param prefix
124    * @param binaryFileList   |-separated list of files that should be saved
125    *                         as xx xx xx hex byte strings. The directory listing
126    *                         is appended with ":asBinaryString"
127    * @param fileData
128    */
129   @Override
130   public void getAllDataMapped(String prefix, 
131                          String binaryFileList, Map<String, String> fileData) {
132     fileData.put("#Directory_Listing", getDirectoryListing("|"));
133     binaryFileList = "|" + binaryFileList + "|";
134     for (int i = 0; i < directory.size(); i++) {
135       CompoundDocDirEntry thisEntry = directory.get(i);
136       if (!thisEntry.isEmpty && thisEntry.entryType != 5) {
137       String name = thisEntry.entryName;
138         System.out.println("CompoundDocument file " + name);
139         boolean isBinary = (binaryFileList.indexOf("|" + name + "|") >= 0);
140         if (isBinary)
141           name += ":asBinaryString";
142         fileData.put(prefix + "/" + name, appendData(new SB(), name, thisEntry, isBinary).toString());
143       }
144     }
145     close();
146   }
147
148   @Override
149   public SB getAllDataFiles(String binaryFileList, String firstFile) {
150 // firstFile is now ignored
151 //    if (firstFile != null) {
152 //      for (int i = 0; i < directory.size(); i++) {
153 //        CompoundDocDirEntry thisEntry = directory.get(i);
154 //        if (thisEntry.entryName.equals(firstFile)) {
155 //          directory.remove(i);
156 //          directory.add(1, thisEntry); // after ROOT_ENTRY
157 //          break;
158 //        }
159 //      }
160 //    }
161     SB data = new SB();
162     data.append("Compound Document File Directory: ");
163     data.append(getDirectoryListing("|"));
164     data.append("\n");
165     CompoundDocDirEntry thisEntry;
166     binaryFileList = "|" + binaryFileList + "|";
167     for (int i = 0, n = directory.size(); i < n; i++) {
168       thisEntry = directory.get(i);
169       //System.out.println("CompoundDocument reading " + thisEntry.entryName);
170       String name = thisEntry.entryName;
171       switch (thisEntry.entryType) {
172       case 5: // root
173         break;
174       case 1: // user storage (dir)
175         data.append("NEW Directory ").append(name).append("\n");            
176         break;
177       case 2: // user stream (file)
178         if (name.endsWith(".gz"))
179           name = name.substring(0, name.length() - 3);
180         appendData(data, name, thisEntry, binaryFileList.indexOf("|" + thisEntry.entryName + "|") >= 0);
181         break;
182       }
183     }
184     close();
185     return data;
186   }
187
188   private SB appendData(SB data, String name, CompoundDocDirEntry thisEntry,
189                           boolean isBinary) {
190     data.append("BEGIN Directory Entry ").append(name).append("\n");            
191     data.appendSB(getEntryAsString(thisEntry, isBinary));
192     data.append("\nEND Directory Entry ").append(name).append("\n");
193     return data;
194   }
195
196   public SB getFileAsString(String entryName) {
197     for (int i = 0; i < directory.size(); i++) {
198       CompoundDocDirEntry thisEntry = directory.get(i);
199       if (thisEntry.entryName.equals(entryName))
200         return getEntryAsString(thisEntry, false);
201     }
202     return new SB();
203   }
204
205   private long getOffset(int SID) {
206     return (SID + 1) * sectorSize;
207   }
208
209   private void gotoSector(int SID) {
210     seek(getOffset(SID));
211   }
212
213   private boolean readHeader() {
214     if (!header.readData())
215       return false;
216     sectorSize = 1 << header.sectorPower;
217     shortSectorSize = 1 << header.shortSectorPower;
218     nShortSectorsPerStandardSector = sectorSize / shortSectorSize; // e.g. 512 / 64 = 8
219     nIntPerSector = sectorSize / 4; // e.g. 512 / 4 = 128
220     nDirEntriesperSector = sectorSize / 128; // e.g. 512 / 128 = 4
221 //    System.out.println(
222 //          "compound document: revNum=" + header.revNumber +
223 //          " verNum=" + header.verNumber + " isBigEndian=" + isBigEndian +
224 //          " bytes per standard/short sector=" + sectorSize + "/" + shortSectorSize);
225     return true;
226   }
227
228   private void getSectorAllocationTable() {
229     int nSID = 0;
230     int thisSID;
231     SAT = new int[header.nSATsectors * nIntPerSector + 109];
232
233     try {
234       for (int i = 0; i < 109; i++) {
235         thisSID = header.MSAT0[i];
236         if (thisSID < 0)
237           break;
238         gotoSector(thisSID);
239         for (int j = 0; j < nIntPerSector; j++) {
240           SAT[nSID++] = readInt();
241           //Logger.debug(thisSID+"."+j + "/" + (nSID - 1) + " : " + SAT[nSID - 1]);
242         }
243       }
244       int nMaster = header.nAdditionalMATsectors;
245       thisSID = header.SID_MSAT_next;
246       int[] MSAT = new int[nIntPerSector];
247       out: while (nMaster-- > 0 && thisSID >= 0) {
248         // read a page of sector identifiers pointing to SAT sectors
249         gotoSector(thisSID);
250         for (int i = 0; i < nIntPerSector; i++)
251           MSAT[i] = readInt();
252         // read each page of SAT sector identifiers 
253         // last entry is pointer to next master sector allocation table page
254         for (int i = 0; i < nIntPerSector - 1; i++) {
255           thisSID = MSAT[i];
256           if (thisSID < 0)
257             break out;
258           gotoSector(thisSID);
259           for (int j = nIntPerSector; --j >= 0;)
260             SAT[nSID++] = readInt();
261         }
262         thisSID = MSAT[nIntPerSector - 1];
263       }
264     } catch (Exception e) {
265       System.out.println(e.toString());
266     }
267   }
268
269   private void getShortSectorAllocationTable() {
270     int nSSID = 0;
271     int thisSID = header.SID_SSAT_start;
272     int nMax = header.nSSATsectors * nIntPerSector;
273     SSAT = new int[nMax];
274     try {
275       while (thisSID > 0 && nSSID < nMax) {
276         gotoSector(thisSID);
277         for (int j = 0; j < nIntPerSector; j++) {
278           SSAT[nSSID++] = readInt();
279           //System.out.println("short: " + thisSID+"."+j+" SSID=" +(nSSID-1)+" "+SSAT[nSSID-1]);
280         }
281         thisSID = SAT[thisSID];
282       }
283     } catch (Exception e) {
284       System.out.println(e.toString());
285     }
286   }
287
288   private void getDirectoryTable() {
289     int thisSID = header.SID_DIR_start;
290     CompoundDocDirEntry thisEntry;
291     rootEntry = null;
292     try {
293       while (thisSID > 0) {
294         gotoSector(thisSID);
295         for (int j = nDirEntriesperSector; --j >= 0;) {
296           thisEntry = new CompoundDocDirEntry(this);
297           thisEntry.readData();
298           directory.addLast(thisEntry);
299           if (thisEntry.entryType == 5)
300             rootEntry = thisEntry;
301         }
302         thisSID = SAT[thisSID];
303       }
304     } catch (Exception e) {
305       System.out.println(e.toString());
306     }
307 //    System.out.println("CompoundDocument directory entry: \n"
308 //        + getDirectoryListing("\n"));
309   }
310
311   private SB getEntryAsString(CompoundDocDirEntry thisEntry, boolean asBinaryString) {
312     if(thisEntry.isEmpty)
313       return new SB();
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));
318   }
319   private SB getStandardStringData(int thisSID, int nBytes,
320                                              boolean asBinaryString) {
321     SB data = new SB();
322     byte[] byteBuf = new byte[sectorSize];
323     ZipData gzipData = new ZipData(nBytes);
324     try {
325       while (thisSID > 0 && nBytes > 0) {
326         gotoSector(thisSID);
327         nBytes = getSectorData(data, byteBuf, sectorSize, nBytes, asBinaryString, gzipData);
328         thisSID = SAT[thisSID];
329       }
330       if (nBytes == -9999)
331         return new SB();
332     } catch (Exception e) {
333       System.out.println(e.toString());
334     }
335     if (gzipData.isEnabled)
336       gzipData.addTo(jzt, data);
337     return data;
338   }
339
340   private int getSectorData(SB data, byte[] byteBuf,
341                             int nSectorBytes, int nBytes, 
342                             boolean asBinaryString, ZipData gzipData)
343       throws Exception {
344     readByteArray(byteBuf, 0, byteBuf.length);
345     int n = gzipData.addBytes(byteBuf, nSectorBytes, nBytes);
346     if (n >= 0)
347       return n;
348     if (asBinaryString) {
349       for (int i = 0; i < nSectorBytes; i++) {
350         data.append(Integer.toHexString(byteBuf[i] & 0xFF)).appendC(' ');
351         if (--nBytes < 1)
352           break;
353       }
354     } else {
355       for (int i = 0; i < nSectorBytes; i++) {
356         if (byteBuf[i] == 0)
357           return -9999; // don't allow binary data
358         data.appendC((char) byteBuf[i]);
359         if (--nBytes < 1)
360           break;
361       }
362     }
363     return nBytes;
364   }
365
366   private SB getShortStringData(int shortSID, int nBytes, boolean asBinaryString) {
367     SB data = new SB();
368     if (rootEntry == null)
369       return data;
370     int thisSID = rootEntry.SIDfirstSector;
371     int ptShort = 0;
372     byte[] byteBuf = new byte[shortSectorSize];
373     ZipData gzipData = new ZipData(nBytes);
374     try {
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];
381         }
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);
386       }
387     } catch (Exception e) {
388       System.out.println(data.toString());
389       System.out.println("reader error in CompoundDocument " + e.toString());
390     }
391     if (gzipData.isEnabled)
392       gzipData.addTo(jzt, data);
393     return data;
394   }  
395 }