Clover coverage report - QedeqKernelSe Coverage Report
Coverage timestamp: Sa Jan 26 2008 14:11:34 CET
file stats: LOC: 387   Methods: 19
NCLOC: 195   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
SaxDefaultHandler.java 77,8% 66,3% 78,9% 69,5%
coverage coverage
 1    /* $Id: SaxDefaultHandler.java,v 1.29 2008/01/26 12:39:10 m31 Exp $
 2    *
 3    * This file is part of the project "Hilbert II" - http://www.qedeq.org
 4    *
 5    * Copyright 2000-2007, Michael Meyling <mime@qedeq.org>.
 6    *
 7    * "Hilbert II" is free software; you can redistribute
 8    * it and/or modify it under the terms of the GNU General Public
 9    * License as published by the Free Software Foundation; either
 10    * version 2 of the License, or (at your option) any later version.
 11    *
 12    * This program is distributed in the hope that it will be useful,
 13    * but WITHOUT ANY WARRANTY; without even the implied warranty of
 14    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 15    * GNU General Public License for more details.
 16    */
 17    package org.qedeq.kernel.xml.parser;
 18   
 19    import java.net.URL;
 20    import java.util.Stack;
 21   
 22    import org.qedeq.kernel.common.SourceArea;
 23    import org.qedeq.kernel.common.SourceFileException;
 24    import org.qedeq.kernel.common.SourcePosition;
 25    import org.qedeq.kernel.trace.Trace;
 26    import org.qedeq.kernel.xml.common.XmlSyntaxException;
 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    /**
 35    * Default SAX handler. Delegates SAX events to a
 36    * {@link org.qedeq.kernel.xml.parser.AbstractSimpleHandler}
 37    * which could also delegate events to other
 38    * {@link org.qedeq.kernel.xml.parser.AbstractSimpleHandler}s.
 39    * <p>
 40    * Before anything is parsed the method {@link #setExceptionList(DefaultSourceFileExceptionList)}
 41    * must be called.
 42    *
 43    * @version $Revision: 1.29 $
 44    * @author Michael Meyling
 45    */
 46    public class SaxDefaultHandler extends DefaultHandler {
 47   
 48    /** This class. */
 49    private static final Class CLASS = SaxDefaultHandler.class;
 50   
 51    /** Delegate currently to this handler. */
 52    private AbstractSimpleHandler currentHandler;
 53   
 54    /** Stack of previous {@link AbstractSimpleHandler}s. */
 55    private Stack handlerStack = new Stack();
 56   
 57    /** Top level handler. This handler is activated after the begin of the document. */
 58    private AbstractSimpleHandler basisHandler;
 59   
 60    /** Collect errors in this object. */
 61    private DefaultSourceFileExceptionList errorList;
 62   
 63    /** Buffer for combining character events. */
 64    private StringBuffer buffer = new StringBuffer(2000);
 65   
 66    /** Locator for current row and column information. */
 67    private Locator locator;
 68   
 69    /** Tag level for current handler. */
 70    private int level;
 71   
 72    /** Tag level for previous handlers. */
 73    private Stack levelStack = new Stack();
 74   
 75    /** Current tag name. Could be <code>null</code>. */
 76    private String currentElementName;
 77   
 78    /** File that is parsed. */
 79    private URL url;
 80   
 81   
 82    /**
 83    * Constructor.
 84    */
 85  568 public SaxDefaultHandler() {
 86  568 super();
 87    }
 88   
 89    /**
 90    * Set parse exception list. This list collects occurring parsing errors.
 91    *
 92    * @param errorList Collect errors here.
 93    */
 94  553 public void setExceptionList(final DefaultSourceFileExceptionList errorList) {
 95  553 this.errorList = errorList;
 96    }
 97   
 98    /**
 99    * Receive a Locator object for document events.
 100    * Store the locator for use with other document events.
 101    *
 102    * @param locator A locator for all SAX document events.
 103    * @see org.xml.sax.ContentHandler#setDocumentLocator
 104    * @see org.xml.sax.Locator
 105    */
 106  553 public void setDocumentLocator(final Locator locator) {
 107  553 this.locator = locator;
 108    }
 109   
 110    /**
 111    * Set basis handler for documents.
 112    *
 113    * @param handler Basis handler for documents. This handler might also pass control to
 114    * another handler via the
 115    * {@link AbstractSimpleHandler#changeHandler(AbstractSimpleHandler, String, SimpleAttributes)}
 116    * method.
 117    */
 118  568 public final void setBasisDocumentHandler(final AbstractSimpleHandler handler) {
 119  568 basisHandler = handler;
 120  568 currentHandler = handler;
 121  568 handlerStack.clear();
 122  568 level = 0;
 123    }
 124   
 125    /* (non-Javadoc)
 126    * @see org.xml.sax.helpers.DefaultHandler#startDocument()
 127    */
 128  553 public final void startDocument() throws SAXException {
 129  553 sendCharacters();
 130  553 currentHandler = basisHandler;
 131  553 handlerStack.clear();
 132  553 level = 0;
 133  553 currentElementName = null;
 134    }
 135   
 136    /* (non-Javadoc)
 137    * @see org.xml.sax.helpers.DefaultHandler#endDocument()
 138    */
 139  553 public final void endDocument() throws SAXException {
 140  553 sendCharacters();
 141  553 currentElementName = null;
 142    }
 143   
 144    /* (non-Javadoc)
 145    * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String,
 146    * java.lang.String, org.xml.sax.Attributes)
 147    */
 148  82725 public final void startElement(final String uri, final String localName, final String qName,
 149    final Attributes amap) throws SAXException {
 150  82725 final String method = "startElement";
 151  82725 try {
 152  82724 Trace.param(CLASS, this, method, "currentHandler", currentHandler.getClass().getName());
 153  82726 Trace.param(CLASS, this, method, "localName", localName);
 154  82727 Trace.param(CLASS, this, method, "qName", qName);
 155  82726 if (handlerStack.empty() && level == 0) {
 156  553 currentHandler.init();
 157    }
 158  82727 level++;
 159  82727 Trace.param(CLASS, this, method, "level", level);
 160  82727 sendCharacters();
 161  82727 currentElementName = localName;
 162  82727 final SimpleAttributes attributes = new SimpleAttributes();
 163  82727 for (int i = 0; i < amap.getLength(); i++) {
 164  62156 attributes.add(amap.getQName(i), amap.getValue(i));
 165    }
 166  82727 Trace.param(CLASS, this, method, "attributes", attributes);
 167  82727 currentHandler.startElement(qName, attributes);
 168    } catch (XmlSyntaxException e) {
 169  0 Trace.trace(CLASS, this, method, e);
 170  0 setLocationInformation(e);
 171  0 errorList.add(new SourceFileException(e, createSourceArea(), null));
 172    } catch (RuntimeException e) {
 173  0 Trace.trace(CLASS, this, method, e);
 174  0 final XmlSyntaxException ex = XmlSyntaxException.createByRuntimeException(e);
 175  0 setLocationInformation(ex);
 176  0 errorList.add(ex);
 177    }
 178    }
 179   
 180    /* (non-Javadoc)
 181    * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String,
 182    * java.lang.String)
 183    */
 184  82726 public final void endElement(final String uri, final String localName, final String qName)
 185    throws SAXException {
 186  82726 sendCharacters();
 187  82727 final String method = "endElement";
 188  82727 try {
 189  82727 Trace.param(CLASS, this, method, "currentHandler", currentHandler.getClass().getName());
 190  82727 Trace.param(CLASS, this, method, "localName", localName);
 191  82727 currentHandler.endElement(localName);
 192    } catch (XmlSyntaxException e) {
 193  0 Trace.trace(CLASS, this, method, e);
 194  0 setLocationInformation(e);
 195  0 errorList.add(new SourceFileException(e, createSourceArea(), null));
 196    } catch (RuntimeException e) {
 197  0 Trace.trace(CLASS, this, method, e);
 198  0 final XmlSyntaxException ex = XmlSyntaxException.createByRuntimeException(e);
 199  0 setLocationInformation(ex);
 200  0 errorList.add(new SourceFileException(ex, createSourceArea(), null));
 201    }
 202  82726 try {
 203  82727 currentElementName = null;
 204  82727 level--;
 205  82727 Trace.param(CLASS, this, method, "level", level);
 206  82727 if (level <= 0) {
 207  25555 restoreHandler(localName);
 208    }
 209    } catch (XmlSyntaxException e) {
 210  0 Trace.trace(CLASS, this, method, e);
 211  0 setLocationInformation(e);
 212  0 errorList.add(e);
 213    } catch (RuntimeException e) {
 214  0 Trace.trace(CLASS, this, method, e);
 215  0 final XmlSyntaxException ex = XmlSyntaxException.createByRuntimeException(e);
 216  0 setLocationInformation(ex);
 217  0 errorList.add(new SourceFileException(ex, createSourceArea(), null));
 218    }
 219    }
 220   
 221    /* (non-Javadoc)
 222    * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
 223    */
 224  201682 public final void characters(final char[] ch, final int start, final int length) {
 225  201680 buffer.append(ch, start, length);
 226    }
 227   
 228    /**
 229    * Sends <code>characters</code> event to current handler.
 230    */
 231  166555 private void sendCharacters() {
 232  166555 try {
 233  166555 if (buffer.length() > 0) {
 234  142979 final String str = buffer.toString().trim();
 235  142981 buffer.setLength(0);
 236  142981 if (str.length() > 0) {
 237  18523 currentHandler.characters(currentElementName, str);
 238    }
 239    }
 240    } catch (XmlSyntaxException e) {
 241  0 Trace.trace(CLASS, this, "sendCharacters", e);
 242  0 setLocationInformation(e);
 243  0 errorList.add(new SourceFileException(e, createSourceArea(), null));
 244    } catch (RuntimeException e) {
 245  0 Trace.trace(CLASS, this, "sendCharacters", e);
 246  0 final XmlSyntaxException ex = XmlSyntaxException.createByRuntimeException(e);
 247  0 setLocationInformation(ex);
 248  0 errorList.add(new SourceFileException(ex, createSourceArea(), null));
 249    }
 250    }
 251   
 252    /**
 253    * Change current handler to new one. The new handler is initialized by calling
 254    * {@link AbstractSimpleHandler#init()}.
 255    * The new handler also gets a {@link AbstractSimpleHandler#startElement(String,
 256    * SimpleAttributes)} event.
 257    * The current handler is stacked. After the new handler gets the appropriate endElement
 258    * event, the control is switched back to the old handler.
 259    * <p>
 260    * The switch back is also done, if the tag level gets back to the same number. That means
 261    * if for example the new handler starts with the <code>&lt;banana&gt;</code> tag, the
 262    * old handler is restored when the misspelled <code>&lt;/bnana&gt</code> tag occurs:
 263    * <p>
 264    * <pre>
 265    * &lt;banana&gt;
 266    * &lt;one /&gt;
 267    * &lt;two &gt;
 268    * &lt;one /&gt;
 269    * &lt;one /&gt;
 270    * &lt;/two &gt;
 271    * &lt;/bnana&gt
 272    * </pre>
 273    *
 274    * @param newHandler This handler gets the new events.
 275    * @param elementName Element name.
 276    * @param attributes Element attributes.
 277    * @throws XmlSyntaxException New Handler detected a semantic problem.
 278    */
 279  25289 public final void changeHandler(final AbstractSimpleHandler newHandler,
 280    final String elementName, final SimpleAttributes attributes)
 281    throws XmlSyntaxException {
 282  25288 handlerStack.push(currentHandler);
 283  25289 levelStack.push(new Integer(level));
 284  25289 currentHandler = newHandler;
 285  25289 level = 0;
 286  25289 level++;
 287  25289 Trace.param(CLASS, this, "changeHandler", "level", level);
 288  25289 currentHandler.init();
 289  25289 currentHandler.startElement(elementName, attributes);
 290    }
 291   
 292    /**
 293    * Restore previous handler if there is any. An endElement event is also send to the restored
 294    * handler.
 295    *
 296    * @param elementName
 297    * @throws XmlSyntaxException
 298    */
 299  25555 private final void restoreHandler(final String elementName) throws XmlSyntaxException {
 300  25555 while (level <= 0 && !handlerStack.empty()) {
 301  25289 currentHandler = (AbstractSimpleHandler) handlerStack.pop();
 302  25289 Trace.param(CLASS, this, "restoreHandler", "currentHandler", currentHandler);
 303  25289 level = ((Integer) levelStack.pop()).intValue();
 304  25289 currentHandler.endElement(elementName);
 305  25288 level--;
 306  25288 Trace.param(CLASS, this, "restoreHandler", "level", level);
 307    }
 308  25555 if (handlerStack.empty()) {
 309  1602 Trace.trace(CLASS, this, "restoreHandler", "no handler to restore");
 310    }
 311    }
 312   
 313    /**
 314    * Get current level.
 315    *
 316    * @return Current level.
 317    */
 318  5978 public final int getLevel() {
 319  5978 return level;
 320    }
 321   
 322    /**
 323    * Wraps exception in new {@link SAXParseException} including parsing position information.
 324    *
 325    * @param e Exception to wrap.
 326    * @return Exception to throw.
 327    */
 328  0 public final SAXParseException createSAXParseException(final Exception e) {
 329  0 return new SAXParseException(null, locator, e);
 330    }
 331   
 332    /**
 333    * Creates new {@link SAXParseException} including parsing position information.
 334    *
 335    * @param message Problem description.
 336    * @return Exception to throw.
 337    */
 338  1 public final SAXParseException createSAXParseException(final String message) {
 339  1 return new SAXParseException(message, locator);
 340    }
 341   
 342    /**
 343    * Set current location information within an {@link XmlSyntaxException}.
 344    *
 345    * @param e Set location information within this exception.
 346    */
 347  0 private final void setLocationInformation(final XmlSyntaxException e) {
 348  0 if (locator != null && url != null) {
 349  0 e.setErrorPosition(new SourcePosition(url, locator.getLineNumber(),
 350    locator.getColumnNumber()));
 351    }
 352    }
 353   
 354    /**
 355    * Get current source area.
 356    *
 357    * @return Current area.
 358    */
 359  0 private final SourceArea createSourceArea() {
 360  0 if (locator != null && url != null) {
 361  0 return new SourceArea(url, new SourcePosition(url, locator.getLineNumber(),
 362    locator.getColumnNumber()), null);
 363    }
 364  0 return new SourceArea(url, null, null);
 365    }
 366   
 367    /**
 368    * Set original file URL.
 369    *
 370    * @param url Data from this source is parsed. This URL is only for information. The
 371    * actual parsed data might be a local copy.
 372    */
 373  553 public final void setUrl(final URL url) {
 374  553 this.url = url;
 375    }
 376   
 377    /**
 378    * Get original file URL.
 379    *
 380    * @return Data from this source is parsed. This URL is only for information. The
 381    * actual parsed data might be a local copy.
 382    */
 383  0 public final URL getUrl() {
 384  0 return url;
 385    }
 386   
 387    }