Clover coverage report -
Coverage timestamp: Sun Nov 1 2009 23:08:24 UTC
file stats: LOC: 367   Methods: 27
NCLOC: 220   Classes: 2
 
 Source file Conditionals Statements Methods TOTAL
SymbolTable.java 91.7% 92.9% 92.6% 92.6%
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: SymbolTable.java 712562 2008-11-09 21:49:50Z vgritsenko $
 18    */
 19   
 20    package org.apache.xindice.xml;
 21   
 22    import org.apache.xindice.util.ReadOnlyException;
 23   
 24    import org.w3c.dom.DOMException;
 25    import org.w3c.dom.Document;
 26    import org.w3c.dom.Element;
 27    import org.w3c.dom.NodeList;
 28   
 29    import java.io.Serializable;
 30    import java.util.HashMap;
 31    import java.util.Iterator;
 32    import java.util.Map;
 33   
 34    /**
 35    * SymbolTable is a class that allows the Xindice Compression system to
 36    * resolve symbol IDs in compression/decompression passes.
 37    *
 38    * @version $Revision: 712562 $, $Date: 2008-11-09 21:49:50 +0000 (Sun, 09 Nov 2008) $
 39    */
 40    public class SymbolTable implements XMLSerializable {
 41    static final String SYMBOLS = "symbols";
 42    static final String SYMBOL = "symbol";
 43    static final String NAME = "name";
 44    static final String NSURI = "nsuri";
 45    static final String ID = "id";
 46   
 47    private short maxSymbol = -1;
 48    private final Map symbols = new HashMap(); // String to SymbolInfo
 49    private final Map names = new HashMap(); // Short to SymbolInfo
 50    private boolean readOnly;
 51   
 52    private transient boolean dirty;
 53    private transient long lastModified = System.currentTimeMillis();
 54   
 55   
 56    public static final class SymbolInfo implements Serializable {
 57    private final String namespaceURI;
 58    private final String qname;
 59    private final short symbol;
 60   
 61  3081 private SymbolInfo(String qname, short symbol) {
 62  3081 this.namespaceURI = null;
 63  3081 this.qname = qname;
 64  3081 this.symbol = symbol;
 65    }
 66   
 67  6222 private SymbolInfo(String qname, String namespaceURI, short symbol) {
 68  6222 this.namespaceURI = namespaceURI;
 69  6222 this.qname = qname;
 70  6222 this.symbol = symbol;
 71    }
 72   
 73  211652 public String getNamespaceURI() {
 74  211652 return namespaceURI;
 75    }
 76   
 77  211652 public String getQName() {
 78  211652 return qname;
 79    }
 80   
 81  6 public short getSymbolID() {
 82  6 return symbol;
 83    }
 84    }
 85   
 86    //
 87    // Instance Methods
 88    //
 89   
 90  2332 public SymbolTable() {
 91    }
 92   
 93  131 public SymbolTable(Element symbols) {
 94  131 streamFromXML(symbols);
 95    }
 96   
 97  124 public SymbolTable(Element symbols, boolean readOnly) {
 98  124 this(symbols);
 99  124 this.readOnly = readOnly;
 100    }
 101   
 102    /**
 103    * @return true if symbol table has been modified
 104    */
 105  45442 public final boolean isDirty() {
 106  45442 return dirty;
 107    }
 108   
 109    /**
 110    * Set dirty flag to true and update last modified
 111    * time stamp.
 112    */
 113  5577 private void markDirty() {
 114  5577 this.dirty = true;
 115  5577 this.lastModified = System.currentTimeMillis();
 116    }
 117   
 118    /**
 119    * Reset dirty flag to false. Should be invoked
 120    * once symbol table is successfully saved.
 121    */
 122  941 public final void resetDirty() {
 123  941 this.dirty = false;
 124    }
 125   
 126  169 public final long getLastModified() {
 127  169 return lastModified;
 128    }
 129   
 130    /**
 131    * @return true if symbol table is read only
 132    */
 133  1036 public final boolean isReadOnly() {
 134  1036 return readOnly;
 135    }
 136   
 137    //
 138    // Lookup by symbol id
 139    //
 140   
 141  390954 public final SymbolInfo getSymbolInfo(short symbol) {
 142  390954 return (SymbolInfo) names.get(new Short(symbol));
 143    }
 144   
 145  65918 public final String getNamespaceURI(short symbol) {
 146  65918 SymbolInfo info = getSymbolInfo(symbol);
 147  65918 return info != null ? info.namespaceURI : null;
 148    }
 149   
 150  113383 public final String getName(short symbol) {
 151  113383 SymbolInfo info = getSymbolInfo(symbol);
 152  113383 return info != null ? info.qname : null;
 153    }
 154   
 155    //
 156    // Lookup by qname and namespace
 157    //
 158   
 159  21 public final short getSymbol(String qname) {
 160  21 try {
 161  21 return getSymbol(qname, false);
 162    } catch (ReadOnlyException e) {
 163    // Won't happen
 164  0 throw new IllegalStateException();
 165    }
 166    }
 167   
 168  7 public final short getSymbol(String qname, String namespaceURI) {
 169  7 try {
 170  7 return getSymbol(qname, namespaceURI, false);
 171    } catch (ReadOnlyException e) {
 172    // Won't happen
 173  0 throw new IllegalStateException();
 174    }
 175    }
 176   
 177  274901 public final short getSymbol(String qname, boolean create) throws ReadOnlyException {
 178  274901 SymbolInfo info = (SymbolInfo) symbols.get(qname);
 179  274901 if (info != null) {
 180  271810 return info.symbol;
 181    }
 182   
 183  3088 if (create) {
 184  3082 if (readOnly) {
 185  1 throw new ReadOnlyException();
 186    }
 187   
 188  3081 synchronized (symbols) {
 189  3081 short id = ++maxSymbol;
 190  3081 info = new SymbolInfo(qname, id);
 191  3081 symbols.put(qname, info);
 192  3081 names.put(new Short(id), info);
 193  3081 markDirty();
 194  3081 return id;
 195    }
 196    }
 197   
 198  6 return -1;
 199    }
 200   
 201  25212 public final short getSymbol(String qname, String namespaceURI, boolean create) throws ReadOnlyException {
 202  25212 String lookupName = getLookupName(qname, namespaceURI);
 203   
 204  25212 SymbolInfo info = (SymbolInfo) symbols.get(lookupName);
 205  25212 if (info != null) {
 206  22709 return info.symbol;
 207    }
 208   
 209  2503 if (create) {
 210  2496 if (readOnly) {
 211  0 throw new ReadOnlyException();
 212    }
 213   
 214  2496 synchronized (symbols) {
 215  2496 short id = ++maxSymbol;
 216  2496 info = new SymbolInfo(qname, namespaceURI, id);
 217  2496 symbols.put(lookupName, info);
 218  2496 names.put(new Short(id), info);
 219  2496 markDirty();
 220  2496 return id;
 221    }
 222    }
 223   
 224  7 return -1;
 225    }
 226   
 227    //
 228    // Lookup normalized symbol
 229    //
 230   
 231  0 public final short getNormalizedSymbol(String localName, String namespaceURI) {
 232  0 try {
 233  0 return getNormalizedSymbol(localName, namespaceURI, false);
 234    } catch (ReadOnlyException e) {
 235    // Won't happen
 236  0 throw new IllegalStateException();
 237    }
 238    }
 239   
 240    /**
 241    * Lookup normalized symbol by element (or attribute) local name and namespace URI.
 242    *
 243    * @param localName element (attribute) local name
 244    * @param namespaceURI element (attribute) namespace URI
 245    * @param create when true, creates symbol if it is missing
 246    * @return symbol id or -1
 247    * @throws ReadOnlyException if 'create' option specified and the symbol is missing
 248    */
 249  0 public final short getNormalizedSymbol(String localName, String namespaceURI, boolean create) throws ReadOnlyException {
 250  0 String normalizedQName = getNormalizedQName(localName, namespaceURI);
 251  0 return getSymbol(normalizedQName, namespaceURI, create);
 252    }
 253   
 254    /**
 255    * Lookup normalized symbol by lookup string and (optional) namespace map. Lookup
 256    * string can take one of the following forms:
 257    * <ul>
 258    * <li> [<namespaceURI>]<nsPrefix>:<localName>, where nsPrefix can be empty
 259    * <li> <nsPrefix>:<localName>, any regular qName
 260    * </ul>
 261    *
 262    * Namespace map is used to determine namespaceURI for passed in lookup string and
 263    * normalize namespace prefix.
 264    *
 265    * @param lookup lookup string
 266    * @param nsMap namespace map
 267    * @param create when true, creates symbol if it is missing
 268    * @return symbol id or -1
 269    * @throws ReadOnlyException if 'create' option specified and the symbol is missing
 270    */
 271  2625 public final short getNormalizedSymbol(String lookup, NamespaceMap nsMap, boolean create) throws ReadOnlyException {
 272    // Parse [<namespaceURI>]<nsPrefix>:<localName> with optional nsPrefix
 273  2625 if (lookup.startsWith("[")) {
 274  8 int idx = lookup.indexOf(']');
 275  8 String nsURI = lookup.substring(1, idx);
 276  8 int cidx = lookup.indexOf(':', idx + 1);
 277  8 String name = cidx != -1 ? lookup.substring(cidx + 1) : lookup.substring(idx + 1);
 278   
 279  8 return getSymbol(getNormalizedQName(name, nsURI), nsURI, create);
 280    }
 281   
 282    // Parse <nsPrefix>:<localName> with passed in nsMap
 283  2617 int idx = lookup.indexOf(':');
 284  2617 if (idx != -1) {
 285  94 String pfx = lookup.substring(0, idx);
 286  94 String nsURI = (String) nsMap.get(pfx);
 287  94 if (nsURI != null) {
 288  94 String name = lookup.substring(idx + 1);
 289  94 return getSymbol(getNormalizedQName(name, nsURI), nsURI, create);
 290    }
 291    }
 292   
 293  2523 return getSymbol(lookup, create);
 294    }
 295   
 296    //
 297    // XMLSerializable
 298    //
 299   
 300  1110 public final Element streamToXML(Document doc) throws DOMException {
 301  1110 Element root = doc.createElement(SYMBOLS);
 302   
 303  1110 synchronized (symbols) {
 304  1110 Iterator i = symbols.values().iterator();
 305  1110 while (i.hasNext()) {
 306  7149 SymbolInfo info = (SymbolInfo) i.next();
 307   
 308  7149 Element e = doc.createElement(SYMBOL);
 309  7149 e.setAttribute(ID, Short.toString(info.symbol));
 310  7149 e.setAttribute(NAME, info.qname);
 311  7149 if (info.namespaceURI != null && info.namespaceURI.length() > 0) {
 312  2404 e.setAttribute(NSURI, info.namespaceURI);
 313    }
 314  7149 root.appendChild(e);
 315    }
 316    }
 317   
 318  1110 return root;
 319    }
 320   
 321  421 public final void streamFromXML(Element element) throws DOMException {
 322  421 synchronized (symbols) {
 323  421 maxSymbol = -1;
 324  421 symbols.clear();
 325  421 names.clear();
 326   
 327  421 NodeList list = element.getElementsByTagName(SYMBOL);
 328  421 int size = list.getLength();
 329  421 for (int i = 0; i < size; i++) {
 330  3726 Element elem = (Element) list.item(i);
 331   
 332  3726 String qname = elem.getAttribute(NAME);
 333  3726 String namespaceURI = elem.getAttribute(NSURI);
 334  3726 if (namespaceURI != null && namespaceURI.length() == 0) {
 335  3569 namespaceURI = null;
 336    }
 337   
 338  3726 short id = Short.parseShort(elem.getAttribute(ID));
 339  3726 if (id > maxSymbol) {
 340  1320 maxSymbol = id;
 341    }
 342   
 343  3726 SymbolInfo info = new SymbolInfo(qname, namespaceURI, id);
 344  3726 if (namespaceURI != null) {
 345  157 String lookupName = getLookupName(qname, namespaceURI);
 346  157 symbols.put(lookupName, info);
 347    } else {
 348  3569 symbols.put(qname, info);
 349    }
 350   
 351  3726 names.put(new Short(id), info);
 352    }
 353    }
 354    }
 355   
 356    //
 357    // Implementation methods
 358    //
 359   
 360  102 private String getNormalizedQName(String localName, String namespaceURI) {
 361  102 return "ns" + namespaceURI.hashCode() + ':' + localName;
 362    }
 363   
 364  25369 private String getLookupName(String qname, String namespaceURI) {
 365  25369 return '[' + namespaceURI + ']' + qname;
 366    }
 367    }