1
2
3
4
5
6
7
8
9
10
11
12 package com.eviware.soapui.support.xml;
13
14 import java.io.IOException;
15 import java.io.LineNumberReader;
16 import java.io.StringReader;
17 import java.util.ArrayList;
18 import java.util.Collection;
19 import java.util.HashMap;
20 import java.util.Iterator;
21 import java.util.List;
22
23 import com.eviware.soapui.support.types.StringToStringMap;
24
25 /***
26 *
27 * @author lars
28 */
29 public class XPathData
30 {
31 private StringToStringMap nsMap = new StringToStringMap();
32 private List<String> pathComponents = new ArrayList<String>();
33 private String function;
34 private boolean absolute = false;
35
36 public XPathData( StringToStringMap nsMap, List<String> pathComponents, boolean absolute )
37 {
38 this.nsMap = nsMap;
39 this.pathComponents = pathComponents;
40 this.absolute = absolute;
41 }
42
43 public XPathData( String xpath, boolean skipFirst )
44 {
45 try
46 {
47 LineNumberReader reader = new LineNumberReader( new StringReader( xpath.trim() ) );
48 String s;
49 while( ( s = reader.readLine() ) != null )
50 {
51 if( s.startsWith( "declare namespace " ) )
52 {
53 String[] words = s.substring( "declare namespace ".length() ).split( "=" );
54 String prefix = words[0];
55 int ix1 = words[1].indexOf( '\'' );
56 int ix2 = words[1].lastIndexOf( '\'' );
57 String ns = words[1].substring( ix1 + 1, ix2 - ix1 );
58 nsMap.put( ns, prefix );
59 }
60 else
61 {
62 if( s.startsWith( "count(" ) && s.endsWith( ")" ) )
63 {
64 function = "count";
65 s = s.substring( "count(".length(), s.length() - ")".length() );
66 }
67 else if( s.startsWith( "exists(" ) && s.endsWith( ")" ) )
68 {
69 function = "exists";
70 s = s.substring( "exists(".length(), s.length() - ")".length() );
71 }
72
73 String[] words = s.split( "/" );
74 int firstWord = 1 + ( skipFirst ? 1 : 0 );
75 for( int i = firstWord; i < words.length; i++ )
76 {
77 pathComponents.add( 0, words[i] );
78 }
79 }
80 }
81 }
82 catch( IOException e )
83 {
84 throw new RuntimeException( e );
85 }
86 }
87
88 public void strip()
89 {
90 if( pathComponents.size() > 0 && "text()".equals( pathComponents.get( 0 ) ) )
91 pathComponents.remove( 0 );
92
93 for( int i1 = 0; i1 < pathComponents.size(); i1++ )
94 {
95 String s = pathComponents.get( i1 );
96 if( s.indexOf( '[' ) >= 0 )
97 {
98 StringBuffer buf = new StringBuffer();
99 boolean skip = false;
100 for( int i2 = 0; i2 < s.length(); i2++ )
101 {
102 char ch = s.charAt( i2 );
103 if( ch == '[' )
104 skip = true;
105 if( !skip )
106 buf.append( ch );
107
108 if( ch == ']' )
109 skip = false;
110 }
111
112 s = buf.toString();
113 pathComponents.set( i1, s );
114 }
115 }
116
117 function = null;
118 }
119
120 public XPathData createParent()
121 {
122 if( pathComponents.isEmpty() )
123 return null;
124
125 StringToStringMap nsMap2 = new StringToStringMap( nsMap );
126 ArrayList<String> pathComponents2 = new ArrayList<String>( pathComponents );
127 pathComponents2.remove( 0 );
128 return new XPathData( nsMap2, pathComponents2, absolute );
129 }
130
131 @Override
132 public String toString()
133 {
134 return getShortPath();
135 }
136
137 @Override
138 public boolean equals( Object obj )
139 {
140 if( obj.getClass() != XPathData.class )
141 return false;
142
143 XPathData other = ( XPathData )obj;
144 return this.getHashKey().equals( other.getHashKey() );
145 }
146
147 @Override
148 public int hashCode()
149 {
150 return getHashKey().hashCode();
151 }
152
153 public String getHashKey()
154 {
155 return getCanonicalPath();
156 }
157
158 public StringToStringMap getNamespaceMap()
159 {
160 return nsMap;
161 }
162
163 public List<String> getPathComponents()
164 {
165 return pathComponents;
166 }
167
168 public boolean isAttribute()
169 {
170 if( pathComponents.isEmpty() )
171 return false;
172
173 String c = pathComponents.get( 0 );
174 return c.startsWith( "@" );
175 }
176
177 public String getAttributeName()
178 {
179 String c = pathComponents.get( 0 );
180 return c.substring( 1 );
181 }
182
183 public String getFunction()
184 {
185 return function;
186 }
187
188 public String getXPath()
189 {
190 return buildXPath( null );
191 }
192
193 public String getFullPath()
194 {
195 return buildXPath( null );
196 }
197
198 public String buildXPath( XPathModifier modifier )
199 {
200 StringBuffer xpath = new StringBuffer();
201
202 for( Iterator<String> i = nsMap.keySet().iterator(); i.hasNext(); )
203 {
204 String ns = i.next();
205 xpath.append( "declare namespace " + nsMap.get( ns ) + "='" + ns + "';\n" );
206 }
207
208 if( function != null )
209 xpath.append( function ).append( "(" );
210
211 if( modifier != null )
212 modifier.beforeSelector( xpath );
213
214 String firstComponent = "";
215 if( pathComponents.size() > 0 )
216 firstComponent = pathComponents.get( pathComponents.size() - 1 );
217 if( !absolute && !"".equals( firstComponent ) )
218 xpath.append( "/" );
219
220 for( int c = pathComponents.size() - 1; c >= 0; c-- )
221 {
222 xpath.append( "/" ).append( pathComponents.get( c ) );
223 }
224
225 if( modifier != null )
226 modifier.afterSelector( xpath );
227
228 if( function != null )
229 xpath.append( ")" );
230
231 return xpath.toString();
232 }
233
234 public String getPath()
235 {
236 StringBuffer buf = new StringBuffer();
237 buf.append( "/" );
238
239 for( int c = pathComponents.size() - 1; c >= 0; c-- )
240 {
241 buf.append( "/" ).append( pathComponents.get( c ) );
242 }
243
244 return buf.toString();
245 }
246
247 /*** Get a path with all namespaces replaced. */
248 public String getCanonicalPath()
249 {
250 HashMap<String, String> inverseNsMap = new HashMap<String, String>();
251 for( String key : nsMap.keySet() )
252 {
253 String value = nsMap.get( key );
254 inverseNsMap.put( value, key );
255 }
256
257 StringBuffer buf = new StringBuffer();
258 buf.append( "/" );
259
260 for( int c = pathComponents.size() - 1; c >= 0; c-- )
261 {
262 buf.append( "/" );
263 String s = pathComponents.get( c );
264 String[] words = s.split( ":" );
265 if( words.length == 2 )
266 {
267 String ns = inverseNsMap.get( words[0] );
268 if( ns != null )
269 {
270 buf.append( ns ).append( ":" ).append( words[1] );
271 }
272 else
273 {
274 buf.append( s );
275 }
276 }
277 else
278 {
279 buf.append( s );
280 }
281 }
282
283 return buf.toString();
284 }
285
286 /*** Get a path with no namespaces or namespace prefixes. */
287 public String getShortPath()
288 {
289 StringBuffer buf = new StringBuffer();
290 buf.append( "/" );
291
292 for( int c = pathComponents.size() - 1; c >= 0; c-- )
293 {
294 buf.append( "/" );
295 String s = pathComponents.get( c );
296 String[] words = s.split( ":" );
297 if( words.length == 2 )
298 {
299 buf.append( words[1] );
300 }
301 else
302 {
303 buf.append( s );
304 }
305 }
306
307 return buf.toString();
308 }
309
310 public Collection<String> getNamespaces()
311 {
312 return nsMap.keySet();
313 }
314
315 public boolean hasNamespaces()
316 {
317 return nsMap != null && !nsMap.isEmpty();
318 }
319 }