package org.apache.maven; /* ==================================================================== * Copyright 2001-2005 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ==================================================================== */ import java.util.HashMap; import java.util.Map; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.helpers.XMLFilterImpl; /** * Constructs an XPath expression in the form: * /root/child[3]/grandchild[2] ... * * @author Lukas Theussl */ public class XPathLocationTracker extends XMLFilterImpl { private State state; private static final Integer[] ints = new Integer[] { new Integer( 0 ), new Integer( 1 ), new Integer( 2 ), new Integer( 3 ), new Integer( 4 ) }; /** * Overriding ContentHandler. * @throws SAXException SAXException */ public void startDocument() throws SAXException { state = new State( null ); super.startDocument(); } /** * Overriding ContentHandler. * @throws SAXException SAXException */ public void endDocument() throws SAXException { super.endDocument(); state = null; } /** * Overriding ContentHandler. * @param uri uri * @param localName localName * @param qName qName * @param atts atts * @throws SAXException SAXException */ public void startElement( String uri, String localName, String qName, Attributes atts ) throws SAXException { state = state.push( qName ); try { super.startElement( uri, localName, qName, atts ); } catch ( SAXParseException e ) { // ignored on purpose: just avoid to throw an exception here // it is thrown by the ErrorHandler in JaxpMsvBean instead } } /** * Overriding ContentHandler. * @param uri uri * @param localName localName * @param qName qName * @throws SAXException SAXException */ public void endElement( String uri, String localName, String qName ) throws SAXException { try { super.endElement( uri, localName, qName ); } catch ( SAXParseException e ) { // ignored on purpose: just avoid to throw an exception here // it is thrown by the ErrorHandler in JaxpMsvBean instead } state = state.pop(); } /** * Gets the XPath expression that points to the current location. * Throws a new IllegalStateException if the component is not * parsing a document. * @return The XPath expression */ public final String getXPath() { if ( state == null ) { throw new IllegalStateException( "startDocument event is not invoked" ); } return state.getXPath(); } private static Integer getInt( int i ) { if ( i < ints.length ) { return ints[i]; } else { return new Integer(i); } } private static final class State { private final Map counter = new HashMap(); private final State parent; private State child; private String currentName; State( State newParent ) { this.parent = newParent; } protected State push( String rawName ) { count(rawName); currentName = rawName; if ( child == null ) { child = new State( this ); } else { child.reset(); } return child; } protected State pop() { parent.currentName = null; return parent; } private void count( String rawName ) { Integer i = (Integer) counter.get( rawName ); if ( i == null ) { i = getInt( 1 ); } else { i = getInt( i.intValue() + 1 ); } counter.put( rawName, i ); } private void reset() { counter.clear(); currentName = null; } private String getXPath() { String xPath; if ( parent == null ) // root { xPath = "/"; if ( currentName != null ) { xPath += currentName; } } else // child node { xPath = parent.getXPath(); if ( currentName != null ) { xPath += '/' + currentName; Integer i = (Integer) counter.get( currentName ); xPath += '[' + i.toString() + ']'; } } return xPath; } } }