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