package org.uiautomation.ios.utils;
import com.google.common.collect.ImmutableMap;
import net.sf.saxon.dom.DOMNodeList;
import net.sf.saxon.lib.NamespaceConstant;
import org.apache.commons.io.IOUtils;
import org.json.JSONException;
import org.json.JSONObject;
import org.openqa.selenium.InvalidSelectorException;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.UnhandledAlertException;
import org.openqa.selenium.WebDriverException;
import org.uiautomation.ios.client.uiamodels.impl.RemoteIOSDriver;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
public class XPath2Engine {
private final XPath xpath2;
private final Node document;
private final String xml;
public static XPath2Engine getXpath2Engine(RemoteIOSDriver driver) {
JSONObject logElementTree = driver.logElementTree(null, false);
JSONObject tree = logElementTree.optJSONObject("tree");
try {
return new XPath2Engine(new JSONToXMLConvertor(tree).asXML());
} catch (Exception e) {
throw new WebDriverException(e);
}
}
public XPath2Engine(String xml) throws Exception {
this.xml = xml;
String objectModel = NamespaceConstant.OBJECT_MODEL_SAXON;
System.setProperty("javax.xml.xpath.XPathFactory:" + objectModel,
"net.sf.saxon.xpath.XPathFactoryImpl");
XPathFactory xpathFactory = XPathFactory.newInstance(objectModel);
/*XPathEvaluator xpath2 = new XPathEvaluator();
net.sf.saxon.Configuration c = new Configuration();
c.getCharacterSetFactory().setCharacterSetImplementation(encoding, charSet)*/
xpath2 = xpathFactory.newXPath();
DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = dfactory.newDocumentBuilder();
//document = docBuilder.parse(new File("/Users/freynaud/Documents/workspace/ios-driver/server/zh.xml"));
document = docBuilder.parse(IOUtils.toInputStream(xml, "UTF-8"));
}
public List<Map<String, String>> findElementsByXpath(String xpath) {
try {
return findElementsByXpath(xpath, document);
} catch (XPathExpressionException e) {
throw new WebDriverException(e);
}
}
public Map<String, String> findElementByXpath(String xpath) {
try {
return findElementByXpath(xpath, document);
} catch (XPathExpressionException e) {
throw new InvalidSelectorException("wrong xpath " + xpath, e);
}
}
public Map<String, String> findElementByXpath(String xpath, String reference) {
try {
return findElementByXpath(xpath, getNode(reference));
} catch (XPathExpressionException e) {
throw new InvalidSelectorException("wrong xpath " + xpath, e);
}
}
public List<Map<String, String>> findElementsByXpath(String xpath, String reference) {
try {
return findElementsByXpath(xpath, getNode(reference));
} catch (XPathExpressionException e) {
throw new InvalidSelectorException("wrong xpath " + xpath, e);
}
}
private List<Map<String, String>> findElementsByXpath(String xpath, Node from)
throws XPathExpressionException {
XPathExpression expr = xpath2.compile(xpath);
DOMNodeList elements;
elements = (DOMNodeList) expr.evaluate(document, XPathConstants.NODESET);
List<Map<String, String>> res = new ArrayList<Map<String, String>>();
for (int i = 0; i < elements.getLength(); i++) {
Element el = (Element) elements.item(i);
String reference = el.getAttribute("ref");
String type = el.getNodeName();
res.add(ImmutableMap.of("ELEMENT", reference, "type", type));
}
return res;
}
public static void main(String[] args)
throws UnsupportedEncodingException, XPathExpressionException, JSONException {
final
String
intlMountainZH =
"{\"ref\":1,\"name\":\"山\",\"value\":null,\"children\":[{\"ref\":7,\"name\":null,\"value\":null,\"children\":[{\"ref\":8,\"name\":\"山 Detail\",\"value\":null,\"children\":[{\"ref\":9,\"name\":null,\"value\":null,\"children\":[{\"ref\":10,\"name\":null,\"value\":null,\"label\":null,\"rect\":{\"origin\":{\"y\":0,\"x\":0},\"size\":{\"height\":3,\"width\":320}},\"type\":\"UIAImage\"}],\"label\":null,\"rect\":{\"origin\":{\"y\":20,\"x\":0},\"size\":{\"height\":44,\"width\":320}},\"type\":\"UIAImage\"},{\"ref\":11,\"name\":\"山\",\"value\":null,\"label\":\"山\",\"rect\":{\"origin\":{\"y\":27,\"x\":5},\"size\":{\"height\":30,\"width\":48}},\"type\":\"UIAButton\"},{\"ref\":12,\"name\":\"山 Detail\",\"value\":\"山 Detail\",\"label\":\"山 Detail\",\"rect\":{\"origin\":{\"y\":29,\"x\":119},\"size\":{\"height\":27,\"width\":82}},\"type\":\"UIAStaticText\"}],\"label\":null,\"rect\":{\"origin\":{\"y\":20,\"x\":0},\"size\":{\"height\":44,\"width\":320}},\"type\":\"UIANavigationBar\"},{\"ref\":13,\"name\":\"山 1 是8,848米高。它第一次攀登了在29 May 1953。\",\"value\":\"山 1 是8,848米高。它第一次攀登了在29 May 1953。\",\"label\":\"山 1 是8,848米高。它第一次攀登了在29 May 1953。\",\"rect\":{\"origin\":{\"y\":168,\"x\":34},\"size\":{\"height\":164,\"width\":251}},\"type\":\"UIAStaticText\"},{\"ref\":14,\"name\":null,\"value\":null,\"children\":[{\"ref\":15,\"name\":null,\"value\":null,\"label\":null,\"rect\":{\"origin\":{\"y\":477,\"x\":0},\"size\":{\"height\":3,\"width\":320}},\"type\":\"UIAImage\"},{\"ref\":16,\"name\":null,\"value\":null,\"label\":null,\"rect\":{\"origin\":{\"y\":480,\"x\":0},\"size\":{\"height\":44,\"width\":320}},\"type\":\"UIAImage\"}],\"label\":null,\"rect\":{\"origin\":{\"y\":480,\"x\":0},\"size\":{\"height\":44,\"width\":320}},\"type\":\"UIAToolbar\"}],\"label\":null,\"rect\":{\"origin\":{\"y\":0,\"x\":0},\"size\":{\"height\":480,\"width\":320}},\"type\":\"UIAWindow\"},{\"ref\":17,\"name\":null,\"value\":null,\"children\":[{\"ref\":18,\"name\":null,\"value\":null,\"children\":[{\"ref\":19,\"name\":\"使用三指向下滑過螢幕來顯示通知中心。, 點兩下來捲動到頁首\",\"value\":null,\"label\":null,\"rect\":{\"origin\":{\"y\":0,\"x\":4},\"size\":{\"height\":20,\"width\":52}},\"type\":\"UIAElement\"},{\"ref\":20,\"name\":\"Wi-Fi 訊號強度 3 格(共 3 格)\",\"value\":null,\"label\":\"Wi-Fi 訊號強度 3 格(共 3 格)\",\"rect\":{\"origin\":{\"y\":0,\"x\":61},\"size\":{\"height\":20,\"width\":20}},\"type\":\"UIAElement\"},{\"ref\":21,\"name\":\"14:17\",\"value\":null,\"label\":\"14:17\",\"rect\":{\"origin\":{\"y\":0,\"x\":142},\"size\":{\"height\":20,\"width\":36}},\"type\":\"UIAElement\"},{\"ref\":22,\"name\":\"電池電力 100%\",\"value\":null,\"label\":\"電池電力 100%\",\"rect\":{\"origin\":{\"y\":0,\"x\":296},\"size\":{\"height\":20,\"width\":21}},\"type\":\"UIAElement\"},{\"ref\":23,\"name\":null,\"value\":null,\"label\":null,\"rect\":{\"origin\":{\"y\":0,\"x\":0},\"size\":{\"height\":3,\"width\":3}},\"type\":\"UIAImage\"},{\"ref\":24,\"name\":null,\"value\":null,\"label\":null,\"rect\":{\"origin\":{\"y\":0,\"x\":317},\"size\":{\"height\":3,\"width\":3}},\"type\":\"UIAImage\"}],\"label\":null,\"rect\":{\"origin\":{\"y\":0,\"x\":0},\"size\":{\"height\":20,\"width\":320}},\"type\":\"UIAStatusBar\"},{\"ref\":25,\"name\":null,\"value\":null,\"label\":null,\"rect\":{\"origin\":{\"y\":477,\"x\":0},\"size\":{\"height\":3,\"width\":3}},\"type\":\"UIAImage\"},{\"ref\":26,\"name\":null,\"value\":null,\"label\":null,\"rect\":{\"origin\":{\"y\":477,\"x\":317},\"size\":{\"height\":3,\"width\":3}},\"type\":\"UIAImage\"}],\"label\":null,\"rect\":{\"origin\":{\"y\":0,\"x\":0},\"size\":{\"height\":480,\"width\":320}},\"type\":\"UIAWindow\"}],\"label\":\"山\",\"rect\":{\"origin\":{\"y\":20,\"x\":0},\"size\":{\"height\":460,\"width\":320}},\"type\":\"UIAApplication\"}";
String xml = new JSONToXMLConvertor(new JSONObject(intlMountainZH)).asXML();
InputStream is = new ByteArrayInputStream(xml.getBytes("UTF8"));
XPath xpath = XPathFactory.newInstance().newXPath();
String xpathExpression = "/UIAApplication";
InputSource inputSource = new InputSource(is);
String handle = xpath.evaluate(xpathExpression, inputSource);
}
private Map<String, String> findElementByXpath(String xpath, Node from)
throws XPathExpressionException {
XPathExpression expr = xpath2.compile(xpath);
Element el = (Element) expr.evaluate(document, XPathConstants.NODE);
if (el == null) {
throw new NoSuchElementException("No element for xpath " + xpath);
}
checkIfElementIsAccessible(el);
String reference = el.getAttribute("ref");
String type = el.getNodeName();
return ImmutableMap.of("ELEMENT", reference, "type", type);
}
private void checkIfElementIsAccessible(Element el) throws XPathExpressionException {
XPathExpression expr = xpath2.compile("//UIAAlert | //UIAActionSheet ");
Element alert = (Element) expr.evaluate(document, XPathConstants.NODE);
if (alert == null) {
// no alert, no problem
return;
}
// alert detected. The xpath should only return what's in the alert.
Node parent = el;
while (parent != null) {
if (parent.getNodeName().equals("UIAAlert") || parent.getNodeName()
.equals("UIAActionSheet")) {
return;
}
parent = parent.getParentNode();
}
throw new UnhandledAlertException("cannot select this element. There is an alert.");
}
private Node getNode(String reference) throws XPathExpressionException {
String xpath = "//*[@ref=" + reference + "]";
XPathExpression expr = xpath2.compile(xpath);
Node n = (Node) expr.evaluate(document, XPathConstants.NODE);
if (n == null) {
throw new NoSuchElementException("Element with ref " + reference + " doesn't exist.");
}
return n;
}
}