/**************************************************************
*
* 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;
/**
*
This class contains data for a single Palm database for use during
* a conversion process.
*
* It contains zero or more Record
objects stored in an
* array. The index of the Record
object in the array is
* the record id or number for that specific Record
object.
* Note that this class does not check for maximum number of records
* allowable in an actual pdb.
*
* 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).
*
* The non default constructors take in a name parameter which may not
* be the exact pdb name to be used. The name parameter in
* String
or byte[]
are converted to an exact
* NAME_LENGTH
byte array. If the length of the name is less
* than NAME_LENGTH
, 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
* bName
, and a corresponding String object sName
* that contains characters without the '\0' characters.
*
* 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 OutputStream
object.
*
* 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 InputStream
object.
*
* @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;
}
}