Clover coverage report - Code Coverage for hivemind release 1.1-alpha-3
Coverage timestamp: Tue Mar 22 2005 09:10:26 EST
file stats: LOC: 417   Methods: 27
NCLOC: 199   Classes: 2
30 day Evaluation Version distributed via the Maven Jar Repository. Clover is not free. You have 30 days to evaluate it. Please visit http://www.thecortex.net/clover to obtain a licensed version of Clover
 
 Source file Conditionals Statements Methods TOTAL
AbstractParser.java 90.9% 92% 77.8% 88.7%
coverage coverage
 1   
 // Copyright 2004, 2005 The Apache Software Foundation
 2   
 //
 3   
 // Licensed under the Apache License, Version 2.0 (the "License");
 4   
 // you may not use this file except in compliance with the License.
 5   
 // You may obtain a copy of the License at
 6   
 //
 7   
 //     http://www.apache.org/licenses/LICENSE-2.0
 8   
 //
 9   
 // Unless required by applicable law or agreed to in writing, software
 10   
 // distributed under the License is distributed on an "AS IS" BASIS,
 11   
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12   
 // See the License for the specific language governing permissions and
 13   
 // limitations under the License.
 14   
 
 15   
 package org.apache.hivemind.parse;
 16   
 
 17   
 import java.util.ArrayList;
 18   
 import java.util.HashMap;
 19   
 import java.util.List;
 20   
 import java.util.Map;
 21   
 
 22   
 import org.apache.hivemind.ApplicationRuntimeException;
 23   
 import org.apache.hivemind.HiveMind;
 24   
 import org.apache.hivemind.Location;
 25   
 import org.apache.hivemind.Resource;
 26   
 import org.apache.hivemind.impl.LocationImpl;
 27   
 import org.xml.sax.Attributes;
 28   
 import org.xml.sax.Locator;
 29   
 import org.xml.sax.SAXException;
 30   
 import org.xml.sax.SAXParseException;
 31   
 import org.xml.sax.helpers.DefaultHandler;
 32   
 
 33   
 /**
 34   
  * Abstract super-class for parsers based around the SAX event model. This class provides support
 35   
  * for managing a stack of elements, making it reasonable to establish relationships between
 36   
  * elements. It also assists in setting the {@link org.apache.hivemind.Location}of elements as they
 37   
  * are created.
 38   
  * <p>
 39   
  * This support is structured around both XML but is suited towards configuration files rather than
 40   
  * documents, in that the <em>content</em> (parsable character data) within an element is
 41   
  * concatinated together and tracked as a single blob.
 42   
  * 
 43   
  * @author Howard Lewis Ship
 44   
  */
 45   
 public abstract class AbstractParser extends DefaultHandler
 46   
 {
 47   
 
 48   
     /**
 49   
      * The parser is built around a stack of these Items. This used to figure out the current state,
 50   
      * the element being processed, and the matching descriptor object.
 51   
      */
 52   
     private static class Item
 53   
     {
 54   
         StringBuffer _buffer;
 55   
 
 56   
         String _elementName;
 57   
 
 58   
         boolean _ignoreCharacterData;
 59   
 
 60   
         Object _object;
 61   
 
 62   
         /**
 63   
          * Prior state of the parser before this item was pushed.
 64   
          */
 65   
         int _priorState;
 66   
 
 67  37334
         Item(String elementName, Object object, int priorState, boolean ignoreCharacterData)
 68   
         {
 69  37334
             _elementName = elementName;
 70  37334
             _object = object;
 71  37334
             _priorState = priorState;
 72  37334
             _ignoreCharacterData = ignoreCharacterData;
 73   
         }
 74   
 
 75  86829
         void addContent(char[] buffer, int start, int length)
 76   
         {
 77  86829
             if (_ignoreCharacterData)
 78  3835
                 return;
 79   
 
 80  82994
             if (_buffer == null)
 81  17088
                 _buffer = new StringBuffer(length);
 82   
 
 83  82994
             _buffer.append(buffer, start, length);
 84   
         }
 85   
 
 86  36862
         String getContent()
 87   
         {
 88  36862
             if (_buffer != null)
 89  17079
                 return _buffer.toString().trim();
 90   
 
 91  19783
             return null;
 92   
         }
 93   
     }
 94   
 
 95   
     private int _currentColumn;
 96   
 
 97   
     private int _currentLine;
 98   
 
 99   
     private Location _location;
 100   
 
 101   
     private Locator _locator;
 102   
 
 103   
     private Resource _resource;
 104   
 
 105   
     private List _stack;
 106   
 
 107   
     private int _state;
 108   
 
 109   
     private Item _top;
 110   
 
 111   
     /**
 112   
      * Accepts parseable character data from within an element and applies it to the top stack
 113   
      * element. This may be invoked multiple times by the parser, and the overall data will
 114   
      * accumulate. This content can be retrieved via {@link #peekContent()}.
 115   
      */
 116  86829
     public void characters(char[] ch, int start, int length) throws SAXException
 117   
     {
 118  86829
         _top.addContent(ch, start, length);
 119   
     }
 120   
 
 121   
     /**
 122   
      * Invokes {@link #fatalError(SAXParseException)}.
 123   
      */
 124  0
     public void error(SAXParseException ex) throws SAXException
 125   
     {
 126  0
         fatalError(ex);
 127   
     }
 128   
 
 129   
     /**
 130   
      * @param ex
 131   
      *            exception to be thrown
 132   
      * @throws SAXParseException
 133   
      */
 134  0
     public void fatalError(SAXParseException ex) throws SAXException
 135   
     {
 136  0
         throw ex;
 137   
     }
 138   
 
 139   
     /**
 140   
      * Returns a "path" to the current element, as a series of element names seperated by slashes,
 141   
      * i.e., "top/middle/leaf".
 142   
      */
 143  6
     protected String getElementPath()
 144   
     {
 145  6
         StringBuffer buffer = new StringBuffer();
 146   
 
 147  6
         int count = _stack.size();
 148  6
         for (int i = 0; i < count; i++)
 149   
         {
 150  12
             if (i > 0)
 151  6
                 buffer.append('/');
 152   
 
 153  12
             Item item = (Item) _stack.get(i);
 154   
 
 155  12
             buffer.append(item._elementName);
 156   
         }
 157   
 
 158  6
         return buffer.toString();
 159   
     }
 160   
 
 161   
     /**
 162   
      * Returns the current lcoation, as reported by the parser.
 163   
      */
 164  37342
     protected Location getLocation()
 165   
     {
 166  37342
         int line = _locator.getLineNumber();
 167  37342
         int column = _locator.getColumnNumber();
 168   
 
 169  37342
         if (line != _currentLine || column != _currentColumn)
 170  37335
             _location = null;
 171   
 
 172  37342
         if (_location == null)
 173  37335
             _location = new LocationImpl(_resource, line, column);
 174   
 
 175  37342
         _currentLine = line;
 176  37342
         _currentColumn = column;
 177   
 
 178  37342
         return _location;
 179   
     }
 180   
 
 181   
     /**
 182   
      * Returns the {@link Resource}being parsed (as set by {@link #initializeParser(Resource, int)}.
 183   
      */
 184   
 
 185  3
     protected Resource getResource()
 186   
     {
 187  3
         return _resource;
 188   
     }
 189   
 
 190   
     /**
 191   
      * Returns the current state of the parser. State is initially set by
 192   
      * {@link #initializeParser(Resource, int)}and is later updated by
 193   
      * {@link #push(String, Object, int, boolean)}&nbsp;and {@link #pop()}.
 194   
      */
 195  74658
     protected int getState()
 196   
     {
 197  74658
         return _state;
 198   
     }
 199   
 
 200   
     /**
 201   
      * Initializes the parser; this should be called before any SAX parse events are received.
 202   
      * 
 203   
      * @param resource
 204   
      *            the resource being parsed (used for some error messages)
 205   
      * @param startState
 206   
      *            the initial state of the parser (the interpretation of state is determined by
 207   
      *            subclasses)
 208   
      */
 209  255
     protected void initializeParser(Resource resource, int startState)
 210   
     {
 211  255
         _resource = resource;
 212  255
         _stack = new ArrayList();
 213   
 
 214  255
         _location = null;
 215  255
         _state = startState;
 216   
     }
 217   
 
 218   
     /**
 219   
      * Peeks at the top element on the stack, and returns its content (the accumuulated parseable
 220   
      * character data directly enclosed by its start/end tags.
 221   
      */
 222  36862
     protected String peekContent()
 223   
     {
 224  36862
         return _top.getContent();
 225   
     }
 226   
 
 227   
     /**
 228   
      * Peeks at the top element on the stack and returns its element name.
 229   
      */
 230  28746
     protected String peekElementName()
 231   
     {
 232  28746
         return _top._elementName;
 233   
     }
 234   
 
 235   
     /**
 236   
      * Peeks at the top element on the stack and returns the object for that element.
 237   
      */
 238   
 
 239  72991
     protected Object peekObject()
 240   
     {
 241  72991
         return _top._object;
 242   
     }
 243   
 
 244   
     /**
 245   
      * Invoked when the closing tag for an element is enountered {i.e, from
 246   
      * {@link #endElement(String, String, String)}. This removes the corresponding item from the
 247   
      * stack, and sets the parser state back to the (new) top element's state.
 248   
      */
 249  37323
     protected void pop()
 250   
     {
 251  37323
         int count = _stack.size();
 252   
 
 253  37323
         _state = _top._priorState;
 254   
 
 255  37323
         _stack.remove(count - 1);
 256   
 
 257  37323
         if (count == 1)
 258  250
             _top = null;
 259   
         else
 260  37073
             _top = (Item) _stack.get(count - 2);
 261   
     }
 262   
 
 263   
     /**
 264   
      * Enters a new state, pushing an object onto the stack. Invokes
 265   
      * {@link #push(String, Object, int, boolean)}, and ignores character data within the element.
 266   
      * 
 267   
      * @param elementName
 268   
      *            the element whose start tag was just parsed
 269   
      * @param object
 270   
      *            the object created to represent the new object
 271   
      * @param state
 272   
      *            the new state for the parse
 273   
      */
 274  0
     protected void push(String elementName, Object object, int state)
 275   
     {
 276  0
         push(elementName, object, state, true);
 277   
     }
 278   
 
 279   
     /**
 280   
      * Enters a new state, pusubhing an object onto the stack. If the object implements
 281   
      * {@link org.apache.hivemind.LocationHolder}&nbsp; then its location property is set to the
 282   
      * current location.
 283   
      * 
 284   
      * @param elementName
 285   
      *            the element whose start tag was just parsed
 286   
      * @param object
 287   
      *            the object created to represent the new object
 288   
      * @param state
 289   
      *            the new state for the parse
 290   
      * @param ignoreCharacterData
 291   
      *            if true, then any character data (typically whitespace) directly enclosed by the
 292   
      *            element is ignored
 293   
      */
 294  37334
     protected void push(String elementName, Object object, int state, boolean ignoreCharacterData)
 295   
     {
 296  37334
         HiveMind.setLocation(object, getLocation());
 297   
 
 298  37334
         Item item = new Item(elementName, object, _state, ignoreCharacterData);
 299   
 
 300  37334
         _stack.add(item);
 301   
 
 302  37334
         _top = item;
 303  37334
         _state = state;
 304   
     }
 305   
 
 306   
     /**
 307   
      * Resets all state after a parse.
 308   
      */
 309  255
     protected void resetParser()
 310   
     {
 311  255
         _resource = null;
 312  255
         _locator = null;
 313  255
         _stack = null;
 314  255
         _location = null;
 315   
     }
 316   
 
 317   
     /**
 318   
      * Invoked by the parser, the locator is stored and later used by {@link #getLocation()}.
 319   
      */
 320  253
     public void setDocumentLocator(Locator locator)
 321   
     {
 322  253
         _locator = locator;
 323   
     }
 324   
 
 325   
     /**
 326   
      * Forces a change to a specific state.
 327   
      */
 328  0
     protected void setState(int state)
 329   
     {
 330  0
         _state = state;
 331   
     }
 332   
 
 333   
     /**
 334   
      * Invoked when an unexpected element is parsed (useful for parses that don't perform
 335   
      * validation, or when there's no DTD).
 336   
      * 
 337   
      * @throws ApplicationRuntimeException
 338   
      *             describing the situation
 339   
      */
 340  1
     protected void unexpectedElement(String elementName)
 341   
     {
 342  1
         throw new ApplicationRuntimeException(ParseMessages.unexpectedElement(
 343   
                 elementName,
 344   
                 getElementPath()), getLocation(), null);
 345   
     }
 346   
 
 347   
     /**
 348   
      * Ocassionaly it is necessary to "change our mind" about what's on the top of the stack.
 349   
      * 
 350   
      * @param object
 351   
      *            the new object for the top stack element
 352   
      */
 353  0
     protected void updateObject(Object object)
 354   
     {
 355  0
         _top._object = object;
 356   
     }
 357   
 
 358   
     /**
 359   
      * Invokes {@link #fatalError(SAXParseException)},
 360   
      */
 361  0
     public void warning(SAXParseException ex) throws SAXException
 362   
     {
 363  0
         fatalError(ex);
 364   
     }
 365   
 
 366  37335
     private Map constructAttributesMap(Attributes attributes)
 367   
     {
 368  37335
         Map result = new HashMap();
 369  37335
         int count = attributes.getLength();
 370   
 
 371  37335
         for (int i = 0; i < count; i++)
 372   
         {
 373  52965
             String key = attributes.getLocalName(i);
 374   
 
 375  52965
             if (HiveMind.isBlank(key))
 376  52965
                 key = attributes.getQName(i);
 377   
 
 378  52965
             String value = attributes.getValue(i);
 379   
 
 380  52965
             result.put(key, value);
 381   
         }
 382   
 
 383  37335
         return result;
 384   
     }
 385   
 
 386   
     /**
 387   
      * Invoked when an element's start tag is recognized. The element and attributes are provided to
 388   
      * the subclass for further processing.
 389   
      */
 390   
     protected abstract void begin(String elementName, Map attributes);
 391   
 
 392   
     /**
 393   
      * Invoked when an element's close tag is recognized. The element is provided. The content of
 394   
      * the element (the unparsed whitespace within the element's tags) is available via
 395   
      * {@link #peekContent()}.
 396   
      */
 397   
 
 398   
     protected abstract void end(String elementName);
 399   
 
 400  37323
     public void endElement(String uri, String localName, String qName) throws SAXException
 401   
     {
 402  37323
         end(getElementName(localName, qName));
 403   
     }
 404   
 
 405  37335
     public void startElement(String uri, String localName, String qName, Attributes attributes)
 406   
             throws SAXException
 407   
     {
 408  37335
         String elementName = getElementName(localName, qName);
 409   
 
 410  37335
         begin(elementName, constructAttributesMap(attributes));
 411   
     }
 412   
 
 413  74658
     private String getElementName(String localName, String qName)
 414   
     {
 415  74658
         return qName != null ? qName : localName;
 416   
     }
 417   
 }