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