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.xml.sxw.wordsmith;
25 
26 import java.io.ByteArrayOutputStream;
27 import java.io.DataOutputStream;
28 import java.io.*;
29 import java.io.UnsupportedEncodingException;
30 import java.util.List;
31 import java.util.ArrayList;
32 import java.util.Vector;
33 
34 import org.openoffice.xmerge.converter.palm.*;
35 
36 /**
37  *  This class is used by {@link
38  *  org.openoffice.xmerge.converter.xml.sxw.wordsmith.DocumentDeserializerImpl
39  *  DocumentDeserializerImpl} to encode the WordSmith format.
40  *
41  *  @author   David Proulx
42  */
43 
44 // DJP: replace 4096 w/ a defined constant
45 
46 final class WSEncoder {
47 
48     /* DJP: These should probably go somewhere else! */
49     /** Constant for uncompressed version. */
50     public static final short UNCOMPRESSED = 1;
51 
52     /** Constant for compressed version. */
53     public static final short COMPRESSED = 2;
54 
55     /** Constant used for spare fields. */
56     public static final int SPARE = 0;
57 
58     /* WordSmith Header information. */
59     private short version;
60     private int textLen;
61     private short maxRecSize;
62     private int textRecCount = 0;
63 
64 
65     /* WordSmith document elements. */
66     WseHeader header = null;
67     WseFontTable ft = null;
68     WseColorTable ct = null;
69     private Vector elements;  // paragraphs & text runs
70 
71     /* Totals for the WordSmith document. */
72     int nrParagraphs = 0;
73     int nrAtoms = 0;
74     int nrChars = 0;
75 
76 
77     /**
78      *  Default constructor creates a header and
79      *  a text buffer for holding all the text in
80      *  the WordSmith database.
81      */
WSEncoder()82     WSEncoder() {
83         version = 1;
84         textLen = 0;
85         maxRecSize = 4096;
86         elements = new Vector();
87     }
88 
89 
90     /**
91      *  This method adds a new element to the WordSmith document.
92      *
93      *  @param  elem  WordSmith document element to add
94      */
addElement(Wse elem)95     void addElement(Wse elem) {
96         if (elem.getClass() == WseHeader.class)
97             header = (WseHeader)elem;
98         else if (elem.getClass() == WseFontTable.class)
99             ft = (WseFontTable)elem;
100         else if (elem.getClass() == WseColorTable.class)
101             ct = (WseColorTable)elem;
102         else
103             elements.addElement(elem);
104     }
105 
106 
107     /**
108      *  This method encodes the information given to
109      *  an array of palm Records in the WordSmith database format.
110      *
111      *  @return  <code>Record</code> array holding WordSmith contents.
112      *
113      *  @throws  IOException  If any I/O error occurs.
114      */
getRecords()115     Record[] getRecords() throws IOException {
116 
117         Vector allRecs = new Vector();
118         int nElements = elements.size();
119 
120         // Count up the number of paragraphs, atoms, and characters.
121         int currElement = 0;
122         while (currElement < nElements) {
123             Wse e = (Wse)elements.elementAt(currElement++);
124             if (e.getClass() == WsePara.class)
125                 nrParagraphs++;
126             if (e.getClass() == WseTextRun.class) {
127                 nrAtoms++;
128                 nrChars += ((WseTextRun)e).getText().length();
129             }
130         }
131 
132         byte[] currRec = new byte[4096];
133         int currRecLen = 0;
134 
135         // This code assumes that the WordSmith header, font table,
136         // and color table total less than 4096 bytes.
137         header = new WseHeader(nrParagraphs, nrAtoms, nrChars, ft, ct);
138         System.arraycopy(header.getBytes(), 0,
139                          currRec, currRecLen, header.getByteCount());
140         currRecLen += header.getByteCount();
141 
142         if (ft != null) {
143             System.arraycopy(ft.getBytes(), 0, currRec, currRecLen,
144                              ft.getByteCount());
145             currRecLen += ft.getByteCount();
146         }
147         if (ct != null) {
148             System.arraycopy(ct.getBytes(), 0, currRec, currRecLen,
149                              ct.getByteCount());
150             currRecLen += ct.getByteCount();
151         }
152 
153         currElement = 0;
154         while (currElement < nElements) {
155             Wse e = (Wse)elements.elementAt(currElement++);
156             int length = e.getByteCount();
157             if ((length + currRecLen) <= 4096) {
158                 System.arraycopy(e.getBytes(), 0, currRec, currRecLen, length);
159                 currRecLen += length;
160         } else {
161                 // Copy in enough to get to full size, then create a
162                 // new Record and add it to the Vector.
163                 int firstPartLen = 4096 - currRecLen;
164                 System.arraycopy(e.getBytes(), 0, currRec, currRecLen,
165                                  firstPartLen);
166                 Record r = new Record(currRec);
167                 allRecs.addElement(r);
168 
169                 // Put the remainder at the beginning of the next record
170                 currRecLen = 0;
171                 System.arraycopy(e.getBytes(), firstPartLen, currRec,
172                                  currRecLen, length - firstPartLen);
173                 currRecLen += length - firstPartLen;
174             }
175         }
176 
177         // Processed all the elements.  Write out any remaining partial record.
178         if (currRecLen > 0) {
179             byte[] partial = new byte[currRecLen];
180             System.arraycopy(currRec, 0, partial, 0, currRecLen);
181             Record rr = new Record(partial);
182             allRecs.addElement(rr);
183         }
184 
185 
186         // Record 0 is the WordSmith header.  Do it last since it
187         // contains totals for the entire document.  It goes
188         // before everything else.
189         ByteArrayOutputStream bos = new ByteArrayOutputStream();
190         DataOutputStream dos = new DataOutputStream(bos);
191         dos.writeShort(version);
192         dos.writeShort(0);
193         dos.writeInt(textLen);
194         dos.writeShort(allRecs.size());
195         dos.writeShort(maxRecSize);
196         dos.writeInt(0);
197         allRecs.insertElementAt(new Record(bos.toByteArray()), 0);
198 
199         // Convert Vector of Records to an array and return it.
200         int nRecs = allRecs.size();
201         Record recs[] = new Record[nRecs];
202         for (int i = 0; i < nRecs; i++)
203         recs[i] = (Record)allRecs.elementAt(i);
204         return recs;
205     }
206 
207 }
208 
209