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 import java.io.OutputStream; 25 import java.io.InputStream; 26 import java.io.DataOutputStream; 27 import java.io.DataInputStream; 28 import java.io.IOException; 29 import java.io.UnsupportedEncodingException; 30 31 /** 32 * <p>This class contains data for a single Palm database for use during 33 * a conversion process.</p> 34 * 35 * <p>It contains zero or more <code>Record</code> objects stored in an 36 * array. The index of the <code>Record</code> object in the array is 37 * the record id or number for that specific <code>Record</code> object. 38 * Note that this class does not check for maximum number of records 39 * allowable in an actual pdb.</p> 40 * 41 * <p>This class also contains the pdb name associated with the Palm database 42 * it represents. A pdb name consists of 32 bytes of a certain encoding 43 * (extended ASCII in this case).</p> 44 * 45 * <p>The non default constructors take in a name parameter which may not 46 * be the exact pdb name to be used. The name parameter in 47 * <code>String</code> or <code>byte[]</code> are converted to an exact 48 * <code>NAME_LENGTH</code> byte array. If the length of the name is less 49 * than <code>NAME_LENGTH</code>, it is padded with '\0' characters. If it 50 * is more, it gets truncated. The last character in the resulting byte 51 * array is always a '\0' character. The resulting byte array is stored in 52 * <code>bName</code>, and a corresponding String object <code>sName</code> 53 * that contains characters without the '\0' characters.</p> 54 * 55 * <p>The {@link #write write} method is called within the 56 * {@link zensync.util.palm.PalmDBSet#write PalmDBSet.write} method 57 * for writing out its data to the <code>OutputStream</code> object.</p> 58 * 59 * <p>The {@link #read read} method is called within the 60 * {@link zensync.util.palm.PalmDBSet#read PalmDBSet.read} method 61 * for reading in its data from the <code>InputStream</code> object.</p> 62 * 63 * @author Akhil Arora, Herbie Ong 64 * @see PalmDBSet 65 * @see Record 66 */ 67 68 public final class PalmDB { 69 70 /** number of bytes for the name field in the pdb */ 71 public final static int NAME_LENGTH = 32; 72 73 /** list of Record objects */ 74 private Record[] records; 75 76 /** pdb name in bytes */ 77 private byte[] bName = null; 78 79 /** pdb name in String */ 80 private String sName = null; 81 82 83 /** 84 * Default constructor for use after a read(). 85 */ 86 PalmDB()87 public PalmDB() { 88 89 records = new Record[0]; 90 } 91 92 /** 93 * Constructor to create object with Record objects. 94 * recs.length can be zero for an empty pdb. 95 * 96 * @param name suggested pdb name in String 97 * @param recs array of Record objects 98 * @throws NullPointerException if recs is null 99 */ 100 PalmDB(String name, Record[] recs)101 public PalmDB(String name, Record[] recs) 102 throws UnsupportedEncodingException { 103 104 this(name.getBytes(PDBUtil.ENCODING), recs); 105 } 106 107 /** 108 * Constructor to create object with Record objects. 109 * recs.length can be zero for an empty pdb. 110 * 111 * @param name suggested pdb name in byte array 112 * @param recs array of Record objects 113 * @throws NullPointerException if recs is null 114 */ 115 PalmDB(byte[] name, Record[] recs)116 public PalmDB(byte[] name, Record[] recs) 117 throws UnsupportedEncodingException { 118 119 store(name); 120 121 records = new Record[recs.length]; 122 System.arraycopy(recs, 0, records, 0, recs.length); 123 } 124 125 /** 126 * This private method is mainly used by the constructors above. 127 * to store bytes into name and also create a String representation. 128 * and also by the read method. 129 * 130 * TODO: Note that this method assumes that the byte array parameter 131 * contains one character per byte, else it would truncate 132 * improperly. 133 * 134 * @param bytes pdb name in byte array 135 * @throws UnsupportedEncodingException if ENCODING is not supported 136 */ 137 store(byte[] bytes)138 private void store(byte[] bytes) throws UnsupportedEncodingException { 139 140 // note that this will initialize all bytes in name to 0. 141 bName = new byte[NAME_LENGTH]; 142 143 // determine minimum length to copy over from bytes to bName. 144 // Note that the last byte in bName has to be '\0'. 145 146 int lastIndex = NAME_LENGTH - 1; 147 148 int len = (bytes.length < lastIndex)? bytes.length: lastIndex; 149 150 int i; 151 152 for (i = 0; i < len; i++) { 153 154 if (bytes[i] == 0) { 155 break; 156 } 157 158 bName[i] = bytes[i]; 159 } 160 161 // set sName, no need to include the '\0' character. 162 sName = new String(bName, 0, i, PDBUtil.ENCODING); 163 } 164 165 /** 166 * Return the number of records contained in this 167 * pdb PalmDB object. 168 * 169 * @return int number of Record objects 170 */ 171 getRecordCount()172 public int getRecordCount() { 173 174 return records.length; 175 } 176 177 /** 178 * Return the specific Record object associated 179 * with the record number. 180 * 181 * @param index record index number 182 * @return Record the Record object in the specified index 183 * @throws ArrayIndexOutOfBoundsException if index is out of bounds 184 */ 185 getRecord(int index)186 public Record getRecord(int index) { 187 188 return records[index]; 189 } 190 191 /** 192 * Return the list of Record objects 193 * 194 * @return Record[] the list of Record objects 195 */ 196 getRecords()197 public Record[] getRecords() { 198 199 return records; 200 } 201 202 /** 203 * Return the PDBName associated with this object in String 204 * 205 * @return String pdb name in String 206 */ 207 getPDBNameString()208 public String getPDBNameString() { 209 210 return sName; 211 } 212 213 /** 214 * Return the PDBName associated with this object 215 * in byte array of exact length of 32 bytes. 216 * 217 * @return byte[] pdb name in byte[] of length 32. 218 */ 219 getPDBNameBytes()220 public byte[] getPDBNameBytes() { 221 222 return bName; 223 } 224 225 /** 226 * Write out the number of records followed by what 227 * will be written out by each Record object. 228 * 229 * @param os the stream to write the object to 230 * @throws IOException if any I/O error occurs 231 */ 232 write(OutputStream os)233 public void write(OutputStream os) throws IOException { 234 235 DataOutputStream out = new DataOutputStream(os); 236 237 // write out pdb name 238 out.write(bName); 239 240 // write out 2 bytes for number of records 241 out.writeShort(records.length); 242 243 // let each Record object write out its own info. 244 for (int i = 0; i < records.length; i++) 245 records[i].write(out); 246 } 247 248 /** 249 * Read the necessary data to create a pdb from 250 * the input stream. 251 * 252 * @param is the stream to read data from in order 253 * to restore the object 254 * @throws IOException if any I/O error occurs 255 */ 256 read(InputStream is)257 public void read(InputStream is) throws IOException { 258 259 DataInputStream in = new DataInputStream(is); 260 261 // read in the pdb name. 262 byte[] bytes = new byte[NAME_LENGTH]; 263 in.readFully(bytes); 264 store(bytes); 265 266 // read in number of records 267 int nrec = in.readUnsignedShort(); 268 records = new Record[nrec]; 269 270 // read in the Record infos 271 for (int i = 0; i < nrec; i++) { 272 273 records[i] = new Record(); 274 records[i].read(in); 275 } 276 } 277 278 /** 279 * Override equals method of Object. 280 * 281 * 2 PalmDB objects are equal if they contain the same information, 282 * i.e. pdb name and records. 283 * 284 * This is used primarily for testing purposes only for now. 285 * 286 * @param obj a PalmDB object to compare with 287 * @return boolean true if obj is equal to this, else false. 288 */ 289 equals(Object obj)290 public boolean equals(Object obj) { 291 292 boolean bool = false; 293 294 if (obj instanceof PalmDB) { 295 296 PalmDB pdb = (PalmDB) obj; 297 298 checkLabel: { 299 300 // compare sName 301 302 if (!sName.equals(pdb.sName)) { 303 304 break checkLabel; 305 } 306 307 // compare bName 308 309 if (bName.length != pdb.bName.length) { 310 311 break checkLabel; 312 } 313 314 for (int i = 0; i < bName.length; i++) { 315 316 if (bName[i] != pdb.bName[i]) { 317 318 break checkLabel; 319 } 320 } 321 322 // compare each Record 323 324 if (records.length != pdb.records.length) { 325 326 break checkLabel; 327 } 328 329 for (int i = 0; i < records.length; i++) { 330 331 if (!records[i].equals(pdb.records[i])) { 332 333 break checkLabel; 334 } 335 } 336 337 // all checks done 338 bool = true; 339 } 340 } 341 342 return bool; 343 } 344 } 345