View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2008 eviware.com 
3    *
4    *  soapUI is free software; you can redistribute it and/or modify it under the 
5    *  terms of version 2.1 of the GNU Lesser General Public License as published by 
6    *  the Free Software Foundation.
7    *
8    *  soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without 
9    *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
10   *  See the GNU Lesser General Public License for more details at gnu.org.
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 }