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