2009/04/15 - Apache HiveMind has been retired.

For more information, please explore the Attic.

Clover coverage report - Code Coverage for hivemind release 1.2.1
Coverage timestamp: Fri Feb 10 2006 16:33:43 PST
file stats: LOC: 417   Methods: 27
NCLOC: 199   Classes: 2
 
 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  42085 Item(String elementName, Object object, int priorState, boolean ignoreCharacterData)
 68    {
 69  42085 _elementName = elementName;
 70  42085 _object = object;
 71  42085 _priorState = priorState;
 72  42085 _ignoreCharacterData = ignoreCharacterData;
 73    }
 74   
 75  98061 void addContent(char[] buffer, int start, int length)
 76    {
 77  98061 if (_ignoreCharacterData)
 78  4277 return;
 79   
 80  93784 if (_buffer == null)
 81  19166 _buffer = new StringBuffer(length);
 82   
 83  93784 _buffer.append(buffer, start, length);
 84    }
 85   
 86  41556 String getContent()
 87    {
 88  41556 if (_buffer != null)
 89  19157 return _buffer.toString().trim();
 90   
 91  22399 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  98061 public void characters(char[] ch, int start, int length) throws SAXException
 117    {
 118  98061 _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  42093 protected Location getLocation()
 165    {
 166  42093 int line = _locator.getLineNumber();
 167  42093 int column = _locator.getColumnNumber();
 168   
 169  42093 if (line != _currentLine || column != _currentColumn)
 170  42086 _location = null;
 171   
 172  42093 if (_location == null)
 173  42086 _location = new LocationImpl(_resource, line, column);
 174   
 175  42093 _currentLine = line;
 176  42093 _currentColumn = column;
 177   
 178  42093 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)} and {@link #pop()}.
 194    */
 195  84160 protected int getState()
 196    {
 197  84160 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  285 protected void initializeParser(Resource resource, int startState)
 210    {
 211  285 _resource = resource;
 212  285 _stack = new ArrayList();
 213   
 214  285 _location = null;
 215  285 _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  41556 protected String peekContent()
 223    {
 224  41556 return _top.getContent();
 225    }
 226   
 227    /**
 228    * Peeks at the top element on the stack and returns its element name.
 229    */
 230  32466 protected String peekElementName()
 231    {
 232  32466 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  82324 protected Object peekObject()
 240    {
 241  82324 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  42074 protected void pop()
 250    {
 251  42074 int count = _stack.size();
 252   
 253  42074 _state = _top._priorState;
 254   
 255  42074 _stack.remove(count - 1);
 256   
 257  42074 if (count == 1)
 258  280 _top = null;
 259    else
 260  41794 _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} 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  42085 protected void push(String elementName, Object object, int state, boolean ignoreCharacterData)
 295    {
 296  42085 HiveMind.setLocation(object, getLocation());
 297   
 298  42085 Item item = new Item(elementName, object, _state, ignoreCharacterData);
 299   
 300  42085 _stack.add(item);
 301   
 302  42085 _top = item;
 303  42085 _state = state;
 304    }
 305   
 306    /**
 307    * Resets all state after a parse.
 308    */
 309  285 protected void resetParser()
 310    {
 311  285 _resource = null;
 312  285 _locator = null;
 313  285 _stack = null;
 314  285 _location = null;
 315    }
 316   
 317    /**
 318    * Invoked by the parser, the locator is stored and later used by {@link #getLocation()}.
 319    */
 320  283 public void setDocumentLocator(Locator locator)
 321    {
 322  283 _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  42086 private Map constructAttributesMap(Attributes attributes)
 367    {
 368  42086 Map result = new HashMap();
 369  42086 int count = attributes.getLength();
 370   
 371  42086 for (int i = 0; i < count; i++)
 372    {
 373  59880 String key = attributes.getLocalName(i);
 374   
 375  59880 if (HiveMind.isBlank(key))
 376  59880 key = attributes.getQName(i);
 377   
 378  59880 String value = attributes.getValue(i);
 379   
 380  59880 result.put(key, value);
 381    }
 382   
 383  42086 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  42074 public void endElement(String uri, String localName, String qName) throws SAXException
 401    {
 402  42074 end(getElementName(localName, qName));
 403    }
 404   
 405  42086 public void startElement(String uri, String localName, String qName, Attributes attributes)
 406    throws SAXException
 407    {
 408  42086 String elementName = getElementName(localName, qName);
 409   
 410  42086 begin(elementName, constructAttributesMap(attributes));
 411    }
 412   
 413  84160 private String getElementName(String localName, String qName)
 414    {
 415  84160 return qName != null ? qName : localName;
 416    }
 417    }