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