Clover coverage report -
Coverage timestamp: Sun Nov 1 2009 23:08:24 UTC
file stats: LOC: 347   Methods: 4
NCLOC: 248   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
DOMCompressor.java 50% 71.5% 100% 68.2%
coverage coverage
 1    /*
 2    * Licensed to the Apache Software Foundation (ASF) under one or more
 3    * contributor license agreements. See the NOTICE file distributed with
 4    * this work for additional information regarding copyright ownership.
 5    * The ASF licenses this file to You under the Apache License, Version 2.0
 6    * (the "License"); you may not use this file except in compliance with
 7    * the License. You may obtain a copy of the License at
 8    *
 9    * http://www.apache.org/licenses/LICENSE-2.0
 10    *
 11    * Unless required by applicable law or agreed to in writing, software
 12    * distributed under the License is distributed on an "AS IS" BASIS,
 13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14    * See the License for the specific language governing permissions and
 15    * limitations under the License.
 16    *
 17    * $Id: DOMCompressor.java 568786 2007-08-23 00:40:57Z vgritsenko $
 18    */
 19   
 20    package org.apache.xindice.xml.dom;
 21   
 22    import org.apache.commons.logging.Log;
 23    import org.apache.commons.logging.LogFactory;
 24    import org.apache.xindice.util.ReadOnlyException;
 25    import org.apache.xindice.xml.Signatures;
 26    import org.apache.xindice.xml.SymbolTable;
 27    import org.apache.xindice.xml.XMLCompressedOutput;
 28   
 29    import org.w3c.dom.Attr;
 30    import org.w3c.dom.DOMException;
 31    import org.w3c.dom.Document;
 32    import org.w3c.dom.NamedNodeMap;
 33    import org.w3c.dom.Node;
 34    import org.w3c.dom.NodeList;
 35   
 36    import java.io.ByteArrayOutputStream;
 37    import java.io.IOException;
 38    import java.io.OutputStream;
 39   
 40    /**
 41    * DOMCompressor is an OutputStream extension that provides functions for
 42    * writing DOM types to a Xindice Compressed XML Stream.
 43    *
 44    * @version $Revision: 568786 $, $Date: 2007-08-22 17:40:57 -0700 (Wed, 22 Aug 2007) $
 45    */
 46    public final class DOMCompressor extends XMLCompressedOutput {
 47   
 48    private static final Log log = LogFactory.getLog(DOMCompressor.class);
 49   
 50   
 51  225915 public DOMCompressor(OutputStream os, SymbolTable st) {
 52  225915 super(os, st);
 53    }
 54   
 55    /**
 56    * writeNode writes a Node to the compressed output stream. This method
 57    * is recursive and will write all children of the specific Node.
 58    *
 59    * @param node the node to write
 60    * @throws IOException if the write to underlying stream has failed
 61    * @throws DOMException if used symbol table is read only and some of
 62    * node's content can not be serialized.
 63    */
 64  480273 public void writeNode(Node node) throws IOException {
 65    // Check if it's node of ours
 66  480266 if (node instanceof NodeImpl) {
 67  480265 NodeImpl impl = (NodeImpl) node;
 68  480265 Document doc = node.getOwnerDocument();
 69   
 70  480275 SymbolTable docSymbols = null;
 71  480272 if (doc instanceof DocumentImpl) {
 72  480272 docSymbols = ((DocumentImpl) doc).getSymbols();
 73    }
 74   
 75    // If document is compressed with exact same symbol table
 76    // and is not dirty, use its compressed bytes.
 77  480271 if (!impl.dirty && impl.data != null && docSymbols == st) {
 78  261 write(impl.data, impl.pos, impl.len);
 79  261 flush();
 80  261 return;
 81    }
 82    }
 83   
 84    // Have to do it manually
 85  480012 switch (node.getNodeType()) {
 86   
 87  155149 case Node.ELEMENT_NODE:
 88    {
 89  155149 String nsURI = node.getNamespaceURI();
 90  155149 short symbolID;
 91  155149 if (nsURI != null) {
 92  20012 try {
 93  20012 symbolID = st.getSymbol(node.getNodeName(), nsURI, true);
 94    } catch (ReadOnlyException e) {
 95  0 throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
 96    "Element can not be serialized (read only symbol table). " +
 97    "Name: '" + node.getNodeName() + "', namespace: " + nsURI);
 98    }
 99    } else {
 100  135137 try {
 101  135137 symbolID = st.getSymbol(node.getNodeName(), true);
 102    } catch (ReadOnlyException e) {
 103  0 throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
 104    "Element can not be serialized (read only symbol table). " +
 105    "Name: '" + node.getNodeName() + "'");
 106    }
 107    }
 108   
 109  155149 byte signature = Signatures.NODE_ELEM;
 110  155149 int attrLen = node.getAttributes().getLength();
 111  155149 int attrSize = getSizeType(attrLen);
 112  155149 if (attrLen > 0) {
 113  90802 signature |= Signatures.ELEM_ATTRS;
 114    }
 115  155149 if (node.hasChildNodes()) {
 116  128315 signature |= Signatures.ELEM_CHILDREN;
 117    }
 118  155149 signature |= (byte) (attrSize);
 119  155149 byte[] children = buildChildren(node);
 120  155149 int valLen = children.length;
 121  155149 int sizeType = getSizeType(valLen + 11);
 122  155149 signature |= (byte) (sizeType << 0x2);
 123  155149 writeByte(signature);
 124  155149 valLen += (getSizeSize(sizeType) + 3);
 125  155149 writeSize(sizeType, valLen);
 126  155149 writeShort(symbolID);
 127  155149 write(children);
 128  155149 break;
 129    }
 130   
 131  120554 case Node.ATTRIBUTE_NODE:
 132    {
 133  120554 Attr attr = (Attr) node;
 134  120553 String nsURI = attr.getNamespaceURI();
 135  120554 short symbolID;
 136  120554 if (nsURI != null) {
 137  5089 try {
 138  5089 symbolID = st.getSymbol(attr.getName(), nsURI, true);
 139    } catch (ReadOnlyException e) {
 140  0 throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
 141    "Attribute can not be serialized (read only symbol table). " +
 142    "Name: '" + attr.getName() + "', namespace: " + nsURI);
 143    }
 144    } else {
 145  115465 try {
 146  115465 symbolID = st.getSymbol(attr.getName(), true);
 147    } catch (ReadOnlyException e) {
 148  0 throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
 149    "Attribute can not be serialized (read only symbol table). " +
 150    "Name: '" + attr.getName() + "'");
 151    }
 152    }
 153  120554 writeShort(symbolID);
 154   
 155  120554 byte[] b = attr.getValue().getBytes("UTF-8");
 156  120554 writeShort((short) b.length);
 157  120554 write(b);
 158  120554 break;
 159    }
 160   
 161  162999 case Node.TEXT_NODE:
 162    {
 163  162999 byte[] b = node.getNodeValue().getBytes("UTF-8");
 164  162999 int valLen = b.length;
 165  162999 int sizeType = getSizeType(valLen + 5);
 166  162999 byte signature = Signatures.NODE_TEXT;
 167  162999 signature |= (byte) (sizeType << 0x2);
 168  162999 writeByte(signature);
 169  162999 valLen += (getSizeSize(sizeType) + 1);
 170  162999 writeSize(sizeType, valLen);
 171  162999 write(b);
 172  162999 break;
 173    }
 174   
 175  10 case Node.CDATA_SECTION_NODE:
 176    {
 177  10 byte[] b = node.getNodeValue().getBytes("UTF-8");
 178  10 int valLen = b.length;
 179  10 writeByte(Signatures.NODE_DECL | Signatures.DECL_CDATA);
 180  10 valLen += 5;
 181  10 writeInt(valLen);
 182  10 write(b);
 183  10 break;
 184    }
 185   
 186  0 case Node.ENTITY_REFERENCE_NODE:
 187    {
 188  0 String value = node.getNodeName();
 189  0 byte signature = /* Signatures.NODE_TEXT | */ Signatures.TEXT_ENTITY;
 190  0 short symbol = 0;
 191  0 int encoding = 0;
 192  0 if (value.equals("&amp;")) {
 193  0 signature |= (byte) (Signatures.ENT_AMP);
 194  0 } else if (value.equals("&lt;")) {
 195  0 signature |= (byte) (Signatures.ENT_LT);
 196  0 } else if (value.equals("&gt;")) {
 197  0 signature |= (byte) (Signatures.ENT_GT);
 198  0 } else if (value.equals("&quot;")) {
 199  0 signature |= (byte) (Signatures.ENT_QUOT);
 200  0 } else if (value.equals("&apos;")) {
 201  0 signature |= (byte) (Signatures.ENT_APOS);
 202  0 } else if (value.startsWith("&#x")) {
 203  0 encoding = 1;
 204  0 signature |= (byte) (Signatures.ENT_UNICODE);
 205    // Convert the Unicode to a short
 206    // TODO symbol =
 207    } else {
 208  0 encoding = 2;
 209  0 try {
 210  0 symbol = st.getSymbol(value, true);
 211    } catch (ReadOnlyException e) {
 212  0 throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
 213    "Entity reference can not be serialized (read only symbol table). " +
 214    "Value: '" + value + "'");
 215    }
 216  0 signature |= (byte) (Signatures.ENT_DEFINED);
 217    }
 218  0 writeByte(signature);
 219  0 if (encoding > 0) {
 220  0 writeShort(symbol);
 221    }
 222  0 break;
 223    }
 224   
 225  0 case Node.ENTITY_NODE:
 226    {
 227  0 break;
 228    }
 229   
 230  5869 case Node.PROCESSING_INSTRUCTION_NODE:
 231    {
 232  5869 String value = node.getNodeName() + " " + node.getNodeValue();
 233  5869 byte[] b = value.getBytes("UTF-8");
 234  5869 int valLen = b.length;
 235  5869 writeByte(Signatures.NODE_PROC);
 236  5869 valLen += 5;
 237  5869 writeInt(valLen);
 238  5869 write(b);
 239  5869 break;
 240    }
 241   
 242  156 case Node.COMMENT_NODE:
 243    {
 244  156 byte[] b = node.getNodeValue().getBytes("UTF-8");
 245  156 int valLen = b.length;
 246  156 writeByte(Signatures.NODE_DECL | Signatures.DECL_COMMENT);
 247  156 valLen += 5;
 248  156 writeInt(valLen);
 249  156 write(b);
 250  156 break;
 251    }
 252   
 253  35277 case Node.DOCUMENT_NODE:
 254    {
 255  35277 byte[] children = buildChildren(node);
 256  35277 writeInt(children.length);
 257  35277 write(children);
 258  35277 break;
 259    }
 260   
 261  0 case Node.DOCUMENT_TYPE_NODE:
 262    {
 263    // Errr?
 264  0 break;
 265    }
 266   
 267  0 case Node.DOCUMENT_FRAGMENT_NODE:
 268    {
 269  0 byte[] children = buildChildren(node);
 270  0 writeInt(children.length);
 271  0 write(children);
 272  0 break;
 273    }
 274   
 275  0 case Node.NOTATION_NODE:
 276    {
 277    // byte signature = (byte)(Signatures.Decl << 0x6);
 278  0 break;
 279    }
 280   
 281  0 default:
 282  0 if (log.isWarnEnabled()) {
 283  0 log.warn("invalid node type : " + node.getNodeType());
 284    }
 285    }
 286  480014 flush();
 287    }
 288   
 289    /**
 290    * buildChildren builds a byte array containing the child definitions
 291    * for the specified Node. Because Xindice's compressed system is a
 292    * depth-first system that requires the length of nested data sets in
 293    * order to allow quick traversal, this process must build the byte
 294    * arrays in memory before including them in their parents.
 295    * <br>
 296    * If a Node has already been graphed and has not been modified, it's
 297    * byte array portion is simply copied into the stream without
 298    * requiring a regraph. This allows for quickly streaming out a modified
 299    * DOM tree, but will take some time in streaming a brand new DOM tree.
 300    *
 301    * @param node The Node whose children to build
 302    * @return The child byte array
 303    * @throws IOException If the build failed
 304    */
 305  190425 private byte[] buildChildren(Node node) throws IOException {
 306  190425 ByteArrayOutputStream bos = new ByteArrayOutputStream();
 307  190426 DOMCompressor out = new DOMCompressor(bos, st);
 308  190426 if (node.getNodeType() == Node.ELEMENT_NODE) {
 309  155149 NamedNodeMap attrs = node.getAttributes();
 310  155149 int len = attrs.getLength();
 311  155149 out.writeSize(len);
 312  155149 for (int i = 0; i < len; i++) {
 313  120554 out.writeNode(attrs.item(i));
 314    }
 315    }
 316   
 317  190426 NodeList children = node.getChildNodes();
 318  190425 int len = children.getLength();
 319  190426 for (int i = 0; i < len; i++) {
 320  324230 out.writeNode(children.item(i));
 321    }
 322   
 323  190426 return bos.toByteArray();
 324    }
 325   
 326    /**
 327    * Compress is a convenience method that compresses a Node into a byte
 328    * array with a single call.
 329    *
 330    * @param node The Node to compress
 331    * @param symbols The Symbol Table to use
 332    * @return The resulting byte array
 333    */
 334  35490 public static byte[] compress(Node node, SymbolTable symbols) {
 335  35490 ByteArrayOutputStream bos = new ByteArrayOutputStream();
 336  35490 DOMCompressor xco = new DOMCompressor(bos, symbols);
 337  35490 node.normalize();
 338  35490 try {
 339  35490 xco.writeNode(node);
 340    } catch (IOException e) {
 341    // ByteArrayOutputStream does not throw IOException, ignore
 342    }
 343  35490 return bos.toByteArray();
 344    }
 345    }
 346   
 347