| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| import java.io.OutputStream; |
| import java.io.InputStream; |
| import java.io.DataOutputStream; |
| import java.io.DataInputStream; |
| import java.io.IOException; |
| import java.io.UnsupportedEncodingException; |
| |
| /** |
| * <p>This class contains data for a single Palm database for use during |
| * a conversion process.</p> |
| * |
| * <p>It contains zero or more <code>Record</code> objects stored in an |
| * array. The index of the <code>Record</code> object in the array is |
| * the record id or number for that specific <code>Record</code> object. |
| * Note that this class does not check for maximum number of records |
| * allowable in an actual pdb.</p> |
| * |
| * <p>This class also contains the pdb name associated with the Palm database |
| * it represents. A pdb name consists of 32 bytes of a certain encoding |
| * (extended ASCII in this case).</p> |
| * |
| * <p>The non default constructors take in a name parameter which may not |
| * be the exact pdb name to be used. The name parameter in |
| * <code>String</code> or <code>byte[]</code> are converted to an exact |
| * <code>NAME_LENGTH</code> byte array. If the length of the name is less |
| * than <code>NAME_LENGTH</code>, it is padded with '\0' characters. If it |
| * is more, it gets truncated. The last character in the resulting byte |
| * array is always a '\0' character. The resulting byte array is stored in |
| * <code>bName</code>, and a corresponding String object <code>sName</code> |
| * that contains characters without the '\0' characters.</p> |
| * |
| * <p>The {@link #write write} method is called within the |
| * {@link zensync.util.palm.PalmDBSet#write PalmDBSet.write} method |
| * for writing out its data to the <code>OutputStream</code> object.</p> |
| * |
| * <p>The {@link #read read} method is called within the |
| * {@link zensync.util.palm.PalmDBSet#read PalmDBSet.read} method |
| * for reading in its data from the <code>InputStream</code> object.</p> |
| * |
| * @author Akhil Arora, Herbie Ong |
| * @see PalmDBSet |
| * @see Record |
| */ |
| |
| public final class PalmDB { |
| |
| /** number of bytes for the name field in the pdb */ |
| public final static int NAME_LENGTH = 32; |
| |
| /** list of Record objects */ |
| private Record[] records; |
| |
| /** pdb name in bytes */ |
| private byte[] bName = null; |
| |
| /** pdb name in String */ |
| private String sName = null; |
| |
| |
| /** |
| * Default constructor for use after a read(). |
| */ |
| |
| public PalmDB() { |
| |
| records = new Record[0]; |
| } |
| |
| /** |
| * Constructor to create object with Record objects. |
| * recs.length can be zero for an empty pdb. |
| * |
| * @param name suggested pdb name in String |
| * @param recs array of Record objects |
| * @throws NullPointerException if recs is null |
| */ |
| |
| public PalmDB(String name, Record[] recs) |
| throws UnsupportedEncodingException { |
| |
| this(name.getBytes(PDBUtil.ENCODING), recs); |
| } |
| |
| /** |
| * Constructor to create object with Record objects. |
| * recs.length can be zero for an empty pdb. |
| * |
| * @param name suggested pdb name in byte array |
| * @param recs array of Record objects |
| * @throws NullPointerException if recs is null |
| */ |
| |
| public PalmDB(byte[] name, Record[] recs) |
| throws UnsupportedEncodingException { |
| |
| store(name); |
| |
| records = new Record[recs.length]; |
| System.arraycopy(recs, 0, records, 0, recs.length); |
| } |
| |
| /** |
| * This private method is mainly used by the constructors above. |
| * to store bytes into name and also create a String representation. |
| * and also by the read method. |
| * |
| * TODO: Note that this method assumes that the byte array parameter |
| * contains one character per byte, else it would truncate |
| * improperly. |
| * |
| * @param bytes pdb name in byte array |
| * @throws UnsupportedEncodingException if ENCODING is not supported |
| */ |
| |
| private void store(byte[] bytes) throws UnsupportedEncodingException { |
| |
| // note that this will initialize all bytes in name to 0. |
| bName = new byte[NAME_LENGTH]; |
| |
| // determine minimum length to copy over from bytes to bName. |
| // Note that the last byte in bName has to be '\0'. |
| |
| int lastIndex = NAME_LENGTH - 1; |
| |
| int len = (bytes.length < lastIndex)? bytes.length: lastIndex; |
| |
| int i; |
| |
| for (i = 0; i < len; i++) { |
| |
| if (bytes[i] == 0) { |
| break; |
| } |
| |
| bName[i] = bytes[i]; |
| } |
| |
| // set sName, no need to include the '\0' character. |
| sName = new String(bName, 0, i, PDBUtil.ENCODING); |
| } |
| |
| /** |
| * Return the number of records contained in this |
| * pdb PalmDB object. |
| * |
| * @return int number of Record objects |
| */ |
| |
| public int getRecordCount() { |
| |
| return records.length; |
| } |
| |
| /** |
| * Return the specific Record object associated |
| * with the record number. |
| * |
| * @param index record index number |
| * @return Record the Record object in the specified index |
| * @throws ArrayIndexOutOfBoundsException if index is out of bounds |
| */ |
| |
| public Record getRecord(int index) { |
| |
| return records[index]; |
| } |
| |
| /** |
| * Return the list of Record objects |
| * |
| * @return Record[] the list of Record objects |
| */ |
| |
| public Record[] getRecords() { |
| |
| return records; |
| } |
| |
| /** |
| * Return the PDBName associated with this object in String |
| * |
| * @return String pdb name in String |
| */ |
| |
| public String getPDBNameString() { |
| |
| return sName; |
| } |
| |
| /** |
| * Return the PDBName associated with this object |
| * in byte array of exact length of 32 bytes. |
| * |
| * @return byte[] pdb name in byte[] of length 32. |
| */ |
| |
| public byte[] getPDBNameBytes() { |
| |
| return bName; |
| } |
| |
| /** |
| * Write out the number of records followed by what |
| * will be written out by each Record object. |
| * |
| * @param os the stream to write the object to |
| * @throws IOException if any I/O error occurs |
| */ |
| |
| public void write(OutputStream os) throws IOException { |
| |
| DataOutputStream out = new DataOutputStream(os); |
| |
| // write out pdb name |
| out.write(bName); |
| |
| // write out 2 bytes for number of records |
| out.writeShort(records.length); |
| |
| // let each Record object write out its own info. |
| for (int i = 0; i < records.length; i++) |
| records[i].write(out); |
| } |
| |
| /** |
| * Read the necessary data to create a pdb from |
| * the input stream. |
| * |
| * @param is the stream to read data from in order |
| * to restore the object |
| * @throws IOException if any I/O error occurs |
| */ |
| |
| public void read(InputStream is) throws IOException { |
| |
| DataInputStream in = new DataInputStream(is); |
| |
| // read in the pdb name. |
| byte[] bytes = new byte[NAME_LENGTH]; |
| in.readFully(bytes); |
| store(bytes); |
| |
| // read in number of records |
| int nrec = in.readUnsignedShort(); |
| records = new Record[nrec]; |
| |
| // read in the Record infos |
| for (int i = 0; i < nrec; i++) { |
| |
| records[i] = new Record(); |
| records[i].read(in); |
| } |
| } |
| |
| /** |
| * Override equals method of Object. |
| * |
| * 2 PalmDB objects are equal if they contain the same information, |
| * i.e. pdb name and records. |
| * |
| * This is used primarily for testing purposes only for now. |
| * |
| * @param obj a PalmDB object to compare with |
| * @return boolean true if obj is equal to this, else false. |
| */ |
| |
| public boolean equals(Object obj) { |
| |
| boolean bool = false; |
| |
| if (obj instanceof PalmDB) { |
| |
| PalmDB pdb = (PalmDB) obj; |
| |
| checkLabel: { |
| |
| // compare sName |
| |
| if (!sName.equals(pdb.sName)) { |
| |
| break checkLabel; |
| } |
| |
| // compare bName |
| |
| if (bName.length != pdb.bName.length) { |
| |
| break checkLabel; |
| } |
| |
| for (int i = 0; i < bName.length; i++) { |
| |
| if (bName[i] != pdb.bName[i]) { |
| |
| break checkLabel; |
| } |
| } |
| |
| // compare each Record |
| |
| if (records.length != pdb.records.length) { |
| |
| break checkLabel; |
| } |
| |
| for (int i = 0; i < records.length; i++) { |
| |
| if (!records[i].equals(pdb.records[i])) { |
| |
| break checkLabel; |
| } |
| } |
| |
| // all checks done |
| bool = true; |
| } |
| } |
| |
| return bool; |
| } |
| } |