Clover coverage report - Code Coverage for hivemind release 1.0-beta-1
Coverage timestamp: Sat Jul 3 2004 09:41:37 EDT
file stats: LOC: 405   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% 93.2% 81.5% 90.2%
coverage coverage
 1   
 //  Copyright 2004 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.
 35   
  * This class provides support for managing a stack of elements, making it
 36   
  * reasonable to establish relationships between elements. It also assists in
 37   
  * setting the {@link org.apache.hivemind.Location} of elements as they are created.
 38   
  * 
 39   
  * <p>
 40   
  * This support is structured around both XML and Simplified Data Language (SDL), both
 41   
  * of which can produce SAX events. It is also suited towards configuration files rather 
 42   
  * than documents, in that the <em>content</em> (parsable character data) within an element
 43   
  * is concatinated together and tracked as a single blob.
 44   
  *
 45   
  * @author Howard Lewis Ship
 46   
  */
 47   
 public abstract class AbstractParser extends DefaultHandler
 48   
 {
 49   
 
 50   
     /**
 51   
      * The parser is built around a stack of these Items.  This used to figure
 52   
      * out the current state, the element being processed, and the matching descriptor
 53   
      * object.
 54   
      */
 55   
     private static class Item
 56   
     {
 57   
         StringBuffer _buffer;
 58   
         String _elementName;
 59   
         boolean _ignoreCharacterData;
 60   
         Object _object;
 61   
 
 62   
         /**
 63   
          * Prior state of the parser before this item was pushed.
 64   
          */
 65   
         int _priorState;
 66   
 
 67  20726
         Item(String elementName, Object object, int priorState, boolean ignoreCharacterData)
 68   
         {
 69  20726
             _elementName = elementName;
 70  20726
             _object = object;
 71  20726
             _priorState = priorState;
 72  20726
             _ignoreCharacterData = ignoreCharacterData;
 73   
         }
 74   
 
 75  9566
         void addContent(char[] buffer, int start, int length)
 76   
         {
 77  9566
             if (_ignoreCharacterData)
 78  9431
                 return;
 79   
 
 80  135
             if (_buffer == null)
 81  66
                 _buffer = new StringBuffer(length);
 82   
 
 83  135
             _buffer.append(buffer, start, length);
 84   
         }
 85   
 
 86  2771
         String getContent()
 87   
         {
 88  2771
             if (_buffer != null)
 89  66
                 return _buffer.toString().trim();
 90   
 
 91  2705
             return null;
 92   
         }
 93   
     }
 94   
 
 95   
     private int _currentColumn;
 96   
     private int _currentLine;
 97   
     private Location _location;
 98   
     private Locator _locator;
 99   
     private Resource _resource;
 100   
     private List _stack;
 101   
     private int _state;
 102   
     private Item _top;
 103   
 
 104   
     /**
 105   
      * Accepts parseable character data from within an element and applies it to the
 106   
      * top stack element. This may be invoked multiple times by the parser, and the
 107   
      * overall data will accumulate. This content can be
 108   
      * retrieved via {@link #peekContent()}.
 109   
      */
 110  9566
     public void characters(char[] ch, int start, int length) throws SAXException
 111   
     {
 112  9566
         _top.addContent(ch, start, length);
 113   
     }
 114   
 
 115   
     /**
 116   
      * Invokes {@link #fatalError(SAXParseException)}.
 117   
      */
 118  0
     public void error(SAXParseException ex) throws SAXException
 119   
     {
 120  0
         fatalError(ex);
 121   
     }
 122   
 
 123   
     /**
 124   
      * @param ex exception to be thrown
 125   
      * @throws SAXParseException 
 126   
      */
 127  0
     public void fatalError(SAXParseException ex) throws SAXException
 128   
     {
 129  0
         throw ex;
 130   
     }
 131   
 
 132   
     /**
 133   
      * Returns a "path" to the current element, as a series of element names
 134   
      * seperated by slashes, i.e., "top/middle/leaf".
 135   
      */
 136  6
     protected String getElementPath()
 137   
     {
 138  6
         StringBuffer buffer = new StringBuffer();
 139   
 
 140  6
         int count = _stack.size();
 141  6
         for (int i = 0; i < count; i++)
 142   
         {
 143  12
             if (i > 0)
 144  6
                 buffer.append('/');
 145   
 
 146  12
             Item item = (Item) _stack.get(i);
 147   
 
 148  12
             buffer.append(item._elementName);
 149   
         }
 150   
 
 151  6
         return buffer.toString();
 152   
     }
 153   
 
 154   
     /**
 155   
      * Returns the current lcoation, as reported by the parser.
 156   
      */
 157  21106
     protected Location getLocation()
 158   
     {
 159  21106
         int line = _locator.getLineNumber();
 160  21106
         int column = _locator.getColumnNumber();
 161   
 
 162  21106
         if (line != _currentLine || column != _currentColumn)
 163  21106
             _location = null;
 164   
 
 165  21106
         if (_location == null)
 166  21106
             _location = new LocationImpl(_resource, line, column);
 167   
 
 168  21106
         return _location;
 169   
     }
 170   
 
 171   
     /**
 172   
      * Returns the {@link Resource} being parsed (as set
 173   
      * by {@link #initializeParser(Resource, int)}.
 174   
      */
 175   
 
 176  1
     protected Resource getResource()
 177   
     {
 178  1
         return _resource;
 179   
     }
 180   
 
 181   
     /**
 182   
      * Returns the current state of the parser. State is initially
 183   
      * set by {@link #initializeParser(Resource, int) and is later updated
 184   
      * by {@link #push(String, Object, int)} and {@link #pop()}.
 185   
      */
 186  41442
     protected int getState()
 187   
     {
 188  41442
         return _state;
 189   
     }
 190   
 
 191   
     /**
 192   
      * Initializes the parser; this should be called before any SAX parse events
 193   
      * are received.
 194   
      * 
 195   
      * @param resource the resource being parsed (used for some error messages)
 196   
      * @param startState the initial state of the parser (the interpretation of state
 197   
      * is determined by subclasses)
 198   
      */
 199  177
     protected void initializeParser(Resource resource, int startState)
 200   
     {
 201  177
         _resource = resource;
 202  177
         _stack = new ArrayList();
 203   
 
 204  177
         _location = null;
 205  177
         _state = startState;
 206   
     }
 207   
 
 208   
     /**
 209   
      * Peeks at the top element on the stack, and returns
 210   
      * its content (the accumuulated parseable character data directly
 211   
      * enclosed by its start/end tags (for XML) or braces (for SDL).
 212   
      */
 213  2771
     protected String peekContent()
 214   
     {
 215  2771
         return _top.getContent();
 216   
     }
 217   
 
 218   
     /**
 219   
      * Peeks at the top element on the stack and returns its element name.
 220   
      */
 221  15692
     protected String peekElementName()
 222   
     {
 223  15692
         return _top._elementName;
 224   
     }
 225   
 
 226   
     /**
 227   
      * Peeks at the top element on the stack and returns the object for that element.
 228   
      */
 229   
 
 230  23428
     protected Object peekObject()
 231   
     {
 232  23428
         return _top._object;
 233   
     }
 234   
 
 235   
     /**
 236   
      * Invoked when the closing tag for an element is enountered
 237   
      * {i.e, from {@link #endElement(String)}). This removes the corresponding
 238   
      * item from the stack, and sets the parser state back to the (new) top element's
 239   
      * state.
 240   
      */
 241  20715
     protected void pop()
 242   
     {
 243  20715
         int count = _stack.size();
 244   
 
 245  20715
         _state = _top._priorState;
 246   
 
 247  20715
         _stack.remove(count - 1);
 248   
 
 249  20715
         if (count == 1)
 250  174
             _top = null;
 251   
         else
 252  20541
             _top = (Item) _stack.get(count - 2);
 253   
     }
 254   
 
 255   
     /**
 256   
      * Enters a new state, pushing an object onto the stack.
 257   
      * Invokes {@link #push(String, Object, int, boolean), and ignores
 258   
      * character data within the element.
 259   
      * 
 260   
      * @param elementName the element whose start tag was just parsed
 261   
      * @param object the object created to represent the new object
 262   
      * @param state the new state for the parse
 263   
      */
 264  17955
     protected void push(String elementName, Object object, int state)
 265   
     {
 266  17955
         push(elementName, object, state, true);
 267   
     }
 268   
 
 269   
     /**
 270   
      * Enters a new state, pushing an object onto the stack. If the object
 271   
      * implements {@link ILocationHolder} then its location property
 272   
      * is set to the current location.
 273   
      * 
 274   
      * @param elementName the element whose start tag was just parsed
 275   
      * @param object the object created to represent the new object
 276   
      * @param state the new state for the parse
 277   
      * @param ignoreCharacterData if true, then any character data (typically whitespace)
 278   
      * directly enclosed by the element is ignored
 279   
      */
 280  20726
     protected void push(String elementName, Object object, int state, boolean ignoreCharacterData)
 281   
     {
 282  20726
         HiveMind.setLocation(object, getLocation());
 283   
 
 284  20726
         Item item = new Item(elementName, object, _state, ignoreCharacterData);
 285   
 
 286  20726
         _stack.add(item);
 287   
 
 288  20726
         _top = item;
 289  20726
         _state = state;
 290   
     }
 291   
 
 292   
     /**
 293   
      * Resets all state after a parse. 
 294   
      */
 295  177
     protected void resetParser()
 296   
     {
 297  177
         _resource = null;
 298  177
         _locator = null;
 299  177
         _stack = null;
 300  177
         _location = null;
 301   
     }
 302   
 
 303   
     /**
 304   
      * Invoked by the parser, the locator is stored and later used
 305   
      * by {@link #getLocation()}.
 306   
      */
 307  177
     public void setDocumentLocator(Locator locator)
 308   
     {
 309  177
         _locator = locator;
 310   
     }
 311   
 
 312   
     /**
 313   
      * Forces a change to a specific state.
 314   
      */
 315  0
     protected void setState(int state)
 316   
     {
 317  0
         _state = state;
 318   
     }
 319   
 
 320   
     /**
 321   
      * Invoked when an unexpected element is parsed (useful for
 322   
      * parses that don't perform validation, or when there's no DTD).
 323   
      * 
 324   
      * @throws ApplicationRuntimeException describing the situation
 325   
      */
 326  1
     protected void unexpectedElement(String elementName)
 327   
     {
 328  1
         throw new ApplicationRuntimeException(
 329   
             ParseMessages.unexpectedElement(elementName, getElementPath()),
 330   
             getLocation(),
 331   
             null);
 332   
     }
 333   
 
 334   
     /**
 335   
      * Ocassionaly it is necessary to "change our mind" about what's
 336   
      * on the top of the stack.
 337   
      * 
 338   
      * @param object the new object for the top stack element
 339   
      */
 340  0
     protected void updateObject(Object object)
 341   
     {
 342  0
         _top._object = object;
 343   
     }
 344   
 
 345   
     /**
 346   
      * Invokes {@link #fatalError(SAXParseException)},
 347   
      */
 348  0
     public void warning(SAXParseException ex) throws SAXException
 349   
     {
 350  0
         fatalError(ex);
 351   
     }
 352   
 
 353  20727
     private Map constructAttributesMap(Attributes attributes)
 354   
     {
 355  20727
         Map result = new HashMap();
 356  20727
         int count = attributes.getLength();
 357   
 
 358  20727
         for (int i = 0; i < count; i++)
 359   
         {
 360  29069
             String key = attributes.getLocalName(i);
 361   
 
 362  29069
             if (HiveMind.isBlank(key))
 363  1548
                 key = attributes.getQName(i);
 364   
 
 365  29069
             String value = attributes.getValue(i);
 366   
 
 367  29069
             result.put(key, value);
 368   
         }
 369   
 
 370  20727
         return result;
 371   
     }
 372   
 
 373   
     /**
 374   
      * Invoked when an element's start tag is recognized. The element and attributes are provided to the subclass
 375   
      * for further processing.
 376   
      */
 377   
     protected abstract void begin(String elementName, Map attributes);
 378   
 
 379   
     /**
 380   
      * Invoked when an element's close tag is recognized. The element is provided. The content of the
 381   
      * element (the unparsed whitespace within the element's tags) is available via
 382   
      * {@link #peekContent()}.
 383   
      */
 384   
 
 385   
     protected abstract void end(String elementName);
 386   
 
 387  20715
     public void endElement(String uri, String localName, String qName) throws SAXException
 388   
     {
 389  20715
         end(getElementName(localName, qName));
 390   
     }
 391   
 
 392  20727
     public void startElement(String uri, String localName, String qName, Attributes attributes)
 393   
         throws SAXException
 394   
     {
 395  20727
         String elementName = getElementName(localName, qName);
 396   
 
 397  20727
         begin(elementName, constructAttributesMap(attributes));
 398   
     }
 399   
 
 400  41442
     private String getElementName(String localName, String qName)
 401   
     {
 402  41442
         return qName != null ? qName : localName;
 403   
     }
 404   
 }
 405