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 }