/*
* 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();
}
}