1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 package org.openoffice.xmerge.converter.palm;
25 
26 import java.io.RandomAccessFile;
27 import java.io.IOException;
28 import java.io.ByteArrayInputStream;
29 import java.io.DataInputStream;
30 
31 /**
32  *  <p>Provides functionality to decode a PDB formatted file into
33  *  a <code>PalmDB</code> object given an <code>InputStream</code>.
34  *  This class is only used by the <code>PalmDB</code> object.
35  *
36  *  <p>Sample usage:
37  *
38  *  <blockquote><pre><code>
39  *     PdbDecoder decoder = new PdbDecoder("sample.pdb");
40  *     PalmDB palmDB = decoder.parse();
41  *  </code></pre></blockquote>
42  *
43  *  <p>This decoder has the following assumptions on the PDB file:
44  *
45  *  <p><ol>
46  *  <li>There is only one RecordList section in the PDB.</li>
47  *  <li>The <code>Record</code> indices in the RecordList are sorted in
48  *      order, i.e. the first <code>Record</code> index refers to
49  *      <code>Record</code> 0, and so forth.</li>
50  *  <li>The raw <code>Record</code> in the <code>Record</code> section
51  *      are sorted as well in order, i.e. first <code>Record</code>
52  *      comes ahead of second <code>Record</code>, etc.</li>
53  *  </ol>
54  *
55  *  <p>Other decoders assume these as well.
56  *
57  *  @author  Herbie Ong
58  *  @see     PalmDB
59  *  @see     Record
60  */
61 public final class PdbDecoder {
62 
63 
64     /**
65      *  <p>This method decodes a PDB file into a <code>PalmDB</code>
66      *  object.</p>
67      *
68      *  <p>First, the header data is read using the <code>PdbHeader</code>
69      *  <code>read</code> method.  Next, the RecordList section is
70      *  read and the <code>Record</code> offsets are stored for use when
71      *  parsing the Records.  Based on these offsets, the bytes
72      *  corresponding to each <code>Record</code> are read and each is
73      *  stored in a  <code>Record</code> object.  Lastly, the data is
74      *  used to create a <code>PalmDB</code> object.</p>
75      *
76      *  @param  fileName  PDB file name.
77      *
78      *  @throws  IOException  If I/O error occurs.
79      */
parse(String fileName)80     public PalmDB parse(String fileName) throws IOException {
81 
82         RandomAccessFile file = new RandomAccessFile(fileName, "r");
83 
84         // read the PDB header
85         PdbHeader header = new PdbHeader();
86         header.read(file);
87 
88         Record recArray[] = new Record[header.numRecords];
89         if (header.numRecords != 0) {
90 
91             // read in the record indices + offsets
92 
93             int recOffset[] = new int[header.numRecords];
94             byte recAttrs[] = new byte[header.numRecords];
95 
96             for (int i = 0; i < header.numRecords; i++) {
97 
98                 recOffset[i] = file.readInt();
99 
100                 // read in attributes (1 byte) + unique id (3 bytes)
101                 // take away the unique id, store the attributes
102 
103                 int attr = file.readInt();
104                 recAttrs[i] = (byte) (attr >>> 24);
105             }
106 
107 
108             // read the records
109 
110             int len = 0;
111             byte[] bytes = null;
112 
113             int lastIndex = header.numRecords - 1;
114 
115             for (int i = 0; i < lastIndex; i++) {
116 
117                 file.seek(recOffset[i]);
118                 len = recOffset[i+1] - recOffset[i];
119                 bytes = new byte[len];
120                 file.readFully(bytes);
121 		recArray[i] = new Record(bytes, recAttrs[i]);
122 	    }
123 
124             // last record
125             file.seek(recOffset[lastIndex]);
126             len = (int) file.length() - recOffset[lastIndex];
127             bytes = new byte[len];
128             file.readFully(bytes);
129             recArray[lastIndex] = new Record(bytes, recAttrs[lastIndex]);
130 
131         }
132 
133         file.close();
134 
135         // create PalmDB and return it
136         PalmDB pdb = new PalmDB(header.pdbName, header.creatorID,
137             header.typeID, header.version, header.attribute, recArray);
138 
139         return pdb;
140     }
141 
142     /**
143      *  <p>This method decodes a PDB file into a <code>PalmDB</code>
144      *  object.</p>
145      *
146      *  <p>First, the header data is read using the <code>PdbHeader</code>
147      *  <code>read</code> method.  Next, the RecordList section is
148      *  read and the <code>Record</code> offsets are stored for use when
149      *  parsing the Records.  Based on these offsets, the bytes
150      *  corresponding to each <code>Record</code> are read and each is
151      *  stored in a  <code>Record</code> object.  Lastly, the data is
152      *  used to create a <code>PalmDB</code> object.</p>
153      *
154      *  @param  b  <code>byte[]</code> containing PDB.
155      *
156      *  @throws  IOException  If I/O error occurs.
157      */
158 
parse(byte[] b)159      public PalmDB parse(byte[] b) throws IOException {
160 
161 	 ByteArrayInputStream bais = new ByteArrayInputStream(b);
162 	 DataInputStream dis = new DataInputStream(bais);
163 
164         // read the PDB header
165 
166         PdbHeader header = new PdbHeader();
167         header.read(dis);
168 
169         Record recArray[] = new Record[header.numRecords];
170         if (header.numRecords != 0) {
171 
172             // read in the record indices + offsets
173 
174             int recOffset[] = new int[header.numRecords];
175             byte recAttrs[] = new byte[header.numRecords];
176 
177             for (int i = 0; i < header.numRecords; i++) {
178 
179                 recOffset[i] = dis.readInt();
180 
181                 // read in attributes (1 byte) + unique id (3 bytes)
182                 // take away the unique id, store the attributes
183 
184                 int attr = dis.readInt();
185                 recAttrs[i] = (byte) (attr >>> 24);
186             }
187 
188             // read the records
189 
190             int len = 0;
191             byte[] bytes = null;
192 
193             int lastIndex = header.numRecords - 1;
194 
195             for (int i = 0; i < lastIndex; i++) {
196 
197                 //dis.seek(recOffset[i]);
198 		dis.reset();
199 		dis.skip(recOffset[i]);
200                 len = recOffset[i+1] - recOffset[i];
201                 bytes = new byte[len];
202                 dis.readFully(bytes);
203                 recArray[i] = new Record(bytes, recAttrs[i]);
204             }
205 
206             // last record
207 
208             dis.reset();
209 	    len = (int) dis.available() - recOffset[lastIndex];
210 	    dis.skip(recOffset[lastIndex]);
211 	    bytes = new byte[len];
212             dis.readFully(bytes);
213             recArray[lastIndex] = new Record(bytes, recAttrs[lastIndex]);
214         }
215 
216 
217 
218         // create PalmDB and return it
219 
220         PalmDB pdb = new PalmDB(header.pdbName, header.creatorID,
221             header.typeID, header.version, header.attribute, recArray);
222 
223         return pdb;
224     }
225 
226 
227 
228 }
229 
230