1 package org.apache.maven;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 import java.util.HashMap;
22 import java.util.Map;
23
24 import org.xml.sax.Attributes;
25 import org.xml.sax.SAXException;
26 import org.xml.sax.SAXParseException;
27 import org.xml.sax.helpers.XMLFilterImpl;
28
29 /**
30 * Constructs an XPath expression in the form:
31 * /root/child[3]/grandchild[2] ...
32 *
33 * @author <a href="mailto:ltheussl@apache.org">Lukas Theussl</a>
34 */
35 public class XPathLocationTracker extends XMLFilterImpl
36 {
37
38 private State state;
39 private static final Integer[] ints = new Integer[]
40 {
41 new Integer( 0 ),
42 new Integer( 1 ),
43 new Integer( 2 ),
44 new Integer( 3 ),
45 new Integer( 4 )
46 };
47
48 /**
49 * Overriding ContentHandler.
50 * @throws SAXException SAXException
51 */
52 public void startDocument() throws SAXException
53 {
54 state = new State( null );
55 super.startDocument();
56 }
57
58 /**
59 * Overriding ContentHandler.
60 * @throws SAXException SAXException
61 */
62 public void endDocument() throws SAXException
63 {
64 super.endDocument();
65 state = null;
66 }
67
68 /**
69 * Overriding ContentHandler.
70 * @param uri uri
71 * @param localName localName
72 * @param qName qName
73 * @param atts atts
74 * @throws SAXException SAXException
75 */
76 public void startElement( String uri, String localName,
77 String qName, Attributes atts ) throws SAXException
78 {
79 state = state.push( qName );
80 try
81 {
82 super.startElement( uri, localName, qName, atts );
83 }
84 catch ( SAXParseException e )
85 {
86
87
88 }
89 }
90
91 /**
92 * Overriding ContentHandler.
93 * @param uri uri
94 * @param localName localName
95 * @param qName qName
96 * @throws SAXException SAXException
97 */
98 public void endElement( String uri, String localName, String qName )
99 throws SAXException
100 {
101 try
102 {
103 super.endElement( uri, localName, qName );
104 }
105 catch ( SAXParseException e )
106 {
107
108
109 }
110 state = state.pop();
111 }
112
113 /**
114 * Gets the XPath expression that points to the current location.
115 * Throws a new IllegalStateException if the component is not
116 * parsing a document.
117 * @return The XPath expression
118 */
119 public final String getXPath()
120 {
121 if ( state == null )
122 {
123 throw new IllegalStateException(
124 "startDocument event is not invoked" );
125 }
126 return state.getXPath();
127 }
128
129 private static Integer getInt( int i )
130 {
131 if ( i < ints.length )
132 {
133 return ints[i];
134 }
135 else
136 {
137 return new Integer(i);
138 }
139 }
140
141 private static final class State
142 {
143
144 private final Map counter = new HashMap();
145 private final State parent;
146 private State child;
147 private String currentName;
148
149 State( State newParent )
150 {
151 this.parent = newParent;
152 }
153
154 protected State push( String rawName )
155 {
156 count(rawName);
157 currentName = rawName;
158 if ( child == null )
159 {
160 child = new State( this );
161 }
162 else
163 {
164 child.reset();
165 }
166 return child;
167 }
168
169 protected State pop()
170 {
171 parent.currentName = null;
172 return parent;
173 }
174
175 private void count( String rawName )
176 {
177 Integer i = (Integer) counter.get( rawName );
178 if ( i == null )
179 {
180 i = getInt( 1 );
181 }
182 else
183 {
184 i = getInt( i.intValue() + 1 );
185 }
186 counter.put( rawName, i );
187 }
188
189 private void reset()
190 {
191 counter.clear();
192 currentName = null;
193 }
194
195 private String getXPath()
196 {
197 String xPath;
198 if ( parent == null )
199 {
200 xPath = "/";
201 if ( currentName != null )
202 {
203 xPath += currentName;
204 }
205 }
206 else
207 {
208 xPath = parent.getXPath();
209 if ( currentName != null )
210 {
211 xPath += '/' + currentName;
212 Integer i = (Integer) counter.get( currentName );
213 xPath += '[' + i.toString() + ']';
214 }
215 }
216 return xPath;
217 }
218
219 }
220
221 }