/* * SoapUI, Copyright (C) 2004-2016 SmartBear Software * * Licensed under the EUPL, Version 1.1 or - as soon as they will be approved by the European Commission - subsequent * versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * * http://ec.europa.eu/idabc/eupl * * Unless required by applicable law or agreed to in writing, software distributed under the Licence is * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the Licence for the specific language governing permissions and limitations * under the Licence. */ package com.eviware.soapui.support.xml; import com.eviware.soapui.support.types.StringToStringMap; import java.io.IOException; import java.io.LineNumberReader; import java.io.StringReader; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; /** * @author lars */ public class XPathData { private StringToStringMap nsMap = new StringToStringMap(); private List<String> pathComponents = new ArrayList<String>(); private String function; private boolean absolute = false; public XPathData(StringToStringMap nsMap, List<String> pathComponents, boolean absolute) { this.nsMap = nsMap; this.pathComponents = pathComponents; this.absolute = absolute; } public XPathData(String xpath, boolean skipFirst) { try { LineNumberReader reader = new LineNumberReader(new StringReader(xpath.trim())); String s; while ((s = reader.readLine()) != null) { if (s.startsWith("declare namespace ")) { String[] words = s.substring("declare namespace ".length()).split("="); String prefix = words[0]; int ix1 = words[1].indexOf('\''); int ix2 = words[1].lastIndexOf('\''); String ns = words[1].substring(ix1 + 1, ix2 - ix1); nsMap.put(ns, prefix); } else { if (s.startsWith("count(") && s.endsWith(")")) { function = "count"; s = s.substring("count(".length(), s.length() - ")".length()); } else if (s.startsWith("exists(") && s.endsWith(")")) { function = "exists"; s = s.substring("exists(".length(), s.length() - ")".length()); } String[] words = s.split("/"); int firstWord = 1 + (skipFirst ? 1 : 0); for (int i = firstWord; i < words.length; i++) { pathComponents.add(0, words[i]); } } } } catch (IOException e) { throw new RuntimeException(e); } } public void strip() { if (pathComponents.size() > 0 && "text()".equals(pathComponents.get(0))) { pathComponents.remove(0); } for (int i1 = 0; i1 < pathComponents.size(); i1++) { String s = pathComponents.get(i1); if (s.indexOf('[') >= 0) { StringBuffer buf = new StringBuffer(); boolean skip = false; for (int i2 = 0; i2 < s.length(); i2++) { char ch = s.charAt(i2); if (ch == '[') { skip = true; } if (!skip) { buf.append(ch); } if (ch == ']') { skip = false; } } s = buf.toString(); pathComponents.set(i1, s); } } function = null; } public XPathData createParent() { if (pathComponents.isEmpty()) { return null; } StringToStringMap nsMap2 = new StringToStringMap(nsMap); ArrayList<String> pathComponents2 = new ArrayList<String>(pathComponents); pathComponents2.remove(0); return new XPathData(nsMap2, pathComponents2, absolute); } @Override public String toString() { return getShortPath(); } @Override public boolean equals(Object obj) { if (obj == null || obj.getClass() != XPathData.class) { return false; } XPathData other = (XPathData) obj; return this.getHashKey().equals(other.getHashKey()); } @Override public int hashCode() { return getHashKey().hashCode(); } public String getHashKey() { return getCanonicalPath(); } public StringToStringMap getNamespaceMap() { return nsMap; } public List<String> getPathComponents() { return pathComponents; } public boolean isAttribute() { if (pathComponents.isEmpty()) { return false; } String c = pathComponents.get(0); return c.startsWith("@"); } public String getAttributeName() { String c = pathComponents.get(0); return c.substring(1); } public String getFunction() { return function; } public String getXPath() { return buildXPath(null); } public String getFullPath() { return buildXPath(null); } public String buildXPath(XPathModifier modifier) { StringBuffer xpath = new StringBuffer(); for (Iterator<String> i = nsMap.keySet().iterator(); i.hasNext(); ) { String ns = i.next(); xpath.append("declare namespace " + nsMap.get(ns) + "='" + ns + "';\n"); } if (function != null) { xpath.append(function).append("("); } if (modifier != null) { modifier.beforeSelector(xpath); } String firstComponent = ""; if (pathComponents.size() > 0) { firstComponent = pathComponents.get(pathComponents.size() - 1); } if (!absolute && !"".equals(firstComponent)) { xpath.append("/"); } for (int c = pathComponents.size() - 1; c >= 0; c--) { xpath.append("/").append(pathComponents.get(c)); } if (modifier != null) { modifier.afterSelector(xpath); } if (function != null) { xpath.append(")"); } return xpath.toString(); } public String getPath() { StringBuffer buf = new StringBuffer(); buf.append("/"); for (int c = pathComponents.size() - 1; c >= 0; c--) { buf.append("/").append(pathComponents.get(c)); } return buf.toString(); } /** * Get a path with all namespaces replaced. */ public String getCanonicalPath() { HashMap<String, String> inverseNsMap = new HashMap<String, String>(); for (String key : nsMap.keySet()) { String value = nsMap.get(key); inverseNsMap.put(value, key); } StringBuffer buf = new StringBuffer(); buf.append("/"); for (int c = pathComponents.size() - 1; c >= 0; c--) { buf.append("/"); String s = pathComponents.get(c); String[] words = s.split(":"); if (words.length == 2) { String ns = inverseNsMap.get(words[0]); if (ns != null) { buf.append(ns).append(":").append(words[1]); } else { buf.append(s); } } else { buf.append(s); } } return buf.toString(); } /** * Get a path with no namespaces or namespace prefixes. */ public String getShortPath() { StringBuffer buf = new StringBuffer(); buf.append("/"); for (int c = pathComponents.size() - 1; c >= 0; c--) { buf.append("/"); String s = pathComponents.get(c); String[] words = s.split(":"); if (words.length == 2) { buf.append(words[1]); } else { buf.append(s); } } return buf.toString(); } public Collection<String> getNamespaces() { return nsMap.keySet(); } public boolean hasNamespaces() { return nsMap != null && !nsMap.isEmpty(); } }