// Copyright FreeHEP, 2007. package org.freehep.wbxml; import java.awt.Color; import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStream; /** * Writes out Binary XML see http://www.wapforum.org instead of ASCII XML. * * @author Mark Donszelmann * @version $Id$ */ public class WBXMLWriter implements WBXMLTagWriter { // outputstream variables private DataOutputStream os; private String dtd; private String version; private boolean writtenHeader; // Code pages private int tagPage; private int attributePage; // tag attributes private boolean hasAttributes; private int[] attributeTypes; // cache private boolean[][] attributeBoolean; private int[] attributeBooleanOffset; private int[] attributeBooleanLength; private byte[][] attributeByte; private int[] attributeByteOffset; private int[] attributeByteLength; private char[][] attributeChar; private int[] attributeCharOffset; private int[] attributeCharLength; private double[][] attributeDouble; private int[] attributeDoubleOffset; private int[] attributeDoubleLength; private float[][] attributeFloat; private int[] attributeFloatOffset; private int[] attributeFloatLength; private int[][] attributeInt; private int[] attributeIntOffset; private int[] attributeIntLength; private long[][] attributeLong; private int[] attributeLongOffset; private int[] attributeLongLength; private short[][] attributeShort; private int[] attributeShortOffset; private int[] attributeShortLength; private String[][] attributeString; private int[] attributeStringOffset; private int[] attributeStringLength; private String encoding; private boolean standalone; /** * Create a Binary AIDA Writer for given stream * * @param os * stream to write to * @param maxAttributeCode largest tagcode for attributes */ public WBXMLWriter(OutputStream os, int maxAttributeTagCode) throws IOException { this.os = (os instanceof DataOutputStream) ? (DataOutputStream) os : new DataOutputStream(os); tagPage = 0; attributePage = 0; int len = maxAttributeTagCode+1; attributeBoolean = new boolean[len][]; attributeBooleanOffset = new int[len]; attributeBooleanLength = new int[len]; attributeByte = new byte[len][]; attributeByteOffset = new int[len]; attributeByteLength = new int[len]; attributeChar = new char[len][]; attributeCharOffset = new int[len]; attributeCharLength = new int[len]; attributeDouble = new double[len][]; attributeDoubleOffset = new int[len]; attributeDoubleLength = new int[len]; attributeFloat = new float[len][]; attributeFloatOffset = new int[len]; attributeFloatLength = new int[len]; attributeInt = new int[len][]; attributeIntOffset = new int[len]; attributeIntLength = new int[len]; attributeLong = new long[len][]; attributeLongOffset = new int[len]; attributeLongLength = new int[len]; attributeShort = new short[len][]; attributeShortOffset = new int[len]; attributeShortLength = new int[len]; attributeString = new String[len][]; attributeStringOffset = new int[len]; attributeStringLength = new int[len]; attributeTypes = new int[len]; clearAttributes(); } private void clearAttributes() { hasAttributes = false; for (int i = 0; i < attributeTypes.length; i++) { attributeTypes[i] = -1; } } public void close() throws IOException { os.close(); } public void openDoc() throws IOException { openDoc("Binary/1.0", "UTF-8", false); } public void openDoc(String version, String encoding, boolean standalone) throws IOException { dtd = null; writtenHeader = false; this.version = version; this.encoding = encoding; this.standalone = standalone; } private void writeHeader() throws IOException { if (writtenHeader) return; if (dtd == null) throw new IOException("DTD is missing"); // header writeByte(WBXML.WBXML_VERSION); writeByte(WBXML.INDEXED_PID); writeMultiByteInt(0); // dtd has to be first entry in table. writeMultiByteInt(WBXML.UTF8); // length string table: add (short) length and (byte) null. int len = WBXMLParser.stringUTFLength(dtd) + 2 + 1; len += WBXMLParser.stringUTFLength(version) + 2 + 1; writeMultiByteInt(len); // Binary AIDA Header (as part of the string table) writeString(dtd); writeString(version); writtenHeader = true; } public void closeDoc() throws IOException { // ignored } public void referToDTD(String name, String pid, String ref) { // ignored } public void referToDTD(String name, String system) { dtd = name+" "+system; } public void openTag(int tag) throws IOException { writeTag(tag, true); } public void closeTag() throws IOException { writeByte(WBXML.END); } public void printTag(int tag) throws IOException { writeTag(tag, false); } public void print(String text) throws IOException { writeByte(WBXML.STR_I); writeString(text); } public void printComment(String comment) throws IOException { // ignored } private void writeTag(int tag, boolean hasContent) throws IOException { if (!writtenHeader) writeHeader(); // write tag int page = tag / WBXML.MAX_CODES; if (page != tagPage) { writeByte(WBXML.SWITCH_PAGE); writeByte(page); tagPage = page; } tag = tag % WBXML.MAX_CODES; writeByte((tag + WBXML.RESERVED_CODES) | (hasContent ? WBXML.CONTENT : 0x00) | (hasAttributes ? WBXML.ATTRIBUTE : 0x00)); // write attributes if (hasAttributes) { // write attributes for (int i = 0; i < attributeTypes.length; i++) { if (attributeTypes[i] >= 0) { // write ATTRSTART writeAttribute(i); switch (attributeTypes[i]) { case Attributes.BOOLEAN: // write OPAQUE writeByte(WBXML.OPAQUE); writeMultiByteInt(attributeBooleanLength[i]*1+1); writeByte(Attributes.BOOLEAN); for (int j = 0; j < attributeBooleanLength[i]; j++) { writeByte(attributeBoolean[i][j+attributeBooleanOffset[i]] ? 1 : 0); } break; case Attributes.BYTE: // write OPAQUE writeByte(WBXML.OPAQUE); writeMultiByteInt(attributeByteLength[i]*1+1); writeByte(Attributes.BYTE); for (int j = 0; j < attributeByteLength[i]; j++) { writeByte(attributeByte[i][j+attributeByteOffset[i]]); } break; case Attributes.CHAR: // write OPAQUE writeByte(WBXML.OPAQUE); writeMultiByteInt(attributeCharLength[i]*2+1); writeByte(Attributes.CHAR); for (int j = 0; j < attributeCharLength[i]; j++) { os.writeChar(attributeChar[i][j+attributeCharOffset[i]]); } break; case Attributes.DOUBLE: // write OPAQUE writeByte(WBXML.OPAQUE); writeMultiByteInt(attributeDoubleLength[i]*8+1); writeByte(Attributes.DOUBLE); for (int j = 0; j < attributeDoubleLength[i]; j++) { os.writeDouble(attributeDouble[i][j+attributeDoubleOffset[i]]); } break; case Attributes.FLOAT: // write OPAQUE writeByte(WBXML.OPAQUE); writeMultiByteInt(attributeFloatLength[i]*4+1); writeByte(Attributes.FLOAT); for (int j = 0; j < attributeFloatLength[i]; j++) { os.writeFloat(attributeFloat[i][j+attributeFloatOffset[i]]); } break; case Attributes.INT: // write OPAQUE writeByte(WBXML.OPAQUE); writeMultiByteInt(attributeIntLength[i]*4+1); writeByte(Attributes.INT); for (int j = 0; j < attributeIntLength[i]; j++) { os.writeInt(attributeInt[i][j+attributeIntOffset[i]]); } break; case Attributes.LONG: // write OPAQUE writeByte(WBXML.OPAQUE); writeMultiByteInt(attributeLongLength[i]*8+1); writeByte(Attributes.LONG); for (int j = 0; j < attributeLongLength[i]; j++) { os.writeLong(attributeLong[i][j+attributeLongOffset[i]]); } break; case Attributes.SHORT: // write OPAQUE writeByte(WBXML.OPAQUE); writeMultiByteInt(attributeShortLength[i]*2+1); writeByte(Attributes.SHORT); for (int j = 0; j < attributeShortLength[i]; j++) { os.writeShort(attributeShort[i][j+attributeShortOffset[i]]); } break; case Attributes.STRING: // calculate total length int length = 0; for (int j = 0; j < attributeStringLength[i]; j++) { // add (short) length and (byte) null termination length += WBXMLParser.stringUTFLength((String)attributeString[i][j+attributeStringOffset[i]]) + 2 + 1; } // write OPAQUE writeByte(WBXML.OPAQUE); writeMultiByteInt(length+1); writeByte(Attributes.STRING); // write UTF strings for (int j = 0; j < attributeStringLength[i]; j++) { writeString(attributeString[i][j+attributeStringOffset[i]]); } break; } } } // end of attributes writeByte(WBXML.END); clearAttributes(); } } private void writeAttribute(int tag) throws IOException { int page = tag / WBXML.MAX_CODES; tag = tag % WBXML.MAX_CODES; if (page != attributePage) { writeByte(WBXML.SWITCH_PAGE); writeByte(page); attributePage = page; } writeByte(tag + WBXML.RESERVED_CODES); } public void setAttribute(int tag, String value) { setAttribute(tag, new String[] { value }, 0, 1); } public void setAttribute(int tag, String[] value, int offset, int length) { hasAttributes = true; attributeString[tag] = value; attributeStringOffset[tag] = offset; attributeStringLength[tag] = length; attributeTypes[tag] = Attributes.STRING; } public void setAttribute(int tag, byte value) { setAttribute(tag, new byte[] { value }, 0, 1); } public void setAttribute(int tag, byte[] value, int offset, int length) { hasAttributes = true; attributeByte[tag] = value; attributeByteOffset[tag] = offset; attributeByteLength[tag] = length; attributeTypes[tag] = Attributes.BYTE; } public void setAttribute(int tag, long value) { setAttribute(tag, new long[] { value }, 0, 1); } public void setAttribute(int tag, long[] value, int offset, int length) { hasAttributes = true; attributeLong[tag] = value; attributeLongOffset[tag] = offset; attributeLongLength[tag] = length; attributeTypes[tag] = Attributes.LONG; } public void setAttribute(int tag, int value) { setAttribute(tag, new int[] { value }, 0, 1); } public void setAttribute(int tag, int[] value, int offset, int length) { hasAttributes = true; attributeInt[tag] = value; attributeIntOffset[tag] = offset; attributeIntLength[tag] = length; attributeTypes[tag] = Attributes.INT; } public void setAttribute(int tag, boolean value) { setAttribute(tag, new boolean[] { value }, 0, 1); } public void setAttribute(int tag, boolean[] value, int offset, int length) { hasAttributes = true; attributeBoolean[tag] = value; attributeBooleanOffset[tag] = offset; attributeBooleanLength[tag] = length; attributeTypes[tag] = Attributes.BOOLEAN; } public void setAttribute(int tag, float value) { setAttribute(tag, new float[] { value }, 0, 1); } public void setAttribute(int tag, float[] value, int offset, int length) { hasAttributes = true; attributeFloat[tag] = value; attributeFloatOffset[tag] = offset; attributeFloatLength[tag] = length; attributeTypes[tag] = Attributes.FLOAT; } public void setAttribute(int tag, double value) { setAttribute(tag, new double[] { value }, 0, 1); } public void setAttribute(int tag, double[] value, int offset, int length) { hasAttributes = true; attributeDouble[tag] = value; attributeDoubleOffset[tag] = offset; attributeDoubleLength[tag] = length; attributeTypes[tag] = Attributes.DOUBLE; } public void setAttribute(int tag, Color value) { setAttribute(tag, new Color[] { value }, 0, 1); } public void setAttribute(int tag, Color[] value, int offset, int length) { // ignored } public void setAttribute(int tag, char value) { setAttribute(tag, new char[] { value }, 0, 1); } public void setAttribute(int tag, char[] value, int offset, int length) { hasAttributes = true; attributeChar[tag] = value; attributeCharOffset[tag] = offset; attributeCharLength[tag] = length; attributeTypes[tag] = Attributes.CHAR; } public void setAttribute(int tag, short value) { setAttribute(tag, new short[] { value }, 0, 1); } public void setAttribute(int tag, short[] value, int offset, int length) { hasAttributes = true; attributeShort[tag] = value; attributeShortOffset[tag] = offset; attributeShortLength[tag] = length; attributeTypes[tag] = Attributes.SHORT; } private void writeMultiByteInt(long ui) throws IOException { int buf[] = new int[5]; int idx = 0; do { buf[idx++] = (int) (ui & 0x7f); ui = ui >> 7; } while (ui != 0); while (idx > 1) { writeByte(buf[--idx] | 0x80); } writeByte(buf[0]); } private void writeByte(int b) throws IOException { os.writeByte(b); } private void writeString(String s) throws IOException { os.writeUTF(s); os.writeByte(0); } }