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.OutputStream; 27 import java.io.InputStream; 28 import java.io.DataOutputStream; 29 import java.io.DataInputStream; 30 import java.io.IOException; 31 import java.io.UnsupportedEncodingException; 32 33 /** 34 * <p>This class contains data for a single Palm database for use during 35 * a conversion process.</p> 36 * 37 * <p>It contains zero or more <code>Record</code> objects stored in an 38 * array. The index of the <code>Record</code> object in the array is 39 * the <code>Record</code> id or number for that specific <code>Record</code> object. 40 * Note that this class does not check for maximum number of Records 41 * allowable in an actual PDB.</p> 42 * 43 * <p>This class also contains the PDB name associated with the Palm 44 * database it represents. A PDB name consists of 32 bytes of a 45 * certain encoding (extended ASCII in this case).</p> 46 * 47 * <p>The non default constructors take in a name parameter which may not 48 * be the exact PDB name to be used. The name parameter in 49 * <code>String</code> or <code>byte</code> array are converted to an exact 50 * <code>NAME_LENGTH</code> byte array. If the length of the name is less 51 * than <code>NAME_LENGTH</code>, it is padded with '\0' characters. If it 52 * is more, it gets truncated. The last character in the resulting byte 53 * array is always a '\0' character. The resulting byte array is stored in 54 * <code>bName</code>, and a corresponding String object <code>sName</code> 55 * that contains characters without the '\0' characters.</p> 56 * 57 * <p>The {@link #write write} method is called within the 58 * {@link org.openoffice.xmerge.converter.palm.PalmDocument#write 59 * PalmDocument.write} method for writing out its data to the <code>OutputStream</code> 60 * object.</p> 61 * 62 * <p>The {@link #read read} method is called within the 63 * {@link org.openoffice.xmerge.converter.palm.PalmDocument#read 64 * PalmDocument.read} method for reading in its data from the <code>InputStream</code> 65 * object.</p> 66 * 67 * @author Akhil Arora, Herbie Ong 68 * @see PalmDocument 69 * @see Record 70 */ 71 72 public final class PalmDB { 73 74 /* Backup attribute for a PDB. This corresponds to dmHdrAttrBackup. */ 75 public final static short PDB_HEADER_ATTR_BACKUP = 0x0008; 76 77 /** Number of bytes for the name field in the PDB. */ 78 public final static int NAME_LENGTH = 32; 79 80 /** List of <code>Record</code> objects. */ 81 private Record[] records; 82 83 /** PDB name in bytes. */ 84 private byte[] bName = null; 85 86 /** PDB name in String. */ 87 private String sName = null; 88 89 /** Creator ID. */ 90 private int creatorID = 0; 91 92 /** Type ID */ 93 private int typeID = 0; 94 95 /** 96 * PDB version. Palm UInt16. 97 * It is treated as a number here, since there is no unsigned 16 bit 98 * in Java, int is used instead, but only 2 bytes are written out or 99 * read in. 100 */ 101 private int version = 0; 102 103 /** 104 * PDB attribute - flags for the database. 105 * Palm UInt16. Unsignedness should be irrelevant. 106 */ 107 private short attribute = 0; 108 109 110 /** 111 * Default constructor. 112 * 113 * @param creatorID The PDB Creator ID. 114 * @param typeID The PDB Type ID. 115 * @param version The PDB header version. 116 * @param attribute The PDB header attribute. 117 */ PalmDB(int creatorID, int typeID, int version, short attribute)118 public PalmDB(int creatorID, int typeID, int version, short attribute) { 119 120 records = new Record[0]; 121 setAttributes(creatorID, typeID, version, attribute); 122 } 123 124 125 /** 126 * Constructor to create <code>PalmDB</code> object with 127 * <code>Record</code> objects. <code>recs.length</code> 128 * can be zero for an empty PDB. 129 * 130 * @param name Suggested PDB name in a <code>String</code>. 131 * @param creatorID The PDB Creator ID. 132 * @param typeID The PDB Type ID. 133 * @param version The PDB header version. 134 * @param attribute The PDB header attribute. 135 * @param recs Array of <code>Record</code> objects. 136 * 137 * @throws UnsupportedEncodingException If <code>name</code> is 138 * not properly encoded. 139 * @throws NullPointerException If <code>recs</code> is null. 140 */ PalmDB(String name, int creatorID, int typeID, int version, short attribute, Record[] recs)141 public PalmDB(String name, int creatorID, int typeID, int version, 142 short attribute, Record[] recs) 143 throws UnsupportedEncodingException { 144 145 this(name.getBytes(PdbUtil.ENCODING), creatorID, typeID, version, 146 attribute, recs); 147 } 148 149 150 /** 151 * Constructor to create object with <code>Record</code> 152 * objects. <code>recs.length</code> can be zero for an 153 * empty PDB. 154 * 155 * @param name Suggested PDB name in a <code>byte</code> 156 * array. 157 * @param creatorID The PDB Creator ID. 158 * @param typeID The PDB Type ID. 159 * @param version The PDB header version. 160 * @param attribute The PDB header attribute. 161 * @param recs Array of <code>Record</code> objects. 162 * 163 * @throws UnsupportedEncodingException If <code>name</code> is 164 * not properly encoded. 165 * @throws NullPointerException If recs is null. 166 */ PalmDB(byte[] name, int creatorID, int typeID, int version, short attribute, Record[] recs)167 public PalmDB(byte[] name, int creatorID, int typeID, int version, 168 short attribute, Record[] recs) throws UnsupportedEncodingException { 169 170 store(name); 171 172 records = new Record[recs.length]; 173 System.arraycopy(recs, 0, records, 0, recs.length); 174 setAttributes(creatorID, typeID, version, attribute); 175 } 176 177 178 /** 179 * Set the attributes for the <code>PalmDB</code> object. 180 * 181 * @param creatorID The PDB Creator ID. 182 * @param typeID The PDB Type ID. 183 * @param version The PDB header version. 184 * @param attribute The PDB header attribute. 185 */ setAttributes(int creatorID, int typeID, int version, short attribute)186 public void setAttributes (int creatorID, int typeID, int version, short attribute) { 187 this.creatorID = creatorID; 188 this.typeID = typeID; 189 this.version = version; 190 this.attribute = attribute; 191 } 192 193 194 /** 195 * This private method is mainly used by the constructors above. 196 * to store bytes into name and also create a <code>String</code> 197 * representation. and also by the <code>read</code> method. 198 * 199 * TODO: Note that this method assumes that the <code>byte</code> 200 * array parameter contains one character per <code>byte</code>, 201 * else it would truncate improperly. 202 * 203 * @param bytes PDB name in <code>byte</code> array. 204 * 205 * @throws UnsupportedEncodingException If ENCODING is 206 * not supported. 207 */ store(byte[] bytes)208 private void store(byte[] bytes) throws UnsupportedEncodingException { 209 210 // note that this will initialize all bytes in name to 0. 211 bName = new byte[NAME_LENGTH]; 212 213 // determine minimum length to copy over from bytes to bName. 214 // Note that the last byte in bName has to be '\0'. 215 216 int lastIndex = NAME_LENGTH - 1; 217 218 int len = (bytes.length < lastIndex)? bytes.length: lastIndex; 219 220 int i; 221 222 for (i = 0; i < len; i++) { 223 224 if (bytes[i] == 0) { 225 break; 226 } 227 228 bName[i] = bytes[i]; 229 } 230 231 // set sName, no need to include the '\0' character. 232 sName = new String(bName, 0, i, PdbUtil.ENCODING); 233 } 234 235 236 /** 237 * Returns creator ID. 238 * 239 * @return The creator ID. 240 */ getCreatorID()241 public int getCreatorID() { 242 243 return creatorID; 244 } 245 246 247 /** 248 * Returns type ID. 249 * 250 * @return The type ID. 251 */ getTypeID()252 public int getTypeID() { 253 254 return typeID; 255 } 256 257 258 /** 259 * Returns attribute flag. 260 * 261 * @return The attribute flag. 262 */ getAttribute()263 public short getAttribute() { 264 265 return attribute; 266 } 267 268 269 /** 270 * Returns version. 271 * 272 * @return The version. 273 */ getVersion()274 public int getVersion() { 275 276 return version; 277 } 278 279 280 /** 281 * Return the number of Records contained in this 282 * PDB <code>PalmDB</code> object. 283 * 284 * @return Number of <code>Record</code> objects. 285 */ getRecordCount()286 public int getRecordCount() { 287 288 return records.length; 289 } 290 291 292 /** 293 * Return the specific <code>Record</code> object associated 294 * with the <code>Record</code> number. 295 * 296 * @param index <code>Record</code> index number. 297 * 298 * @return The <code>Record</code> object in the specified index 299 * 300 * @throws ArrayIndexOutOfBoundsException If index is out of bounds. 301 */ getRecord(int index)302 public Record getRecord(int index) { 303 304 return records[index]; 305 } 306 307 308 /** 309 * Return the list of <code>Record</code> objects. 310 * 311 * @return The array of <code>Record</code> objects. 312 */ getRecords()313 public Record[] getRecords() { 314 315 return records; 316 } 317 318 /** 319 * Return the PDB name associated with this object. 320 * 321 * @return The PDB name. 322 */ getPDBNameString()323 public String getPDBNameString() { 324 325 return sName; 326 } 327 328 329 /** 330 * Return the PDB name associated with this object in 331 * <code>byte</code> array of exact length of 32 bytes. 332 * 333 * @return The PDB name in <code>byte</code> array of 334 * length 32. 335 */ getPDBNameBytes()336 public byte[] getPDBNameBytes() { 337 338 return bName; 339 } 340 341 342 /** 343 * Write out the number of Records followed by what 344 * will be written out by each <code>Record</code> object. 345 * 346 * @param os The <code>OutputStream</code> to write the 347 * object. 348 * 349 * @throws IOException If any I/O error occurs. 350 */ write(OutputStream os)351 public void write(OutputStream os) throws IOException { 352 353 DataOutputStream out = new DataOutputStream(os); 354 355 // write out PDB name 356 out.write(bName); 357 358 // write out 2 bytes for number of records 359 out.writeShort(records.length); 360 361 // let each Record object write out its own info. 362 for (int i = 0; i < records.length; i++) 363 records[i].write(out); 364 } 365 366 /** 367 * Read the necessary data to create a PDB from 368 * the <code>InputStream</code>. 369 * 370 * @param is The <code>InputStream</code> to read data 371 * in order to restore the object. 372 * 373 * @throws IOException If any I/O error occurs. 374 */ read(InputStream is)375 public void read(InputStream is) throws IOException { 376 377 DataInputStream in = new DataInputStream(is); 378 379 // read in the PDB name. 380 byte[] bytes = new byte[NAME_LENGTH]; 381 in.readFully(bytes); 382 store(bytes); 383 384 // read in number of records 385 int nrec = in.readUnsignedShort(); 386 records = new Record[nrec]; 387 388 // read in the Record infos 389 for (int i = 0; i < nrec; i++) { 390 391 records[i] = new Record(); 392 records[i].read(in); 393 } 394 } 395 396 /** 397 * Override equals method of <code>Object</code>. 398 * 399 * Two <code>PalmDB</code> objects are equal if they contain 400 * the same information, i.e. PDB name and Records. 401 * 402 * This is used primarily for testing purposes only for now. 403 * 404 * @param obj A <code>PalmDB</code> <code>Object</code> to 405 * compare. 406 * 407 * @return true if <code>obj</code> is equal to this, otherwise 408 * false. 409 */ equals(Object obj)410 public boolean equals(Object obj) { 411 412 boolean bool = false; 413 414 if (obj instanceof PalmDB) { 415 416 PalmDB pdb = (PalmDB) obj; 417 418 checkLabel: { 419 420 // compare sName 421 422 if (!sName.equals(pdb.sName)) { 423 424 break checkLabel; 425 } 426 427 // compare bName 428 429 if (bName.length != pdb.bName.length) { 430 431 break checkLabel; 432 } 433 434 for (int i = 0; i < bName.length; i++) { 435 436 if (bName[i] != pdb.bName[i]) { 437 438 break checkLabel; 439 } 440 } 441 442 // compare each Record 443 444 if (records.length != pdb.records.length) { 445 446 break checkLabel; 447 } 448 449 for (int i = 0; i < records.length; i++) { 450 451 if (!records[i].equals(pdb.records[i])) { 452 453 break checkLabel; 454 } 455 } 456 457 // all checks done 458 bool = true; 459 } 460 } 461 462 return bool; 463 } 464 } 465 466