View Javadoc

1   /* ***** BEGIN LICENSE BLOCK *****
2    * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3    *
4    * The contents of this file are subject to the Mozilla Public License Version
5    * 1.1 (the "License"); you may not use this file except in compliance with
6    * the License. You may obtain a copy of the License at
7    * http://www.mozilla.org/MPL/
8    *
9    * Software distributed under the License is distributed on an "AS IS" basis,
10   * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11   * for the specific language governing rights and limitations under the
12   * License.
13   *
14   * The Original Code is "SMS Library for the Java platform".
15   *
16   * The Initial Developer of the Original Code is Markus Eriksson.
17   * Portions created by the Initial Developer are Copyright (C) 2002
18   * the Initial Developer. All Rights Reserved.
19   *
20   * Contributor(s):
21   *
22   * Alternatively, the contents of this file may be used under the terms of
23   * either the GNU General Public License Version 2 or later (the "GPL"), or
24   * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
25   * in which case the provisions of the GPL or the LGPL are applicable instead
26   * of those above. If you wish to allow use of your version of this file only
27   * under the terms of either the GPL or the LGPL, and not to allow others to
28   * use your version of this file under the terms of the MPL, indicate your
29   * decision by deleting the provisions above and replace them with the notice
30   * and other provisions required by the GPL or the LGPL. If you do not delete
31   * the provisions above, a recipient may use your version of this file under
32   * the terms of any one of the MPL, the GPL or the LGPL.
33   *
34   * ***** END LICENSE BLOCK ***** */
35  package org.marre.wap.wbxml;
36  
37  import java.io.ByteArrayOutputStream;
38  import java.io.IOException;
39  import java.io.OutputStream;
40  import java.util.HashMap;
41  import java.util.Map;
42  
43  import org.marre.util.StringUtil;
44  import org.marre.wap.WapConstants;
45  import org.marre.wap.WspUtil;
46  import org.marre.xml.XmlAttribute;
47  import org.marre.xml.XmlWriter;
48  
49  public class WbxmlWriter implements XmlWriter
50  {
51      private Map myStringTable;
52      private ByteArrayOutputStream myStringTableBuf;
53  
54      private OutputStream myOs;
55      private ByteArrayOutputStream myWbxmlBody;
56  
57      private String[] myTagTokens;
58      private String[] myAttrStartTokens;
59      private String[] myAttrValueTokens;
60  
61      private String myPublicID;
62      
63      public WbxmlWriter(OutputStream os, String[] theTagTokens, String[] theAttrStrartTokens, String[] theAttrValueTokens)
64      {
65          myWbxmlBody = new ByteArrayOutputStream();
66          myStringTableBuf = new ByteArrayOutputStream();
67          myStringTable = new HashMap();
68          myOs = os;
69  
70          setTagTokens(theTagTokens);
71          setAttrStartTokens(theAttrStrartTokens);
72          setAttrValueTokens(theAttrValueTokens);
73      }
74  
75      public WbxmlWriter(OutputStream os)
76      {
77          this(os, null, null, null);
78      }
79      
80      /***
81       * Writes the wbxml to stream.
82       * 
83       * @throws IOException
84       */
85      public void flush() throws IOException
86      {
87          // WBXML v 0.1
88          WspUtil.writeUint8(myOs, 0x01);
89          // Public ID
90          writePublicIdentifier(myOs, myPublicID);
91          // Charset - "UTF-8"
92          WspUtil.writeUintvar(myOs, WapConstants.MIB_ENUM_UTF_8);
93          // String table
94          writeStringTable(myOs);
95  
96          // Write body
97          myWbxmlBody.close();
98          myWbxmlBody.writeTo(myOs);
99  
100         myOs.flush();
101     }
102 
103     /////// XmlWriter
104 
105     public void setDoctype(String name, String systemURI)
106     {
107         myPublicID = null; //Liquidterm: Defaults to unknown
108     }
109 
110     public void setDoctype(String name, String publicID, String publicURI)
111     {
112         myPublicID = publicID;
113     }
114 
115     public void setDoctype(String publicID)
116     {
117         myPublicID = publicID;
118     }
119 
120     public void addStartElement(String tag) throws IOException
121     {
122         int tagIndex = StringUtil.findString(myTagTokens, tag);
123         if (tagIndex >= 0)
124         {
125             // Known tag
126             tagIndex += 0x05; // Tag token table starts at #5
127             myWbxmlBody.write(WbxmlConstants.TOKEN_KNOWN_C | tagIndex);
128         }
129         else
130         {
131             // Unknown. Add as literal
132             myWbxmlBody.write(WbxmlConstants.TOKEN_LITERAL_C);
133             writeStrT(myWbxmlBody, tag);
134         }
135     }
136 
137     public void addStartElement(String tag, XmlAttribute[] attribs) throws IOException
138     {
139         int tagIndex = StringUtil.findString(myTagTokens, tag);
140         if (tagIndex >= 0)
141         {
142             // Known tag
143             tagIndex += 0x05; // Tag token table starts at #5
144             myWbxmlBody.write(WbxmlConstants.TOKEN_KNOWN_AC | tagIndex);
145         }
146         else if (tag != null)
147         {
148             // Unknown. Add as literal (Liquidterm: only if not null)
149             myWbxmlBody.write(WbxmlConstants.TOKEN_LITERAL_AC);
150             writeStrT(myWbxmlBody, tag);
151         }
152 
153         // Write attributes
154         writeAttributes(myWbxmlBody, attribs);
155     }
156 
157     public void addEmptyElement(String tag) throws IOException
158     {
159         int tagIndex = StringUtil.findString(myTagTokens, tag);
160         if (tagIndex >= 0)
161         {
162             // Known tag
163             tagIndex += 0x05; // Tag token table starts at #5
164             myWbxmlBody.write(WbxmlConstants.TOKEN_KNOWN | tagIndex);
165         }
166         else if (tag != null)
167         {
168             // Unknown. Add as literal (Liquidterm: if not null)
169             myWbxmlBody.write(WbxmlConstants.TOKEN_LITERAL);
170             writeStrT(myWbxmlBody, tag);
171         }
172     }
173 
174     public void addEmptyElement(String tag, XmlAttribute[] attribs) throws IOException
175     {
176         int tagIndex = StringUtil.findString(myTagTokens, tag);
177 
178         if (tagIndex >= 0)
179         {
180             // Known tag
181             tagIndex += 0x05; // Tag token table starts at #5
182             myWbxmlBody.write(WbxmlConstants.TOKEN_KNOWN_A | tagIndex);
183         }
184         else
185         {
186             // Unknown. Add as literal
187             myWbxmlBody.write(WbxmlConstants.TOKEN_LITERAL_A);
188             writeStrT(myWbxmlBody, tag);
189         }
190 
191         // Add attributes
192         writeAttributes(myWbxmlBody, attribs);
193     }
194 
195     public void addEndElement() throws IOException
196     {
197         myWbxmlBody.write(WbxmlConstants.TOKEN_END);
198     }
199 
200     public void addCharacters(char[] ch, int start, int length) throws IOException
201     {
202         addCharacters(new String(ch, start, length));
203     }
204 
205     public void addCharacters(String str) throws IOException
206     {
207         myWbxmlBody.write(WbxmlConstants.TOKEN_STR_I);
208         writeStrI(myWbxmlBody, str);
209     }
210 
211     // WBXML specific stuff
212 
213     public void addOpaqueData(byte[] buff) throws IOException
214     {
215         addOpaqueData(buff, 0, buff.length);
216     }
217 
218     public void addOpaqueData(byte[] buff, int off, int len) throws IOException
219     {
220         myWbxmlBody.write(WbxmlConstants.TOKEN_OPAQ);
221         WspUtil.writeUintvar(myWbxmlBody, buff.length);
222         myWbxmlBody.write(buff, off, len);
223     }
224 
225     /***
226      * Sets the tag tokens.
227      * 
228      * @param theTagTokens
229      *            first element in this array defines tag #5
230      */
231     public void setTagTokens(String[] theTagTokens)
232     {
233         if (theTagTokens != null)
234         {
235             myTagTokens = new String[theTagTokens.length];
236             System.arraycopy(theTagTokens, 0, myTagTokens, 0, theTagTokens.length);
237         }
238         else
239         {
240             myTagTokens = null;
241         }
242     }
243 
244     /***
245      * Sets the attribute start tokens.
246      * 
247      * @param theAttrStrartTokens
248      *            first element in this array defines attribute #85
249      */
250     public void setAttrStartTokens(String[] theAttrStrartTokens)
251     {
252         if (theAttrStrartTokens != null)
253         {
254             myAttrStartTokens = new String[theAttrStrartTokens.length];
255             System.arraycopy(theAttrStrartTokens, 0, myAttrStartTokens, 0, theAttrStrartTokens.length);
256         }
257         else
258         {
259             myAttrStartTokens = null;
260         }
261     }
262 
263     /***
264      * Sets the attribute value tokens.
265      * 
266      * @param theAttrValueTokens
267      *            first element in this array defines attribute #05
268      */
269     public void setAttrValueTokens(String[] theAttrValueTokens)
270     {
271         if (theAttrValueTokens != null)
272         {
273             myAttrValueTokens = new String[theAttrValueTokens.length];
274             System.arraycopy(theAttrValueTokens, 0, myAttrValueTokens, 0, theAttrValueTokens.length);
275         }
276         else
277         {
278             myAttrValueTokens = null;
279         }
280     }
281 
282     /////////////////////////////////////////////////////////
283 
284     private void writePublicIdentifier(OutputStream os, String publicID) throws IOException
285     {
286         if (publicID == null)
287         {
288             // "Unknown or missing public identifier."
289             WspUtil.writeUintvar(os, 0x01);
290         }
291         else
292         {
293             int idx = StringUtil.findString(WbxmlConstants.KNOWN_PUBLIC_DOCTYPES, publicID);
294             if (idx != -1)
295             {
296                 // Known ID
297                 idx += 2; // Skip 0 and 1
298                 WspUtil.writeUintvar(os, idx);
299             }
300             else
301             {
302                 // Unknown ID, add string
303                 WspUtil.writeUintvar(os, 0x00); // String reference following
304                 writeStrT(os, publicID);
305             }
306         }
307     }
308 
309     private void writeStrI(OutputStream theOs, String str) throws IOException
310     {
311         //Liquidterm: protection against null values
312         if (str != null)
313         {
314             theOs.write(str.getBytes("UTF-8"));
315             theOs.write(0x00);
316         }
317     }
318 
319     private void writeStrT(OutputStream theOs, String str) throws IOException
320     {
321         Integer index = (Integer) myStringTable.get(str);
322 
323         if (index == null)
324         {
325             index = new Integer(myStringTableBuf.size());
326             myStringTable.put(str, index);
327             writeStrI(myStringTableBuf, str);
328         }
329 
330         WspUtil.writeUintvar(theOs, index.intValue());
331     }
332 
333     private void writeStringTable(OutputStream theOs) throws IOException
334     {
335         // Write length of string table
336         WspUtil.writeUintvar(theOs, myStringTableBuf.size());
337         // Write string table
338         myStringTableBuf.writeTo(theOs);
339     }
340 
341     // FIXME: Unsure how to do this stuff with the attributes
342     // more efficient...
343     private void writeAttributes(OutputStream os, XmlAttribute[] attribs) throws IOException
344     {
345         int idx;
346 
347         for (int i = 0; i < attribs.length; i++)
348         {
349             // TYPE=VALUE
350             String typeValue = attribs[i].getType() + "=" + attribs[i].getValue();
351             idx = StringUtil.findString(myAttrStartTokens, typeValue);
352             if (idx >= 0)
353             {
354                 // Found a matching type-value pair
355                 idx += 0x05; // Attr start token table starts at #5
356                 myWbxmlBody.write(idx);
357             }
358             else
359             {
360                 // Try with separate type and values
361                 
362                 // TYPE
363                 idx = StringUtil.findString(myAttrStartTokens, attribs[i].getType());
364                 if (idx >= 0)
365                 {
366                     idx += 0x05; // Attr start token table starts at #5
367                     myWbxmlBody.write(idx);
368                 }
369                 else
370                 {
371                     myWbxmlBody.write(WbxmlConstants.TOKEN_LITERAL);
372                     writeStrT(myWbxmlBody, attribs[i].getType());
373                 }
374 
375                 // VALUE
376                 String attrValue = attribs[i].getValue();
377                 if (attrValue != null && (!attrValue.equals("")))
378                 {
379                     idx = StringUtil.findString(myAttrValueTokens, attrValue);
380                     if (idx >= 0)
381                     {
382                         idx += 0x85; // Attr value token table starts at 85
383                         myWbxmlBody.write(idx);
384                     }
385                     else
386                     {
387                         myWbxmlBody.write(WbxmlConstants.TOKEN_STR_I);
388                         writeStrI(myWbxmlBody, attrValue);
389                     }
390                 }
391             }            
392         }
393 
394         // End of attributes
395         myWbxmlBody.write(WbxmlConstants.TOKEN_END);
396     }
397 
398     /*
399      * public static void main(String argv[]) throws Exception { XmlWriter
400      * handler = new WbxmlWriter();
401      * 
402      * handler.addStartElement("element", new XmlAttribute[] { new
403      * XmlAttribute("type1", "value1") }); handler.addCharacters("Some text");
404      * handler.addEmptyElement("empty", new XmlAttribute[] { new
405      * XmlAttribute("type2", "value2") }); handler.addStartElement("element",
406      * new XmlAttribute[] { new XmlAttribute("type3", "value3"), new
407      * XmlAttribute("type4", "value4") }); handler.addEmptyElement("empty", new
408      * XmlAttribute[] { new XmlAttribute("type2", "value2") });
409      * handler.addEmptyElement("empty", new XmlAttribute[] { new
410      * XmlAttribute("type2", "value2") }); handler.addEndTag();
411      * handler.addEndTag();
412      * 
413      * handler.writeTo(new FileOutputStream("demo.wbxml")); }
414      */
415 }