103928aee4356845252ac6b662d5c72c29903813eJake Slack//
203928aee4356845252ac6b662d5c72c29903813eJake Slack//  ========================================================================
303928aee4356845252ac6b662d5c72c29903813eJake Slack//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
403928aee4356845252ac6b662d5c72c29903813eJake Slack//  ------------------------------------------------------------------------
503928aee4356845252ac6b662d5c72c29903813eJake Slack//  All rights reserved. This program and the accompanying materials
603928aee4356845252ac6b662d5c72c29903813eJake Slack//  are made available under the terms of the Eclipse Public License v1.0
703928aee4356845252ac6b662d5c72c29903813eJake Slack//  and Apache License v2.0 which accompanies this distribution.
803928aee4356845252ac6b662d5c72c29903813eJake Slack//
903928aee4356845252ac6b662d5c72c29903813eJake Slack//      The Eclipse Public License is available at
1003928aee4356845252ac6b662d5c72c29903813eJake Slack//      http://www.eclipse.org/legal/epl-v10.html
1103928aee4356845252ac6b662d5c72c29903813eJake Slack//
1203928aee4356845252ac6b662d5c72c29903813eJake Slack//      The Apache License v2.0 is available at
1303928aee4356845252ac6b662d5c72c29903813eJake Slack//      http://www.opensource.org/licenses/apache2.0.php
1403928aee4356845252ac6b662d5c72c29903813eJake Slack//
1503928aee4356845252ac6b662d5c72c29903813eJake Slack//  You may elect to redistribute this code under either of these licenses.
1603928aee4356845252ac6b662d5c72c29903813eJake Slack//  ========================================================================
1703928aee4356845252ac6b662d5c72c29903813eJake Slack//
1803928aee4356845252ac6b662d5c72c29903813eJake Slack
1903928aee4356845252ac6b662d5c72c29903813eJake Slackpackage org.eclipse.jetty.xml;
2003928aee4356845252ac6b662d5c72c29903813eJake Slack
2103928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.io.File;
2203928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.io.IOException;
2303928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.io.InputStream;
2403928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.net.URL;
2503928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.AbstractList;
2603928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.ArrayList;
2703928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.HashMap;
2803928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.Iterator;
2903928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.Map;
3003928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.NoSuchElementException;
3103928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.Stack;
3203928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.StringTokenizer;
3303928aee4356845252ac6b662d5c72c29903813eJake Slack
3403928aee4356845252ac6b662d5c72c29903813eJake Slackimport javax.xml.parsers.SAXParser;
3503928aee4356845252ac6b662d5c72c29903813eJake Slackimport javax.xml.parsers.SAXParserFactory;
3603928aee4356845252ac6b662d5c72c29903813eJake Slack
3703928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.LazyList;
3803928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.log.Log;
3903928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.log.Logger;
4003928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.resource.Resource;
4103928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.xml.sax.Attributes;
4203928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.xml.sax.ContentHandler;
4303928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.xml.sax.InputSource;
4403928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.xml.sax.SAXException;
4503928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.xml.sax.SAXParseException;
4603928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.xml.sax.XMLReader;
4703928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.xml.sax.helpers.DefaultHandler;
4803928aee4356845252ac6b662d5c72c29903813eJake Slack
4903928aee4356845252ac6b662d5c72c29903813eJake Slack/*--------------------------------------------------------------*/
5003928aee4356845252ac6b662d5c72c29903813eJake Slack/**
5103928aee4356845252ac6b662d5c72c29903813eJake Slack * XML Parser wrapper. This class wraps any standard JAXP1.1 parser with convieniant error and
5203928aee4356845252ac6b662d5c72c29903813eJake Slack * entity handlers and a mini dom-like document tree.
5303928aee4356845252ac6b662d5c72c29903813eJake Slack * <P>
5403928aee4356845252ac6b662d5c72c29903813eJake Slack * By default, the parser is created as a validating parser only if xerces is present. This can be
5503928aee4356845252ac6b662d5c72c29903813eJake Slack * configured by setting the "org.eclipse.jetty.xml.XmlParser.Validating" system property.
5603928aee4356845252ac6b662d5c72c29903813eJake Slack *
5703928aee4356845252ac6b662d5c72c29903813eJake Slack *
5803928aee4356845252ac6b662d5c72c29903813eJake Slack */
5903928aee4356845252ac6b662d5c72c29903813eJake Slackpublic class XmlParser
6003928aee4356845252ac6b662d5c72c29903813eJake Slack{
6103928aee4356845252ac6b662d5c72c29903813eJake Slack    private static final Logger LOG = Log.getLogger(XmlParser.class);
6203928aee4356845252ac6b662d5c72c29903813eJake Slack
6303928aee4356845252ac6b662d5c72c29903813eJake Slack    private Map<String,URL> _redirectMap = new HashMap<String,URL>();
6403928aee4356845252ac6b662d5c72c29903813eJake Slack    private SAXParser _parser;
6503928aee4356845252ac6b662d5c72c29903813eJake Slack    private Map<String,ContentHandler> _observerMap;
6603928aee4356845252ac6b662d5c72c29903813eJake Slack    private Stack<ContentHandler> _observers = new Stack<ContentHandler>();
6703928aee4356845252ac6b662d5c72c29903813eJake Slack    private String _xpath;
6803928aee4356845252ac6b662d5c72c29903813eJake Slack    private Object _xpaths;
6903928aee4356845252ac6b662d5c72c29903813eJake Slack    private String _dtd;
7003928aee4356845252ac6b662d5c72c29903813eJake Slack
7103928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
7203928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
7303928aee4356845252ac6b662d5c72c29903813eJake Slack     * Construct
7403928aee4356845252ac6b662d5c72c29903813eJake Slack     */
7503928aee4356845252ac6b662d5c72c29903813eJake Slack    public XmlParser()
7603928aee4356845252ac6b662d5c72c29903813eJake Slack    {
7703928aee4356845252ac6b662d5c72c29903813eJake Slack        SAXParserFactory factory = SAXParserFactory.newInstance();
7803928aee4356845252ac6b662d5c72c29903813eJake Slack        boolean validating_dft = factory.getClass().toString().startsWith("org.apache.xerces.");
7903928aee4356845252ac6b662d5c72c29903813eJake Slack        String validating_prop = System.getProperty("org.eclipse.jetty.xml.XmlParser.Validating", validating_dft ? "true" : "false");
8003928aee4356845252ac6b662d5c72c29903813eJake Slack        boolean validating = Boolean.valueOf(validating_prop).booleanValue();
8103928aee4356845252ac6b662d5c72c29903813eJake Slack        setValidating(validating);
8203928aee4356845252ac6b662d5c72c29903813eJake Slack    }
8303928aee4356845252ac6b662d5c72c29903813eJake Slack
8403928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
8503928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
8603928aee4356845252ac6b662d5c72c29903813eJake Slack     * Constructor.
8703928aee4356845252ac6b662d5c72c29903813eJake Slack     */
8803928aee4356845252ac6b662d5c72c29903813eJake Slack    public XmlParser(boolean validating)
8903928aee4356845252ac6b662d5c72c29903813eJake Slack    {
9003928aee4356845252ac6b662d5c72c29903813eJake Slack        setValidating(validating);
9103928aee4356845252ac6b662d5c72c29903813eJake Slack    }
9203928aee4356845252ac6b662d5c72c29903813eJake Slack
9303928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
9403928aee4356845252ac6b662d5c72c29903813eJake Slack    public void setValidating(boolean validating)
9503928aee4356845252ac6b662d5c72c29903813eJake Slack    {
9603928aee4356845252ac6b662d5c72c29903813eJake Slack        try
9703928aee4356845252ac6b662d5c72c29903813eJake Slack        {
9803928aee4356845252ac6b662d5c72c29903813eJake Slack            SAXParserFactory factory = SAXParserFactory.newInstance();
9903928aee4356845252ac6b662d5c72c29903813eJake Slack            factory.setValidating(validating);
10003928aee4356845252ac6b662d5c72c29903813eJake Slack            _parser = factory.newSAXParser();
10103928aee4356845252ac6b662d5c72c29903813eJake Slack
10203928aee4356845252ac6b662d5c72c29903813eJake Slack            try
10303928aee4356845252ac6b662d5c72c29903813eJake Slack            {
10403928aee4356845252ac6b662d5c72c29903813eJake Slack                if (validating)
10503928aee4356845252ac6b662d5c72c29903813eJake Slack                    _parser.getXMLReader().setFeature("http://apache.org/xml/features/validation/schema", validating);
10603928aee4356845252ac6b662d5c72c29903813eJake Slack            }
10703928aee4356845252ac6b662d5c72c29903813eJake Slack            catch (Exception e)
10803928aee4356845252ac6b662d5c72c29903813eJake Slack            {
10903928aee4356845252ac6b662d5c72c29903813eJake Slack                if (validating)
11003928aee4356845252ac6b662d5c72c29903813eJake Slack                    LOG.warn("Schema validation may not be supported: ", e);
11103928aee4356845252ac6b662d5c72c29903813eJake Slack                else
11203928aee4356845252ac6b662d5c72c29903813eJake Slack                    LOG.ignore(e);
11303928aee4356845252ac6b662d5c72c29903813eJake Slack            }
11403928aee4356845252ac6b662d5c72c29903813eJake Slack
11503928aee4356845252ac6b662d5c72c29903813eJake Slack            _parser.getXMLReader().setFeature("http://xml.org/sax/features/validation", validating);
11603928aee4356845252ac6b662d5c72c29903813eJake Slack            _parser.getXMLReader().setFeature("http://xml.org/sax/features/namespaces", true);
11703928aee4356845252ac6b662d5c72c29903813eJake Slack            _parser.getXMLReader().setFeature("http://xml.org/sax/features/namespace-prefixes", false);
11803928aee4356845252ac6b662d5c72c29903813eJake Slack            try
11903928aee4356845252ac6b662d5c72c29903813eJake Slack            {
12003928aee4356845252ac6b662d5c72c29903813eJake Slack                if (validating)
12103928aee4356845252ac6b662d5c72c29903813eJake Slack                    _parser.getXMLReader().setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", validating);
12203928aee4356845252ac6b662d5c72c29903813eJake Slack            }
12303928aee4356845252ac6b662d5c72c29903813eJake Slack            catch (Exception e)
12403928aee4356845252ac6b662d5c72c29903813eJake Slack            {
12503928aee4356845252ac6b662d5c72c29903813eJake Slack                LOG.warn(e.getMessage());
12603928aee4356845252ac6b662d5c72c29903813eJake Slack            }
12703928aee4356845252ac6b662d5c72c29903813eJake Slack        }
12803928aee4356845252ac6b662d5c72c29903813eJake Slack        catch (Exception e)
12903928aee4356845252ac6b662d5c72c29903813eJake Slack        {
13003928aee4356845252ac6b662d5c72c29903813eJake Slack            LOG.warn(Log.EXCEPTION, e);
13103928aee4356845252ac6b662d5c72c29903813eJake Slack            throw new Error(e.toString());
13203928aee4356845252ac6b662d5c72c29903813eJake Slack        }
13303928aee4356845252ac6b662d5c72c29903813eJake Slack    }
13403928aee4356845252ac6b662d5c72c29903813eJake Slack
13503928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
13603928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
13703928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param name
13803928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param entity
13903928aee4356845252ac6b662d5c72c29903813eJake Slack     */
14003928aee4356845252ac6b662d5c72c29903813eJake Slack    public synchronized void redirectEntity(String name, URL entity)
14103928aee4356845252ac6b662d5c72c29903813eJake Slack    {
14203928aee4356845252ac6b662d5c72c29903813eJake Slack        if (entity != null)
14303928aee4356845252ac6b662d5c72c29903813eJake Slack            _redirectMap.put(name, entity);
14403928aee4356845252ac6b662d5c72c29903813eJake Slack    }
14503928aee4356845252ac6b662d5c72c29903813eJake Slack
14603928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
14703928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
14803928aee4356845252ac6b662d5c72c29903813eJake Slack     *
14903928aee4356845252ac6b662d5c72c29903813eJake Slack     * @return Returns the xpath.
15003928aee4356845252ac6b662d5c72c29903813eJake Slack     */
15103928aee4356845252ac6b662d5c72c29903813eJake Slack    public String getXpath()
15203928aee4356845252ac6b662d5c72c29903813eJake Slack    {
15303928aee4356845252ac6b662d5c72c29903813eJake Slack        return _xpath;
15403928aee4356845252ac6b662d5c72c29903813eJake Slack    }
15503928aee4356845252ac6b662d5c72c29903813eJake Slack
15603928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
15703928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
15803928aee4356845252ac6b662d5c72c29903813eJake Slack     * Set an XPath A very simple subset of xpath is supported to select a partial tree. Currently
15903928aee4356845252ac6b662d5c72c29903813eJake Slack     * only path like "/node1/nodeA | /node1/nodeB" are supported.
16003928aee4356845252ac6b662d5c72c29903813eJake Slack     *
16103928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param xpath The xpath to set.
16203928aee4356845252ac6b662d5c72c29903813eJake Slack     */
16303928aee4356845252ac6b662d5c72c29903813eJake Slack    public void setXpath(String xpath)
16403928aee4356845252ac6b662d5c72c29903813eJake Slack    {
16503928aee4356845252ac6b662d5c72c29903813eJake Slack        _xpath = xpath;
16603928aee4356845252ac6b662d5c72c29903813eJake Slack        StringTokenizer tok = new StringTokenizer(xpath, "| ");
16703928aee4356845252ac6b662d5c72c29903813eJake Slack        while (tok.hasMoreTokens())
16803928aee4356845252ac6b662d5c72c29903813eJake Slack            _xpaths = LazyList.add(_xpaths, tok.nextToken());
16903928aee4356845252ac6b662d5c72c29903813eJake Slack    }
17003928aee4356845252ac6b662d5c72c29903813eJake Slack
17103928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
17203928aee4356845252ac6b662d5c72c29903813eJake Slack    public String getDTD()
17303928aee4356845252ac6b662d5c72c29903813eJake Slack    {
17403928aee4356845252ac6b662d5c72c29903813eJake Slack        return _dtd;
17503928aee4356845252ac6b662d5c72c29903813eJake Slack    }
17603928aee4356845252ac6b662d5c72c29903813eJake Slack
17703928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
17803928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
17903928aee4356845252ac6b662d5c72c29903813eJake Slack     * Add a ContentHandler. Add an additional _content handler that is triggered on a tag name. SAX
18003928aee4356845252ac6b662d5c72c29903813eJake Slack     * events are passed to the ContentHandler provided from a matching start element to the
18103928aee4356845252ac6b662d5c72c29903813eJake Slack     * corresponding end element. Only a single _content handler can be registered against each tag.
18203928aee4356845252ac6b662d5c72c29903813eJake Slack     *
18303928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param trigger Tag local or q name.
18403928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param observer SAX ContentHandler
18503928aee4356845252ac6b662d5c72c29903813eJake Slack     */
18603928aee4356845252ac6b662d5c72c29903813eJake Slack    public synchronized void addContentHandler(String trigger, ContentHandler observer)
18703928aee4356845252ac6b662d5c72c29903813eJake Slack    {
18803928aee4356845252ac6b662d5c72c29903813eJake Slack        if (_observerMap == null)
18903928aee4356845252ac6b662d5c72c29903813eJake Slack            _observerMap = new HashMap();
19003928aee4356845252ac6b662d5c72c29903813eJake Slack        _observerMap.put(trigger, observer);
19103928aee4356845252ac6b662d5c72c29903813eJake Slack    }
19203928aee4356845252ac6b662d5c72c29903813eJake Slack
19303928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
19403928aee4356845252ac6b662d5c72c29903813eJake Slack    public synchronized Node parse(InputSource source) throws IOException, SAXException
19503928aee4356845252ac6b662d5c72c29903813eJake Slack    {
19603928aee4356845252ac6b662d5c72c29903813eJake Slack        _dtd=null;
19703928aee4356845252ac6b662d5c72c29903813eJake Slack        Handler handler = new Handler();
19803928aee4356845252ac6b662d5c72c29903813eJake Slack        XMLReader reader = _parser.getXMLReader();
19903928aee4356845252ac6b662d5c72c29903813eJake Slack        reader.setContentHandler(handler);
20003928aee4356845252ac6b662d5c72c29903813eJake Slack        reader.setErrorHandler(handler);
20103928aee4356845252ac6b662d5c72c29903813eJake Slack        reader.setEntityResolver(handler);
20203928aee4356845252ac6b662d5c72c29903813eJake Slack        if (LOG.isDebugEnabled())
20303928aee4356845252ac6b662d5c72c29903813eJake Slack            LOG.debug("parsing: sid=" + source.getSystemId() + ",pid=" + source.getPublicId());
20403928aee4356845252ac6b662d5c72c29903813eJake Slack        _parser.parse(source, handler);
20503928aee4356845252ac6b662d5c72c29903813eJake Slack        if (handler._error != null)
20603928aee4356845252ac6b662d5c72c29903813eJake Slack            throw handler._error;
20703928aee4356845252ac6b662d5c72c29903813eJake Slack        Node doc = (Node) handler._top.get(0);
20803928aee4356845252ac6b662d5c72c29903813eJake Slack        handler.clear();
20903928aee4356845252ac6b662d5c72c29903813eJake Slack        return doc;
21003928aee4356845252ac6b662d5c72c29903813eJake Slack    }
21103928aee4356845252ac6b662d5c72c29903813eJake Slack
21203928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
21303928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
21403928aee4356845252ac6b662d5c72c29903813eJake Slack     * Parse String URL.
21503928aee4356845252ac6b662d5c72c29903813eJake Slack     */
21603928aee4356845252ac6b662d5c72c29903813eJake Slack    public synchronized Node parse(String url) throws IOException, SAXException
21703928aee4356845252ac6b662d5c72c29903813eJake Slack    {
21803928aee4356845252ac6b662d5c72c29903813eJake Slack        if (LOG.isDebugEnabled())
21903928aee4356845252ac6b662d5c72c29903813eJake Slack            LOG.debug("parse: " + url);
22003928aee4356845252ac6b662d5c72c29903813eJake Slack        return parse(new InputSource(url));
22103928aee4356845252ac6b662d5c72c29903813eJake Slack    }
22203928aee4356845252ac6b662d5c72c29903813eJake Slack
22303928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
22403928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
22503928aee4356845252ac6b662d5c72c29903813eJake Slack     * Parse File.
22603928aee4356845252ac6b662d5c72c29903813eJake Slack     */
22703928aee4356845252ac6b662d5c72c29903813eJake Slack    public synchronized Node parse(File file) throws IOException, SAXException
22803928aee4356845252ac6b662d5c72c29903813eJake Slack    {
22903928aee4356845252ac6b662d5c72c29903813eJake Slack        if (LOG.isDebugEnabled())
23003928aee4356845252ac6b662d5c72c29903813eJake Slack            LOG.debug("parse: " + file);
23103928aee4356845252ac6b662d5c72c29903813eJake Slack        return parse(new InputSource(Resource.toURL(file).toString()));
23203928aee4356845252ac6b662d5c72c29903813eJake Slack    }
23303928aee4356845252ac6b662d5c72c29903813eJake Slack
23403928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
23503928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
23603928aee4356845252ac6b662d5c72c29903813eJake Slack     * Parse InputStream.
23703928aee4356845252ac6b662d5c72c29903813eJake Slack     */
23803928aee4356845252ac6b662d5c72c29903813eJake Slack    public synchronized Node parse(InputStream in) throws IOException, SAXException
23903928aee4356845252ac6b662d5c72c29903813eJake Slack    {
24003928aee4356845252ac6b662d5c72c29903813eJake Slack        _dtd=null;
24103928aee4356845252ac6b662d5c72c29903813eJake Slack        Handler handler = new Handler();
24203928aee4356845252ac6b662d5c72c29903813eJake Slack        XMLReader reader = _parser.getXMLReader();
24303928aee4356845252ac6b662d5c72c29903813eJake Slack        reader.setContentHandler(handler);
24403928aee4356845252ac6b662d5c72c29903813eJake Slack        reader.setErrorHandler(handler);
24503928aee4356845252ac6b662d5c72c29903813eJake Slack        reader.setEntityResolver(handler);
24603928aee4356845252ac6b662d5c72c29903813eJake Slack        _parser.parse(new InputSource(in), handler);
24703928aee4356845252ac6b662d5c72c29903813eJake Slack        if (handler._error != null)
24803928aee4356845252ac6b662d5c72c29903813eJake Slack            throw handler._error;
24903928aee4356845252ac6b662d5c72c29903813eJake Slack        Node doc = (Node) handler._top.get(0);
25003928aee4356845252ac6b662d5c72c29903813eJake Slack        handler.clear();
25103928aee4356845252ac6b662d5c72c29903813eJake Slack        return doc;
25203928aee4356845252ac6b662d5c72c29903813eJake Slack    }
25303928aee4356845252ac6b662d5c72c29903813eJake Slack
25403928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
25503928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
25603928aee4356845252ac6b662d5c72c29903813eJake Slack    private class NoopHandler extends DefaultHandler
25703928aee4356845252ac6b662d5c72c29903813eJake Slack    {
25803928aee4356845252ac6b662d5c72c29903813eJake Slack        Handler _next;
25903928aee4356845252ac6b662d5c72c29903813eJake Slack        int _depth;
26003928aee4356845252ac6b662d5c72c29903813eJake Slack
26103928aee4356845252ac6b662d5c72c29903813eJake Slack        NoopHandler(Handler next)
26203928aee4356845252ac6b662d5c72c29903813eJake Slack        {
26303928aee4356845252ac6b662d5c72c29903813eJake Slack            this._next = next;
26403928aee4356845252ac6b662d5c72c29903813eJake Slack        }
26503928aee4356845252ac6b662d5c72c29903813eJake Slack
26603928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
26703928aee4356845252ac6b662d5c72c29903813eJake Slack        public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException
26803928aee4356845252ac6b662d5c72c29903813eJake Slack        {
26903928aee4356845252ac6b662d5c72c29903813eJake Slack            _depth++;
27003928aee4356845252ac6b662d5c72c29903813eJake Slack        }
27103928aee4356845252ac6b662d5c72c29903813eJake Slack
27203928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
27303928aee4356845252ac6b662d5c72c29903813eJake Slack        public void endElement(String uri, String localName, String qName) throws SAXException
27403928aee4356845252ac6b662d5c72c29903813eJake Slack        {
27503928aee4356845252ac6b662d5c72c29903813eJake Slack            if (_depth == 0)
27603928aee4356845252ac6b662d5c72c29903813eJake Slack                _parser.getXMLReader().setContentHandler(_next);
27703928aee4356845252ac6b662d5c72c29903813eJake Slack            else
27803928aee4356845252ac6b662d5c72c29903813eJake Slack                _depth--;
27903928aee4356845252ac6b662d5c72c29903813eJake Slack        }
28003928aee4356845252ac6b662d5c72c29903813eJake Slack    }
28103928aee4356845252ac6b662d5c72c29903813eJake Slack
28203928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
28303928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
28403928aee4356845252ac6b662d5c72c29903813eJake Slack    private class Handler extends DefaultHandler
28503928aee4356845252ac6b662d5c72c29903813eJake Slack    {
28603928aee4356845252ac6b662d5c72c29903813eJake Slack        Node _top = new Node(null, null, null);
28703928aee4356845252ac6b662d5c72c29903813eJake Slack        SAXParseException _error;
28803928aee4356845252ac6b662d5c72c29903813eJake Slack        private Node _context = _top;
28903928aee4356845252ac6b662d5c72c29903813eJake Slack        private NoopHandler _noop;
29003928aee4356845252ac6b662d5c72c29903813eJake Slack
29103928aee4356845252ac6b662d5c72c29903813eJake Slack        Handler()
29203928aee4356845252ac6b662d5c72c29903813eJake Slack        {
29303928aee4356845252ac6b662d5c72c29903813eJake Slack            _noop = new NoopHandler(this);
29403928aee4356845252ac6b662d5c72c29903813eJake Slack        }
29503928aee4356845252ac6b662d5c72c29903813eJake Slack
29603928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
29703928aee4356845252ac6b662d5c72c29903813eJake Slack        void clear()
29803928aee4356845252ac6b662d5c72c29903813eJake Slack        {
29903928aee4356845252ac6b662d5c72c29903813eJake Slack            _top = null;
30003928aee4356845252ac6b662d5c72c29903813eJake Slack            _error = null;
30103928aee4356845252ac6b662d5c72c29903813eJake Slack            _context = null;
30203928aee4356845252ac6b662d5c72c29903813eJake Slack        }
30303928aee4356845252ac6b662d5c72c29903813eJake Slack
30403928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
30503928aee4356845252ac6b662d5c72c29903813eJake Slack        public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException
30603928aee4356845252ac6b662d5c72c29903813eJake Slack        {
30703928aee4356845252ac6b662d5c72c29903813eJake Slack            String name = null;
30803928aee4356845252ac6b662d5c72c29903813eJake Slack            if (_parser.isNamespaceAware())
30903928aee4356845252ac6b662d5c72c29903813eJake Slack                name = localName;
31003928aee4356845252ac6b662d5c72c29903813eJake Slack
31103928aee4356845252ac6b662d5c72c29903813eJake Slack            if (name == null || "".equals(name))
31203928aee4356845252ac6b662d5c72c29903813eJake Slack                name = qName;
31303928aee4356845252ac6b662d5c72c29903813eJake Slack
31403928aee4356845252ac6b662d5c72c29903813eJake Slack            Node node = new Node(_context, name, attrs);
31503928aee4356845252ac6b662d5c72c29903813eJake Slack
31603928aee4356845252ac6b662d5c72c29903813eJake Slack
31703928aee4356845252ac6b662d5c72c29903813eJake Slack            // check if the node matches any xpaths set?
31803928aee4356845252ac6b662d5c72c29903813eJake Slack            if (_xpaths != null)
31903928aee4356845252ac6b662d5c72c29903813eJake Slack            {
32003928aee4356845252ac6b662d5c72c29903813eJake Slack                String path = node.getPath();
32103928aee4356845252ac6b662d5c72c29903813eJake Slack                boolean match = false;
32203928aee4356845252ac6b662d5c72c29903813eJake Slack                for (int i = LazyList.size(_xpaths); !match && i-- > 0;)
32303928aee4356845252ac6b662d5c72c29903813eJake Slack                {
32403928aee4356845252ac6b662d5c72c29903813eJake Slack                    String xpath = (String) LazyList.get(_xpaths, i);
32503928aee4356845252ac6b662d5c72c29903813eJake Slack
32603928aee4356845252ac6b662d5c72c29903813eJake Slack                    match = path.equals(xpath) || xpath.startsWith(path) && xpath.length() > path.length() && xpath.charAt(path.length()) == '/';
32703928aee4356845252ac6b662d5c72c29903813eJake Slack                }
32803928aee4356845252ac6b662d5c72c29903813eJake Slack
32903928aee4356845252ac6b662d5c72c29903813eJake Slack                if (match)
33003928aee4356845252ac6b662d5c72c29903813eJake Slack                {
33103928aee4356845252ac6b662d5c72c29903813eJake Slack                    _context.add(node);
33203928aee4356845252ac6b662d5c72c29903813eJake Slack                    _context = node;
33303928aee4356845252ac6b662d5c72c29903813eJake Slack                }
33403928aee4356845252ac6b662d5c72c29903813eJake Slack                else
33503928aee4356845252ac6b662d5c72c29903813eJake Slack                {
33603928aee4356845252ac6b662d5c72c29903813eJake Slack                    _parser.getXMLReader().setContentHandler(_noop);
33703928aee4356845252ac6b662d5c72c29903813eJake Slack                }
33803928aee4356845252ac6b662d5c72c29903813eJake Slack            }
33903928aee4356845252ac6b662d5c72c29903813eJake Slack            else
34003928aee4356845252ac6b662d5c72c29903813eJake Slack            {
34103928aee4356845252ac6b662d5c72c29903813eJake Slack                _context.add(node);
34203928aee4356845252ac6b662d5c72c29903813eJake Slack                _context = node;
34303928aee4356845252ac6b662d5c72c29903813eJake Slack            }
34403928aee4356845252ac6b662d5c72c29903813eJake Slack
34503928aee4356845252ac6b662d5c72c29903813eJake Slack            ContentHandler observer = null;
34603928aee4356845252ac6b662d5c72c29903813eJake Slack            if (_observerMap != null)
34703928aee4356845252ac6b662d5c72c29903813eJake Slack                observer = (ContentHandler) _observerMap.get(name);
34803928aee4356845252ac6b662d5c72c29903813eJake Slack            _observers.push(observer);
34903928aee4356845252ac6b662d5c72c29903813eJake Slack
35003928aee4356845252ac6b662d5c72c29903813eJake Slack            for (int i = 0; i < _observers.size(); i++)
35103928aee4356845252ac6b662d5c72c29903813eJake Slack                if (_observers.get(i) != null)
35203928aee4356845252ac6b662d5c72c29903813eJake Slack                    ((ContentHandler) _observers.get(i)).startElement(uri, localName, qName, attrs);
35303928aee4356845252ac6b662d5c72c29903813eJake Slack        }
35403928aee4356845252ac6b662d5c72c29903813eJake Slack
35503928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
35603928aee4356845252ac6b662d5c72c29903813eJake Slack        public void endElement(String uri, String localName, String qName) throws SAXException
35703928aee4356845252ac6b662d5c72c29903813eJake Slack        {
35803928aee4356845252ac6b662d5c72c29903813eJake Slack            _context = _context._parent;
35903928aee4356845252ac6b662d5c72c29903813eJake Slack            for (int i = 0; i < _observers.size(); i++)
36003928aee4356845252ac6b662d5c72c29903813eJake Slack                if (_observers.get(i) != null)
36103928aee4356845252ac6b662d5c72c29903813eJake Slack                    ((ContentHandler) _observers.get(i)).endElement(uri, localName, qName);
36203928aee4356845252ac6b662d5c72c29903813eJake Slack            _observers.pop();
36303928aee4356845252ac6b662d5c72c29903813eJake Slack        }
36403928aee4356845252ac6b662d5c72c29903813eJake Slack
36503928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
36603928aee4356845252ac6b662d5c72c29903813eJake Slack        public void ignorableWhitespace(char buf[], int offset, int len) throws SAXException
36703928aee4356845252ac6b662d5c72c29903813eJake Slack        {
36803928aee4356845252ac6b662d5c72c29903813eJake Slack            for (int i = 0; i < _observers.size(); i++)
36903928aee4356845252ac6b662d5c72c29903813eJake Slack                if (_observers.get(i) != null)
37003928aee4356845252ac6b662d5c72c29903813eJake Slack                    ((ContentHandler) _observers.get(i)).ignorableWhitespace(buf, offset, len);
37103928aee4356845252ac6b662d5c72c29903813eJake Slack        }
37203928aee4356845252ac6b662d5c72c29903813eJake Slack
37303928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
37403928aee4356845252ac6b662d5c72c29903813eJake Slack        public void characters(char buf[], int offset, int len) throws SAXException
37503928aee4356845252ac6b662d5c72c29903813eJake Slack        {
37603928aee4356845252ac6b662d5c72c29903813eJake Slack            _context.add(new String(buf, offset, len));
37703928aee4356845252ac6b662d5c72c29903813eJake Slack            for (int i = 0; i < _observers.size(); i++)
37803928aee4356845252ac6b662d5c72c29903813eJake Slack                if (_observers.get(i) != null)
37903928aee4356845252ac6b662d5c72c29903813eJake Slack                    ((ContentHandler) _observers.get(i)).characters(buf, offset, len);
38003928aee4356845252ac6b662d5c72c29903813eJake Slack        }
38103928aee4356845252ac6b662d5c72c29903813eJake Slack
38203928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
38303928aee4356845252ac6b662d5c72c29903813eJake Slack        public void warning(SAXParseException ex)
38403928aee4356845252ac6b662d5c72c29903813eJake Slack        {
38503928aee4356845252ac6b662d5c72c29903813eJake Slack            LOG.debug(Log.EXCEPTION, ex);
38603928aee4356845252ac6b662d5c72c29903813eJake Slack            LOG.warn("WARNING@" + getLocationString(ex) + " : " + ex.toString());
38703928aee4356845252ac6b662d5c72c29903813eJake Slack        }
38803928aee4356845252ac6b662d5c72c29903813eJake Slack
38903928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
39003928aee4356845252ac6b662d5c72c29903813eJake Slack        public void error(SAXParseException ex) throws SAXException
39103928aee4356845252ac6b662d5c72c29903813eJake Slack        {
39203928aee4356845252ac6b662d5c72c29903813eJake Slack            // Save error and continue to report other errors
39303928aee4356845252ac6b662d5c72c29903813eJake Slack            if (_error == null)
39403928aee4356845252ac6b662d5c72c29903813eJake Slack                _error = ex;
39503928aee4356845252ac6b662d5c72c29903813eJake Slack            LOG.debug(Log.EXCEPTION, ex);
39603928aee4356845252ac6b662d5c72c29903813eJake Slack            LOG.warn("ERROR@" + getLocationString(ex) + " : " + ex.toString());
39703928aee4356845252ac6b662d5c72c29903813eJake Slack        }
39803928aee4356845252ac6b662d5c72c29903813eJake Slack
39903928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
40003928aee4356845252ac6b662d5c72c29903813eJake Slack        public void fatalError(SAXParseException ex) throws SAXException
40103928aee4356845252ac6b662d5c72c29903813eJake Slack        {
40203928aee4356845252ac6b662d5c72c29903813eJake Slack            _error = ex;
40303928aee4356845252ac6b662d5c72c29903813eJake Slack            LOG.debug(Log.EXCEPTION, ex);
40403928aee4356845252ac6b662d5c72c29903813eJake Slack            LOG.warn("FATAL@" + getLocationString(ex) + " : " + ex.toString());
40503928aee4356845252ac6b662d5c72c29903813eJake Slack            throw ex;
40603928aee4356845252ac6b662d5c72c29903813eJake Slack        }
40703928aee4356845252ac6b662d5c72c29903813eJake Slack
40803928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
40903928aee4356845252ac6b662d5c72c29903813eJake Slack        private String getLocationString(SAXParseException ex)
41003928aee4356845252ac6b662d5c72c29903813eJake Slack        {
41103928aee4356845252ac6b662d5c72c29903813eJake Slack            return ex.getSystemId() + " line:" + ex.getLineNumber() + " col:" + ex.getColumnNumber();
41203928aee4356845252ac6b662d5c72c29903813eJake Slack        }
41303928aee4356845252ac6b662d5c72c29903813eJake Slack
41403928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
41503928aee4356845252ac6b662d5c72c29903813eJake Slack        public InputSource resolveEntity(String pid, String sid)
41603928aee4356845252ac6b662d5c72c29903813eJake Slack        {
41703928aee4356845252ac6b662d5c72c29903813eJake Slack            if (LOG.isDebugEnabled())
41803928aee4356845252ac6b662d5c72c29903813eJake Slack                LOG.debug("resolveEntity(" + pid + ", " + sid + ")");
41903928aee4356845252ac6b662d5c72c29903813eJake Slack
42003928aee4356845252ac6b662d5c72c29903813eJake Slack            if (sid!=null && sid.endsWith(".dtd"))
42103928aee4356845252ac6b662d5c72c29903813eJake Slack                _dtd=sid;
42203928aee4356845252ac6b662d5c72c29903813eJake Slack
42303928aee4356845252ac6b662d5c72c29903813eJake Slack            URL entity = null;
42403928aee4356845252ac6b662d5c72c29903813eJake Slack            if (pid != null)
42503928aee4356845252ac6b662d5c72c29903813eJake Slack                entity = (URL) _redirectMap.get(pid);
42603928aee4356845252ac6b662d5c72c29903813eJake Slack            if (entity == null)
42703928aee4356845252ac6b662d5c72c29903813eJake Slack                entity = (URL) _redirectMap.get(sid);
42803928aee4356845252ac6b662d5c72c29903813eJake Slack            if (entity == null)
42903928aee4356845252ac6b662d5c72c29903813eJake Slack            {
43003928aee4356845252ac6b662d5c72c29903813eJake Slack                String dtd = sid;
43103928aee4356845252ac6b662d5c72c29903813eJake Slack                if (dtd.lastIndexOf('/') >= 0)
43203928aee4356845252ac6b662d5c72c29903813eJake Slack                    dtd = dtd.substring(dtd.lastIndexOf('/') + 1);
43303928aee4356845252ac6b662d5c72c29903813eJake Slack
43403928aee4356845252ac6b662d5c72c29903813eJake Slack                if (LOG.isDebugEnabled())
43503928aee4356845252ac6b662d5c72c29903813eJake Slack                    LOG.debug("Can't exact match entity in redirect map, trying " + dtd);
43603928aee4356845252ac6b662d5c72c29903813eJake Slack                entity = (URL) _redirectMap.get(dtd);
43703928aee4356845252ac6b662d5c72c29903813eJake Slack            }
43803928aee4356845252ac6b662d5c72c29903813eJake Slack
43903928aee4356845252ac6b662d5c72c29903813eJake Slack            if (entity != null)
44003928aee4356845252ac6b662d5c72c29903813eJake Slack            {
44103928aee4356845252ac6b662d5c72c29903813eJake Slack                try
44203928aee4356845252ac6b662d5c72c29903813eJake Slack                {
44303928aee4356845252ac6b662d5c72c29903813eJake Slack                    InputStream in = entity.openStream();
44403928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (LOG.isDebugEnabled())
44503928aee4356845252ac6b662d5c72c29903813eJake Slack                        LOG.debug("Redirected entity " + sid + " --> " + entity);
44603928aee4356845252ac6b662d5c72c29903813eJake Slack                    InputSource is = new InputSource(in);
44703928aee4356845252ac6b662d5c72c29903813eJake Slack                    is.setSystemId(sid);
44803928aee4356845252ac6b662d5c72c29903813eJake Slack                    return is;
44903928aee4356845252ac6b662d5c72c29903813eJake Slack                }
45003928aee4356845252ac6b662d5c72c29903813eJake Slack                catch (IOException e)
45103928aee4356845252ac6b662d5c72c29903813eJake Slack                {
45203928aee4356845252ac6b662d5c72c29903813eJake Slack                    LOG.ignore(e);
45303928aee4356845252ac6b662d5c72c29903813eJake Slack                }
45403928aee4356845252ac6b662d5c72c29903813eJake Slack            }
45503928aee4356845252ac6b662d5c72c29903813eJake Slack            return null;
45603928aee4356845252ac6b662d5c72c29903813eJake Slack        }
45703928aee4356845252ac6b662d5c72c29903813eJake Slack    }
45803928aee4356845252ac6b662d5c72c29903813eJake Slack
45903928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
46003928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
46103928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
46203928aee4356845252ac6b662d5c72c29903813eJake Slack     * XML Attribute.
46303928aee4356845252ac6b662d5c72c29903813eJake Slack     */
46403928aee4356845252ac6b662d5c72c29903813eJake Slack    public static class Attribute
46503928aee4356845252ac6b662d5c72c29903813eJake Slack    {
46603928aee4356845252ac6b662d5c72c29903813eJake Slack        private String _name;
46703928aee4356845252ac6b662d5c72c29903813eJake Slack        private String _value;
46803928aee4356845252ac6b662d5c72c29903813eJake Slack
46903928aee4356845252ac6b662d5c72c29903813eJake Slack        Attribute(String n, String v)
47003928aee4356845252ac6b662d5c72c29903813eJake Slack        {
47103928aee4356845252ac6b662d5c72c29903813eJake Slack            _name = n;
47203928aee4356845252ac6b662d5c72c29903813eJake Slack            _value = v;
47303928aee4356845252ac6b662d5c72c29903813eJake Slack        }
47403928aee4356845252ac6b662d5c72c29903813eJake Slack
47503928aee4356845252ac6b662d5c72c29903813eJake Slack        public String getName()
47603928aee4356845252ac6b662d5c72c29903813eJake Slack        {
47703928aee4356845252ac6b662d5c72c29903813eJake Slack            return _name;
47803928aee4356845252ac6b662d5c72c29903813eJake Slack        }
47903928aee4356845252ac6b662d5c72c29903813eJake Slack
48003928aee4356845252ac6b662d5c72c29903813eJake Slack        public String getValue()
48103928aee4356845252ac6b662d5c72c29903813eJake Slack        {
48203928aee4356845252ac6b662d5c72c29903813eJake Slack            return _value;
48303928aee4356845252ac6b662d5c72c29903813eJake Slack        }
48403928aee4356845252ac6b662d5c72c29903813eJake Slack    }
48503928aee4356845252ac6b662d5c72c29903813eJake Slack
48603928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
48703928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
48803928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
48903928aee4356845252ac6b662d5c72c29903813eJake Slack     * XML Node. Represents an XML element with optional attributes and ordered content.
49003928aee4356845252ac6b662d5c72c29903813eJake Slack     */
49103928aee4356845252ac6b662d5c72c29903813eJake Slack    public static class Node extends AbstractList<Object>
49203928aee4356845252ac6b662d5c72c29903813eJake Slack    {
49303928aee4356845252ac6b662d5c72c29903813eJake Slack        Node _parent;
49403928aee4356845252ac6b662d5c72c29903813eJake Slack        private ArrayList<Object> _list;
49503928aee4356845252ac6b662d5c72c29903813eJake Slack        private String _tag;
49603928aee4356845252ac6b662d5c72c29903813eJake Slack        private Attribute[] _attrs;
49703928aee4356845252ac6b662d5c72c29903813eJake Slack        private boolean _lastString = false;
49803928aee4356845252ac6b662d5c72c29903813eJake Slack        private String _path;
49903928aee4356845252ac6b662d5c72c29903813eJake Slack
50003928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
50103928aee4356845252ac6b662d5c72c29903813eJake Slack        Node(Node parent, String tag, Attributes attrs)
50203928aee4356845252ac6b662d5c72c29903813eJake Slack        {
50303928aee4356845252ac6b662d5c72c29903813eJake Slack            _parent = parent;
50403928aee4356845252ac6b662d5c72c29903813eJake Slack            _tag = tag;
50503928aee4356845252ac6b662d5c72c29903813eJake Slack
50603928aee4356845252ac6b662d5c72c29903813eJake Slack            if (attrs != null)
50703928aee4356845252ac6b662d5c72c29903813eJake Slack            {
50803928aee4356845252ac6b662d5c72c29903813eJake Slack                _attrs = new Attribute[attrs.getLength()];
50903928aee4356845252ac6b662d5c72c29903813eJake Slack                for (int i = 0; i < attrs.getLength(); i++)
51003928aee4356845252ac6b662d5c72c29903813eJake Slack                {
51103928aee4356845252ac6b662d5c72c29903813eJake Slack                    String name = attrs.getLocalName(i);
51203928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (name == null || name.equals(""))
51303928aee4356845252ac6b662d5c72c29903813eJake Slack                        name = attrs.getQName(i);
51403928aee4356845252ac6b662d5c72c29903813eJake Slack                    _attrs[i] = new Attribute(name, attrs.getValue(i));
51503928aee4356845252ac6b662d5c72c29903813eJake Slack                }
51603928aee4356845252ac6b662d5c72c29903813eJake Slack            }
51703928aee4356845252ac6b662d5c72c29903813eJake Slack        }
51803928aee4356845252ac6b662d5c72c29903813eJake Slack
51903928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
52003928aee4356845252ac6b662d5c72c29903813eJake Slack        public Node getParent()
52103928aee4356845252ac6b662d5c72c29903813eJake Slack        {
52203928aee4356845252ac6b662d5c72c29903813eJake Slack            return _parent;
52303928aee4356845252ac6b662d5c72c29903813eJake Slack        }
52403928aee4356845252ac6b662d5c72c29903813eJake Slack
52503928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
52603928aee4356845252ac6b662d5c72c29903813eJake Slack        public String getTag()
52703928aee4356845252ac6b662d5c72c29903813eJake Slack        {
52803928aee4356845252ac6b662d5c72c29903813eJake Slack            return _tag;
52903928aee4356845252ac6b662d5c72c29903813eJake Slack        }
53003928aee4356845252ac6b662d5c72c29903813eJake Slack
53103928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
53203928aee4356845252ac6b662d5c72c29903813eJake Slack        public String getPath()
53303928aee4356845252ac6b662d5c72c29903813eJake Slack        {
53403928aee4356845252ac6b662d5c72c29903813eJake Slack            if (_path == null)
53503928aee4356845252ac6b662d5c72c29903813eJake Slack            {
53603928aee4356845252ac6b662d5c72c29903813eJake Slack                if (getParent() != null && getParent().getTag() != null)
53703928aee4356845252ac6b662d5c72c29903813eJake Slack                    _path = getParent().getPath() + "/" + _tag;
53803928aee4356845252ac6b662d5c72c29903813eJake Slack                else
53903928aee4356845252ac6b662d5c72c29903813eJake Slack                    _path = "/" + _tag;
54003928aee4356845252ac6b662d5c72c29903813eJake Slack            }
54103928aee4356845252ac6b662d5c72c29903813eJake Slack            return _path;
54203928aee4356845252ac6b662d5c72c29903813eJake Slack        }
54303928aee4356845252ac6b662d5c72c29903813eJake Slack
54403928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
54503928aee4356845252ac6b662d5c72c29903813eJake Slack        /**
54603928aee4356845252ac6b662d5c72c29903813eJake Slack         * Get an array of element attributes.
54703928aee4356845252ac6b662d5c72c29903813eJake Slack         */
54803928aee4356845252ac6b662d5c72c29903813eJake Slack        public Attribute[] getAttributes()
54903928aee4356845252ac6b662d5c72c29903813eJake Slack        {
55003928aee4356845252ac6b662d5c72c29903813eJake Slack            return _attrs;
55103928aee4356845252ac6b662d5c72c29903813eJake Slack        }
55203928aee4356845252ac6b662d5c72c29903813eJake Slack
55303928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
55403928aee4356845252ac6b662d5c72c29903813eJake Slack        /**
55503928aee4356845252ac6b662d5c72c29903813eJake Slack         * Get an element attribute.
55603928aee4356845252ac6b662d5c72c29903813eJake Slack         *
55703928aee4356845252ac6b662d5c72c29903813eJake Slack         * @return attribute or null.
55803928aee4356845252ac6b662d5c72c29903813eJake Slack         */
55903928aee4356845252ac6b662d5c72c29903813eJake Slack        public String getAttribute(String name)
56003928aee4356845252ac6b662d5c72c29903813eJake Slack        {
56103928aee4356845252ac6b662d5c72c29903813eJake Slack            return getAttribute(name, null);
56203928aee4356845252ac6b662d5c72c29903813eJake Slack        }
56303928aee4356845252ac6b662d5c72c29903813eJake Slack
56403928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
56503928aee4356845252ac6b662d5c72c29903813eJake Slack        /**
56603928aee4356845252ac6b662d5c72c29903813eJake Slack         * Get an element attribute.
56703928aee4356845252ac6b662d5c72c29903813eJake Slack         *
56803928aee4356845252ac6b662d5c72c29903813eJake Slack         * @return attribute or null.
56903928aee4356845252ac6b662d5c72c29903813eJake Slack         */
57003928aee4356845252ac6b662d5c72c29903813eJake Slack        public String getAttribute(String name, String dft)
57103928aee4356845252ac6b662d5c72c29903813eJake Slack        {
57203928aee4356845252ac6b662d5c72c29903813eJake Slack            if (_attrs == null || name == null)
57303928aee4356845252ac6b662d5c72c29903813eJake Slack                return dft;
57403928aee4356845252ac6b662d5c72c29903813eJake Slack            for (int i = 0; i < _attrs.length; i++)
57503928aee4356845252ac6b662d5c72c29903813eJake Slack                if (name.equals(_attrs[i].getName()))
57603928aee4356845252ac6b662d5c72c29903813eJake Slack                    return _attrs[i].getValue();
57703928aee4356845252ac6b662d5c72c29903813eJake Slack            return dft;
57803928aee4356845252ac6b662d5c72c29903813eJake Slack        }
57903928aee4356845252ac6b662d5c72c29903813eJake Slack
58003928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
58103928aee4356845252ac6b662d5c72c29903813eJake Slack        /**
58203928aee4356845252ac6b662d5c72c29903813eJake Slack         * Get the number of children nodes.
58303928aee4356845252ac6b662d5c72c29903813eJake Slack         */
58403928aee4356845252ac6b662d5c72c29903813eJake Slack        public int size()
58503928aee4356845252ac6b662d5c72c29903813eJake Slack        {
58603928aee4356845252ac6b662d5c72c29903813eJake Slack            if (_list != null)
58703928aee4356845252ac6b662d5c72c29903813eJake Slack                return _list.size();
58803928aee4356845252ac6b662d5c72c29903813eJake Slack            return 0;
58903928aee4356845252ac6b662d5c72c29903813eJake Slack        }
59003928aee4356845252ac6b662d5c72c29903813eJake Slack
59103928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
59203928aee4356845252ac6b662d5c72c29903813eJake Slack        /**
59303928aee4356845252ac6b662d5c72c29903813eJake Slack         * Get the ith child node or content.
59403928aee4356845252ac6b662d5c72c29903813eJake Slack         *
59503928aee4356845252ac6b662d5c72c29903813eJake Slack         * @return Node or String.
59603928aee4356845252ac6b662d5c72c29903813eJake Slack         */
59703928aee4356845252ac6b662d5c72c29903813eJake Slack        public Object get(int i)
59803928aee4356845252ac6b662d5c72c29903813eJake Slack        {
59903928aee4356845252ac6b662d5c72c29903813eJake Slack            if (_list != null)
60003928aee4356845252ac6b662d5c72c29903813eJake Slack                return _list.get(i);
60103928aee4356845252ac6b662d5c72c29903813eJake Slack            return null;
60203928aee4356845252ac6b662d5c72c29903813eJake Slack        }
60303928aee4356845252ac6b662d5c72c29903813eJake Slack
60403928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
60503928aee4356845252ac6b662d5c72c29903813eJake Slack        /**
60603928aee4356845252ac6b662d5c72c29903813eJake Slack         * Get the first child node with the tag.
60703928aee4356845252ac6b662d5c72c29903813eJake Slack         *
60803928aee4356845252ac6b662d5c72c29903813eJake Slack         * @param tag
60903928aee4356845252ac6b662d5c72c29903813eJake Slack         * @return Node or null.
61003928aee4356845252ac6b662d5c72c29903813eJake Slack         */
61103928aee4356845252ac6b662d5c72c29903813eJake Slack        public Node get(String tag)
61203928aee4356845252ac6b662d5c72c29903813eJake Slack        {
61303928aee4356845252ac6b662d5c72c29903813eJake Slack            if (_list != null)
61403928aee4356845252ac6b662d5c72c29903813eJake Slack            {
61503928aee4356845252ac6b662d5c72c29903813eJake Slack                for (int i = 0; i < _list.size(); i++)
61603928aee4356845252ac6b662d5c72c29903813eJake Slack                {
61703928aee4356845252ac6b662d5c72c29903813eJake Slack                    Object o = _list.get(i);
61803928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (o instanceof Node)
61903928aee4356845252ac6b662d5c72c29903813eJake Slack                    {
62003928aee4356845252ac6b662d5c72c29903813eJake Slack                        Node n = (Node) o;
62103928aee4356845252ac6b662d5c72c29903813eJake Slack                        if (tag.equals(n._tag))
62203928aee4356845252ac6b662d5c72c29903813eJake Slack                            return n;
62303928aee4356845252ac6b662d5c72c29903813eJake Slack                    }
62403928aee4356845252ac6b662d5c72c29903813eJake Slack                }
62503928aee4356845252ac6b662d5c72c29903813eJake Slack            }
62603928aee4356845252ac6b662d5c72c29903813eJake Slack            return null;
62703928aee4356845252ac6b662d5c72c29903813eJake Slack        }
62803928aee4356845252ac6b662d5c72c29903813eJake Slack
62903928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
63003928aee4356845252ac6b662d5c72c29903813eJake Slack        @Override
63103928aee4356845252ac6b662d5c72c29903813eJake Slack        public void add(int i, Object o)
63203928aee4356845252ac6b662d5c72c29903813eJake Slack        {
63303928aee4356845252ac6b662d5c72c29903813eJake Slack            if (_list == null)
63403928aee4356845252ac6b662d5c72c29903813eJake Slack                _list = new ArrayList<Object>();
63503928aee4356845252ac6b662d5c72c29903813eJake Slack            if (o instanceof String)
63603928aee4356845252ac6b662d5c72c29903813eJake Slack            {
63703928aee4356845252ac6b662d5c72c29903813eJake Slack                if (_lastString)
63803928aee4356845252ac6b662d5c72c29903813eJake Slack                {
63903928aee4356845252ac6b662d5c72c29903813eJake Slack                    int last = _list.size() - 1;
64003928aee4356845252ac6b662d5c72c29903813eJake Slack                    _list.set(last, (String) _list.get(last) + o);
64103928aee4356845252ac6b662d5c72c29903813eJake Slack                }
64203928aee4356845252ac6b662d5c72c29903813eJake Slack                else
64303928aee4356845252ac6b662d5c72c29903813eJake Slack                    _list.add(i, o);
64403928aee4356845252ac6b662d5c72c29903813eJake Slack                _lastString = true;
64503928aee4356845252ac6b662d5c72c29903813eJake Slack            }
64603928aee4356845252ac6b662d5c72c29903813eJake Slack            else
64703928aee4356845252ac6b662d5c72c29903813eJake Slack            {
64803928aee4356845252ac6b662d5c72c29903813eJake Slack                _lastString = false;
64903928aee4356845252ac6b662d5c72c29903813eJake Slack                _list.add(i, o);
65003928aee4356845252ac6b662d5c72c29903813eJake Slack            }
65103928aee4356845252ac6b662d5c72c29903813eJake Slack        }
65203928aee4356845252ac6b662d5c72c29903813eJake Slack
65303928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
65403928aee4356845252ac6b662d5c72c29903813eJake Slack        public void clear()
65503928aee4356845252ac6b662d5c72c29903813eJake Slack        {
65603928aee4356845252ac6b662d5c72c29903813eJake Slack            if (_list != null)
65703928aee4356845252ac6b662d5c72c29903813eJake Slack                _list.clear();
65803928aee4356845252ac6b662d5c72c29903813eJake Slack            _list = null;
65903928aee4356845252ac6b662d5c72c29903813eJake Slack        }
66003928aee4356845252ac6b662d5c72c29903813eJake Slack
66103928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
66203928aee4356845252ac6b662d5c72c29903813eJake Slack        /**
66303928aee4356845252ac6b662d5c72c29903813eJake Slack         * Get a tag as a string.
66403928aee4356845252ac6b662d5c72c29903813eJake Slack         *
66503928aee4356845252ac6b662d5c72c29903813eJake Slack         * @param tag The tag to get
66603928aee4356845252ac6b662d5c72c29903813eJake Slack         * @param tags IF true, tags are included in the value.
66703928aee4356845252ac6b662d5c72c29903813eJake Slack         * @param trim If true, trim the value.
66803928aee4356845252ac6b662d5c72c29903813eJake Slack         * @return results of get(tag).toString(tags).
66903928aee4356845252ac6b662d5c72c29903813eJake Slack         */
67003928aee4356845252ac6b662d5c72c29903813eJake Slack        public String getString(String tag, boolean tags, boolean trim)
67103928aee4356845252ac6b662d5c72c29903813eJake Slack        {
67203928aee4356845252ac6b662d5c72c29903813eJake Slack            Node node = get(tag);
67303928aee4356845252ac6b662d5c72c29903813eJake Slack            if (node == null)
67403928aee4356845252ac6b662d5c72c29903813eJake Slack                return null;
67503928aee4356845252ac6b662d5c72c29903813eJake Slack            String s = node.toString(tags);
67603928aee4356845252ac6b662d5c72c29903813eJake Slack            if (s != null && trim)
67703928aee4356845252ac6b662d5c72c29903813eJake Slack                s = s.trim();
67803928aee4356845252ac6b662d5c72c29903813eJake Slack            return s;
67903928aee4356845252ac6b662d5c72c29903813eJake Slack        }
68003928aee4356845252ac6b662d5c72c29903813eJake Slack
68103928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
68203928aee4356845252ac6b662d5c72c29903813eJake Slack        public synchronized String toString()
68303928aee4356845252ac6b662d5c72c29903813eJake Slack        {
68403928aee4356845252ac6b662d5c72c29903813eJake Slack            return toString(true);
68503928aee4356845252ac6b662d5c72c29903813eJake Slack        }
68603928aee4356845252ac6b662d5c72c29903813eJake Slack
68703928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
68803928aee4356845252ac6b662d5c72c29903813eJake Slack        /**
68903928aee4356845252ac6b662d5c72c29903813eJake Slack         * Convert to a string.
69003928aee4356845252ac6b662d5c72c29903813eJake Slack         *
69103928aee4356845252ac6b662d5c72c29903813eJake Slack         * @param tag If false, only _content is shown.
69203928aee4356845252ac6b662d5c72c29903813eJake Slack         */
69303928aee4356845252ac6b662d5c72c29903813eJake Slack        public synchronized String toString(boolean tag)
69403928aee4356845252ac6b662d5c72c29903813eJake Slack        {
69503928aee4356845252ac6b662d5c72c29903813eJake Slack            StringBuilder buf = new StringBuilder();
69603928aee4356845252ac6b662d5c72c29903813eJake Slack            toString(buf, tag);
69703928aee4356845252ac6b662d5c72c29903813eJake Slack            return buf.toString();
69803928aee4356845252ac6b662d5c72c29903813eJake Slack        }
69903928aee4356845252ac6b662d5c72c29903813eJake Slack
70003928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
70103928aee4356845252ac6b662d5c72c29903813eJake Slack        /**
70203928aee4356845252ac6b662d5c72c29903813eJake Slack         * Convert to a string.
70303928aee4356845252ac6b662d5c72c29903813eJake Slack         *
70403928aee4356845252ac6b662d5c72c29903813eJake Slack         * @param tag If false, only _content is shown.
70503928aee4356845252ac6b662d5c72c29903813eJake Slack         */
70603928aee4356845252ac6b662d5c72c29903813eJake Slack        public synchronized String toString(boolean tag, boolean trim)
70703928aee4356845252ac6b662d5c72c29903813eJake Slack        {
70803928aee4356845252ac6b662d5c72c29903813eJake Slack            String s = toString(tag);
70903928aee4356845252ac6b662d5c72c29903813eJake Slack            if (s != null && trim)
71003928aee4356845252ac6b662d5c72c29903813eJake Slack                s = s.trim();
71103928aee4356845252ac6b662d5c72c29903813eJake Slack            return s;
71203928aee4356845252ac6b662d5c72c29903813eJake Slack        }
71303928aee4356845252ac6b662d5c72c29903813eJake Slack
71403928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
71503928aee4356845252ac6b662d5c72c29903813eJake Slack        private synchronized void toString(StringBuilder buf, boolean tag)
71603928aee4356845252ac6b662d5c72c29903813eJake Slack        {
71703928aee4356845252ac6b662d5c72c29903813eJake Slack            if (tag)
71803928aee4356845252ac6b662d5c72c29903813eJake Slack            {
71903928aee4356845252ac6b662d5c72c29903813eJake Slack                buf.append("<");
72003928aee4356845252ac6b662d5c72c29903813eJake Slack                buf.append(_tag);
72103928aee4356845252ac6b662d5c72c29903813eJake Slack
72203928aee4356845252ac6b662d5c72c29903813eJake Slack                if (_attrs != null)
72303928aee4356845252ac6b662d5c72c29903813eJake Slack                {
72403928aee4356845252ac6b662d5c72c29903813eJake Slack                    for (int i = 0; i < _attrs.length; i++)
72503928aee4356845252ac6b662d5c72c29903813eJake Slack                    {
72603928aee4356845252ac6b662d5c72c29903813eJake Slack                        buf.append(' ');
72703928aee4356845252ac6b662d5c72c29903813eJake Slack                        buf.append(_attrs[i].getName());
72803928aee4356845252ac6b662d5c72c29903813eJake Slack                        buf.append("=\"");
72903928aee4356845252ac6b662d5c72c29903813eJake Slack                        buf.append(_attrs[i].getValue());
73003928aee4356845252ac6b662d5c72c29903813eJake Slack                        buf.append("\"");
73103928aee4356845252ac6b662d5c72c29903813eJake Slack                    }
73203928aee4356845252ac6b662d5c72c29903813eJake Slack                }
73303928aee4356845252ac6b662d5c72c29903813eJake Slack            }
73403928aee4356845252ac6b662d5c72c29903813eJake Slack
73503928aee4356845252ac6b662d5c72c29903813eJake Slack            if (_list != null)
73603928aee4356845252ac6b662d5c72c29903813eJake Slack            {
73703928aee4356845252ac6b662d5c72c29903813eJake Slack                if (tag)
73803928aee4356845252ac6b662d5c72c29903813eJake Slack                    buf.append(">");
73903928aee4356845252ac6b662d5c72c29903813eJake Slack                for (int i = 0; i < _list.size(); i++)
74003928aee4356845252ac6b662d5c72c29903813eJake Slack                {
74103928aee4356845252ac6b662d5c72c29903813eJake Slack                    Object o = _list.get(i);
74203928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (o == null)
74303928aee4356845252ac6b662d5c72c29903813eJake Slack                        continue;
74403928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (o instanceof Node)
74503928aee4356845252ac6b662d5c72c29903813eJake Slack                        ((Node) o).toString(buf, tag);
74603928aee4356845252ac6b662d5c72c29903813eJake Slack                    else
74703928aee4356845252ac6b662d5c72c29903813eJake Slack                        buf.append(o.toString());
74803928aee4356845252ac6b662d5c72c29903813eJake Slack                }
74903928aee4356845252ac6b662d5c72c29903813eJake Slack                if (tag)
75003928aee4356845252ac6b662d5c72c29903813eJake Slack                {
75103928aee4356845252ac6b662d5c72c29903813eJake Slack                    buf.append("</");
75203928aee4356845252ac6b662d5c72c29903813eJake Slack                    buf.append(_tag);
75303928aee4356845252ac6b662d5c72c29903813eJake Slack                    buf.append(">");
75403928aee4356845252ac6b662d5c72c29903813eJake Slack                }
75503928aee4356845252ac6b662d5c72c29903813eJake Slack            }
75603928aee4356845252ac6b662d5c72c29903813eJake Slack            else if (tag)
75703928aee4356845252ac6b662d5c72c29903813eJake Slack                buf.append("/>");
75803928aee4356845252ac6b662d5c72c29903813eJake Slack        }
75903928aee4356845252ac6b662d5c72c29903813eJake Slack
76003928aee4356845252ac6b662d5c72c29903813eJake Slack        /* ------------------------------------------------------------ */
76103928aee4356845252ac6b662d5c72c29903813eJake Slack        /**
76203928aee4356845252ac6b662d5c72c29903813eJake Slack         * Iterator over named child nodes.
76303928aee4356845252ac6b662d5c72c29903813eJake Slack         *
76403928aee4356845252ac6b662d5c72c29903813eJake Slack         * @param tag The tag of the nodes.
76503928aee4356845252ac6b662d5c72c29903813eJake Slack         * @return Iterator over all child nodes with the specified tag.
76603928aee4356845252ac6b662d5c72c29903813eJake Slack         */
76703928aee4356845252ac6b662d5c72c29903813eJake Slack        public Iterator<Node> iterator(final String tag)
76803928aee4356845252ac6b662d5c72c29903813eJake Slack        {
76903928aee4356845252ac6b662d5c72c29903813eJake Slack            return new Iterator<Node>()
77003928aee4356845252ac6b662d5c72c29903813eJake Slack            {
77103928aee4356845252ac6b662d5c72c29903813eJake Slack                int c = 0;
77203928aee4356845252ac6b662d5c72c29903813eJake Slack                Node _node;
77303928aee4356845252ac6b662d5c72c29903813eJake Slack
77403928aee4356845252ac6b662d5c72c29903813eJake Slack                /* -------------------------------------------------- */
77503928aee4356845252ac6b662d5c72c29903813eJake Slack                public boolean hasNext()
77603928aee4356845252ac6b662d5c72c29903813eJake Slack                {
77703928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (_node != null)
77803928aee4356845252ac6b662d5c72c29903813eJake Slack                        return true;
77903928aee4356845252ac6b662d5c72c29903813eJake Slack                    while (_list != null && c < _list.size())
78003928aee4356845252ac6b662d5c72c29903813eJake Slack                    {
78103928aee4356845252ac6b662d5c72c29903813eJake Slack                        Object o = _list.get(c);
78203928aee4356845252ac6b662d5c72c29903813eJake Slack                        if (o instanceof Node)
78303928aee4356845252ac6b662d5c72c29903813eJake Slack                        {
78403928aee4356845252ac6b662d5c72c29903813eJake Slack                            Node n = (Node) o;
78503928aee4356845252ac6b662d5c72c29903813eJake Slack                            if (tag.equals(n._tag))
78603928aee4356845252ac6b662d5c72c29903813eJake Slack                            {
78703928aee4356845252ac6b662d5c72c29903813eJake Slack                                _node = n;
78803928aee4356845252ac6b662d5c72c29903813eJake Slack                                return true;
78903928aee4356845252ac6b662d5c72c29903813eJake Slack                            }
79003928aee4356845252ac6b662d5c72c29903813eJake Slack                        }
79103928aee4356845252ac6b662d5c72c29903813eJake Slack                        c++;
79203928aee4356845252ac6b662d5c72c29903813eJake Slack                    }
79303928aee4356845252ac6b662d5c72c29903813eJake Slack                    return false;
79403928aee4356845252ac6b662d5c72c29903813eJake Slack                }
79503928aee4356845252ac6b662d5c72c29903813eJake Slack
79603928aee4356845252ac6b662d5c72c29903813eJake Slack                /* -------------------------------------------------- */
79703928aee4356845252ac6b662d5c72c29903813eJake Slack                public Node next()
79803928aee4356845252ac6b662d5c72c29903813eJake Slack                {
79903928aee4356845252ac6b662d5c72c29903813eJake Slack                    try
80003928aee4356845252ac6b662d5c72c29903813eJake Slack                    {
80103928aee4356845252ac6b662d5c72c29903813eJake Slack                        if (hasNext())
80203928aee4356845252ac6b662d5c72c29903813eJake Slack                            return _node;
80303928aee4356845252ac6b662d5c72c29903813eJake Slack                        throw new NoSuchElementException();
80403928aee4356845252ac6b662d5c72c29903813eJake Slack                    }
80503928aee4356845252ac6b662d5c72c29903813eJake Slack                    finally
80603928aee4356845252ac6b662d5c72c29903813eJake Slack                    {
80703928aee4356845252ac6b662d5c72c29903813eJake Slack                        _node = null;
80803928aee4356845252ac6b662d5c72c29903813eJake Slack                        c++;
80903928aee4356845252ac6b662d5c72c29903813eJake Slack                    }
81003928aee4356845252ac6b662d5c72c29903813eJake Slack                }
81103928aee4356845252ac6b662d5c72c29903813eJake Slack
81203928aee4356845252ac6b662d5c72c29903813eJake Slack                /* -------------------------------------------------- */
81303928aee4356845252ac6b662d5c72c29903813eJake Slack                public void remove()
81403928aee4356845252ac6b662d5c72c29903813eJake Slack                {
81503928aee4356845252ac6b662d5c72c29903813eJake Slack                    throw new UnsupportedOperationException("Not supported");
81603928aee4356845252ac6b662d5c72c29903813eJake Slack                }
81703928aee4356845252ac6b662d5c72c29903813eJake Slack            };
81803928aee4356845252ac6b662d5c72c29903813eJake Slack        }
81903928aee4356845252ac6b662d5c72c29903813eJake Slack    }
82003928aee4356845252ac6b662d5c72c29903813eJake Slack}
821