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