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