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.BufferedOutputStream; 28 import java.io.DataOutputStream; 29 import java.io.IOException; 30 import java.util.Date; 31 32 /** 33 * <p>Provides functionality to encode a <code>PalmDB</code> object 34 * into a PDB formatted file given a file <code>OutputStream</code>. 35 * This class is only used by the <code>PalmDB</code> object.</p> 36 * 37 * <p>One needs to create one <code>PdbEncoder</code> object per 38 * <code>PalmDB</code> object to be encoded. This class keeps 39 * the PDB header data and functionality in the <code>PdbHeader</code> 40 * class.</p> 41 * 42 * <p>Sample usage:</p> 43 * 44 * <blockquote><pre><code> 45 * PdbEncoder encoder = new PdbEncoder(palmDB, "STRW", "data"); 46 * encoder.write(new FileOutputStream("sample.pdb")); 47 * </code></pre></blockquote> 48 * 49 * @author Herbie Ong 50 * @see PalmDB 51 * @see Record 52 */ 53 public final class PdbEncoder { 54 55 /** PDB header. */ 56 private PdbHeader header = null; 57 58 /** the PalmDB object. */ 59 private PalmDB db = null; 60 61 /** 62 * The pattern for unique_id=0x00BABE(start). 63 */ 64 private final static int START_UNIQUE_ID = 0x00BABE; 65 66 67 /** 68 * Constructor. 69 * 70 * @param db The <code>PalmDB</code> to be encoded. 71 */ PdbEncoder(PalmDB db)72 public PdbEncoder(PalmDB db) { 73 74 header = new PdbHeader(); 75 header.version = db.getVersion(); 76 77 header.attribute = db.getAttribute(); 78 79 this.db = db; 80 81 header.pdbName = db.getPDBNameBytes(); 82 header.creatorID = db.getCreatorID(); 83 header.typeID = db.getTypeID(); 84 85 // set the following dates to current date 86 Date date = new Date(); 87 header.creationDate = (date.getTime() / 1000) + PdbUtil.TIME_DIFF; 88 header.modificationDate = header.creationDate; 89 90 header.numRecords = db.getRecordCount(); 91 } 92 93 94 /** 95 * <p>Write out a PDB into the given <code>OutputStream</code>.</p> 96 * 97 * <p>First, write out the header data by using the 98 * <code>PdbHeader</code> <code>write</code> method. Next, 99 * calculate the RecordList section and write it out. 100 * Lastly, write out the bytes corresponding to each 101 * <code>Record</code>.</p> 102 * 103 * <p>The RecordList section contains a list of 104 * <code>Record</code> index info, where each <code>Record</code> 105 * index info contains:</p> 106 * 107 * <p><ul> 108 * <li>4 bytes local offset of the <code>Record</code> from the 109 * top of the PDB.</li> 110 * <li>1 byte of <code>Record</code> attribute.</li> 111 * <li>3 bytes unique <code>Record</code> ID.</li> 112 * </ul> 113 * 114 * <p>There should be a total of <code>header.numRecords</code> 115 * of <code>Record</code> index info</p>. 116 * 117 * @param os <code>OutputStream</code> to write out PDB. 118 * 119 * @throws IOException If I/O error occurs. 120 */ write(OutputStream os)121 public void write(OutputStream os) throws IOException { 122 123 BufferedOutputStream bos = new BufferedOutputStream(os); 124 DataOutputStream dos = new DataOutputStream(bos); 125 126 // write out the PDB header 127 header.write(dos); 128 129 if (header.numRecords > 0) { 130 131 // compute for recOffset[] 132 133 int recOffset[] = new int[header.numRecords]; 134 byte recAttr[] = new byte[header.numRecords]; 135 136 // first recOffset will be at PdbUtil.HEADER_SIZE + all the 137 // record indices (@ 8 bytes each) 138 recOffset[0] = PdbUtil.HEADER_SIZE + (header.numRecords * 8); 139 140 int lastIndex = header.numRecords - 1; 141 142 for (int i = 0; i < lastIndex; i++) { 143 144 Record rec = db.getRecord(i); 145 int size = rec.getSize(); 146 recAttr[i] = rec.getAttributes(); 147 148 recOffset[i+1] = recOffset[i] + size; 149 } 150 151 // grab the last record's attribute. 152 153 Record lastRec = db.getRecord(lastIndex); 154 recAttr[lastIndex] = lastRec.getAttributes(); 155 156 157 int uid = START_UNIQUE_ID; 158 159 for (int i = 0; i < header.numRecords; i++) { 160 161 // write out each record offset 162 dos.writeInt(recOffset[i]); 163 164 // write out record attribute (recAttr) and 165 // unique ID (uid) in 4 bytes (int) chunk. 166 // unique ID's have to be unique, thus 167 // increment each time. 168 int attr = (((int) recAttr[i]) << 24 ); 169 attr |= uid; 170 dos.writeInt(attr); 171 uid++; 172 } 173 174 // write out the raw records 175 176 for (int i = 0; i < header.numRecords; i++) { 177 178 Record rec = db.getRecord(i); 179 byte bytes[] = rec.getBytes(); 180 dos.write(bytes); 181 } 182 183 } else { 184 185 // placeholder bytes if there are no records in the list. 186 dos.writeShort(0); 187 } 188 189 dos.flush(); 190 } 191 } 192 193