LayoutFileParser.java revision 0cb9fbb96197af013f4f879ed6cddf2681b88fd6
13bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar/* 23bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar * Copyright (C) 2015 The Android Open Source Project 33bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License"); 43bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar * you may not use this file except in compliance with the License. 53bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar * You may obtain a copy of the License at 63bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar * http://www.apache.org/licenses/LICENSE-2.0 73bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar * Unless required by applicable law or agreed to in writing, software 83bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS, 93bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 103bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar * See the License for the specific language governing permissions and 113bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar * limitations under the License. 123bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar */ 133bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar 14fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountpackage android.databinding.tool.store; 153bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar 163bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyarimport com.google.common.base.Preconditions; 173bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar 183bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyarimport org.apache.commons.io.FileUtils; 19895b618d9c6e3deb56465d0759cda57f50c46214Yigit Boyarimport org.apache.commons.lang3.ObjectUtils; 203bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyarimport org.apache.commons.lang3.StringUtils; 213bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyarimport org.w3c.dom.Document; 223bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyarimport org.w3c.dom.NamedNodeMap; 233bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyarimport org.w3c.dom.Node; 243bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyarimport org.w3c.dom.NodeList; 253bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyarimport org.xml.sax.SAXException; 263bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar 27fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.util.L; 28fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.util.ParserHelper; 29fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.util.XmlEditor; 30fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mount 313bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyarimport java.io.File; 323bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyarimport java.io.IOException; 3375da807698ae0f3b857d8160541cb03bb5ee1028Yigit Boyarimport java.net.MalformedURLException; 3475da807698ae0f3b857d8160541cb03bb5ee1028Yigit Boyarimport java.net.URISyntaxException; 3575da807698ae0f3b857d8160541cb03bb5ee1028Yigit Boyarimport java.net.URL; 363bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyarimport java.util.ArrayList; 3796e1c821dd446d1ed78f8ae61131550588f60a24George Mountimport java.util.HashMap; 383bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyarimport java.util.List; 393bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar 403bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyarimport javax.xml.parsers.DocumentBuilder; 413bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyarimport javax.xml.parsers.DocumentBuilderFactory; 423bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyarimport javax.xml.parsers.ParserConfigurationException; 433bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyarimport javax.xml.xpath.XPath; 443bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyarimport javax.xml.xpath.XPathConstants; 453bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyarimport javax.xml.xpath.XPathExpression; 463bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyarimport javax.xml.xpath.XPathExpressionException; 473bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyarimport javax.xml.xpath.XPathFactory; 483bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar 493bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar/** 503bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar * Gets the list of XML files and creates a list of 51fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mount * {@link android.databinding.tool.store.ResourceBundle} that can be persistent or converted to 523bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar * LayoutBinder. 533bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar */ 543bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyarpublic class LayoutFileParser { 553b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount private static final String XPATH_VARIABLE_DEFINITIONS = "/layout/data/variable"; 563b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount private static final String XPATH_BINDING_ELEMENTS = "/layout/*[name() != 'data' and name() != 'merge'] | /layout/merge/* | //*[include/.. or @*[starts-with(., '@{') and substring(., string-length(.)) = '}']]"; 5795d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount private static final String XPATH_ID_ELEMENTS = "//*[@*[local-name()='id']]"; 583b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount private static final String XPATH_IMPORT_DEFINITIONS = "/layout/data/import"; 593b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount private static final String XPATH_MERGE_ELEMENT = "/layout/merge"; 603b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount private static final String XPATH_BINDING_LAYOUT = "/layout"; 613b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount private static final String XPATH_BINDING_CLASS = "/layout/data/@class"; 623bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar final String LAYOUT_PREFIX = "@layout/"; 633bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar 644d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount public ResourceBundle.LayoutFileBundle parseXml(File xml, String pkg, int minSdk) 653bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar throws ParserConfigurationException, IOException, SAXException, 663bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar XPathExpressionException { 670cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar final String xmlNoExtension = ParserHelper.stripExtension(xml.getName()); 680390898cf7c4fcad255e8cfd6802f722b516cb2cGeorge Mount final String newTag = xml.getParentFile().getName() + '/' + xmlNoExtension; 690390898cf7c4fcad255e8cfd6802f722b516cb2cGeorge Mount File original = stripFileAndGetOriginal(xml, newTag); 703bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar if (original == null) { 71895b618d9c6e3deb56465d0759cda57f50c46214Yigit Boyar L.d("assuming the file is the original for %s", xml.getAbsoluteFile()); 72895b618d9c6e3deb56465d0759cda57f50c46214Yigit Boyar original = xml; 733bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar } 743bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar L.d("parsing file %s", xml.getAbsolutePath()); 753bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar 763bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 773bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar final DocumentBuilder builder = factory.newDocumentBuilder(); 788e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount final Document doc = builder.parse(original); 793bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar 803bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar final XPathFactory xPathFactory = XPathFactory.newInstance(); 813bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar final XPath xPath = xPathFactory.newXPath(); 823bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar 833b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount if (!isBindingLayout(doc, xPath)) { 843b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount return null; 853b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount } 863b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount 873bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar List<Node> variableNodes = getVariableNodes(doc, xPath); 8896e1c821dd446d1ed78f8ae61131550588f60a24George Mount final List<Node> imports = getImportNodes(doc, xPath); 8996e1c821dd446d1ed78f8ae61131550588f60a24George Mount 9096e1c821dd446d1ed78f8ae61131550588f60a24George Mount ResourceBundle.LayoutFileBundle bundle = new ResourceBundle.LayoutFileBundle( 9196e1c821dd446d1ed78f8ae61131550588f60a24George Mount xmlNoExtension, xml.getParentFile().getName(), pkg, isMerge(doc, xPath)); 923bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar 933bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar L.d("number of variable nodes %d", variableNodes.size()); 943bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar for (Node item : variableNodes) { 953bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar L.d("reading variable node %s", item); 963bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar NamedNodeMap attributes = item.getAttributes(); 973bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar String variableName = attributes.getNamedItem("name").getNodeValue(); 983bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar String variableType = attributes.getNamedItem("type").getNodeValue(); 993bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar L.d("name: %s, type:%s", variableName, variableType); 1003bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar bundle.addVariable(variableName, variableType); 1013bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar } 1023bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar 1033bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar L.d("import node count %d", imports.size()); 1043bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar for (Node item : imports) { 1053bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar NamedNodeMap attributes = item.getAttributes(); 1063bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar String type = attributes.getNamedItem("type").getNodeValue(); 1073bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar final Node aliasNode = attributes.getNamedItem("alias"); 1083bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar final String alias; 1093bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar if (aliasNode == null) { 1103bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar final String[] split = StringUtils.split(type, '.'); 1113bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar alias = split[split.length - 1]; 1123bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar } else { 1133bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar alias = aliasNode.getNodeValue(); 1143bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar } 1153bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar bundle.addImport(alias, type); 1163bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar } 1173bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar 1183b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount final Node layoutParent = getLayoutParent(doc, xPath); 1193bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar final List<Node> bindingNodes = getBindingNodes(doc, xPath); 12096e1c821dd446d1ed78f8ae61131550588f60a24George Mount final HashMap<Node, String> nodeTagMap = new HashMap<Node, String>(); 1213bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar L.d("number of binding nodes %d", bindingNodes.size()); 1223b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount int tagNumber = 0; 1233bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar for (Node parent : bindingNodes) { 1243bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar NamedNodeMap attributes = parent.getAttributes(); 1258e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount String nodeName = parent.getNodeName(); 1267ff60c24c6de7ba0c674fe65a82ad4a88dab2e5dGeorge Mount String viewName = null; 1278e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount String includedLayoutName = null; 1288e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount final Node id = attributes.getNamedItem("android:id"); 12996e1c821dd446d1ed78f8ae61131550588f60a24George Mount final String tag; 1304d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount final Node originalTag = attributes.getNamedItem("android:tag"); 1318e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount if ("include".equals(nodeName)) { 1328e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount // get the layout attribute 1338e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount final Node includedLayout = attributes.getNamedItem("layout"); 1348e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount Preconditions.checkNotNull(includedLayout, "must include a layout"); 1358e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount final String includeValue = includedLayout.getNodeValue(); 1368e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount Preconditions.checkArgument(includeValue.startsWith(LAYOUT_PREFIX)); 1378e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount // if user is binding something there, there MUST be a layout file to be 1388e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount // generated. 1398e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount String layoutName = includeValue.substring(LAYOUT_PREFIX.length()); 1408e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount includedLayoutName = layoutName; 14196e1c821dd446d1ed78f8ae61131550588f60a24George Mount tag = nodeTagMap.get(parent.getParentNode()); 1424d4979490e1fa374c0d7f3599fed0a9e83a579d0George Mount } else if ("fragment".equals(nodeName)) { 143d6527ee28cc3aa05818799af8def9593346f91bcGeorge Mount L.e("fragments do not support data binding expressions: %s", xml.getPath()); 144d6527ee28cc3aa05818799af8def9593346f91bcGeorge Mount continue; 1453bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar } else { 1467ff60c24c6de7ba0c674fe65a82ad4a88dab2e5dGeorge Mount viewName = getViewName(parent); 1473b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount if (layoutParent == parent.getParentNode()) { 1483b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount tag = newTag + "_" + tagNumber; 14996e1c821dd446d1ed78f8ae61131550588f60a24George Mount } else { 1507ff60c24c6de7ba0c674fe65a82ad4a88dab2e5dGeorge Mount tag = "binding_" + tagNumber; 15196e1c821dd446d1ed78f8ae61131550588f60a24George Mount } 1523b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount tagNumber++; 1538e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount } 1548e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount final ResourceBundle.BindingTargetBundle bindingTargetBundle = 1558e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount bundle.createBindingTarget(id == null ? null : id.getNodeValue(), 1567ff60c24c6de7ba0c674fe65a82ad4a88dab2e5dGeorge Mount viewName, true, tag, originalTag == null ? null : originalTag.getNodeValue()); 15796e1c821dd446d1ed78f8ae61131550588f60a24George Mount nodeTagMap.put(parent, tag); 1588e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount bindingTargetBundle.setIncludedLayout(includedLayoutName); 1598e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount 1608e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount final int attrCount = attributes.getLength(); 1618e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount for (int i = 0; i < attrCount; i ++) { 1628e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount final Node attr = attributes.item(i); 1638e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount String value = attr.getNodeValue(); 1648e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount if (value.charAt(0) == '@' && value.charAt(1) == '{' && 1658e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount value.charAt(value.length() - 1) == '}') { 1668e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount final String strippedValue = value.substring(2, value.length() - 1); 1678e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount bindingTargetBundle.addBinding(attr.getNodeName(), strippedValue); 1688e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount } 1693bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar } 1703bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar } 1713bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar 17296e1c821dd446d1ed78f8ae61131550588f60a24George Mount final List<Node> idNodes = getNakedIds(doc, xPath); 17396e1c821dd446d1ed78f8ae61131550588f60a24George Mount for (Node node : idNodes) { 174d6527ee28cc3aa05818799af8def9593346f91bcGeorge Mount if (!bindingNodes.contains(node) && !"include".equals(node.getNodeName()) && 175d6527ee28cc3aa05818799af8def9593346f91bcGeorge Mount !"fragment".equals(node.getNodeName())) { 17696e1c821dd446d1ed78f8ae61131550588f60a24George Mount final Node id = node.getAttributes().getNamedItem("android:id"); 1777ff60c24c6de7ba0c674fe65a82ad4a88dab2e5dGeorge Mount final String className = getViewName(node); 17896e1c821dd446d1ed78f8ae61131550588f60a24George Mount bundle.createBindingTarget(id.getNodeValue(), className, true, null, null); 17995d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount } 18095d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount } 18195d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount 1823b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount bundle.setBindingClass(getBindingClass(doc, xPath, original)); 1833b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount 1843bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar return bundle; 1853bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar } 1863bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar 1873b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount private boolean isBindingLayout(Document doc, XPath xPath) throws XPathExpressionException { 1883b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount return !get(doc, xPath, XPATH_BINDING_LAYOUT).isEmpty(); 1893b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount } 1903b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount 1913b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount private Node getLayoutParent(Document doc, XPath xPath) throws XPathExpressionException { 1923b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount if (isMerge(doc, xPath)) { 1933b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount return get(doc, xPath, XPATH_MERGE_ELEMENT).get(0); 1943b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount } else { 1953b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount return get(doc, xPath, XPATH_BINDING_LAYOUT).get(0); 1963b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount } 1973b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount } 1983b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount 1993b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount private String getBindingClass(Document doc, XPath xPath, File file) 2003b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount throws XPathExpressionException { 2013b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount List<Node> nodes = get(doc, xPath, XPATH_BINDING_CLASS); 2023b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount if (nodes.isEmpty()) { 2033b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount return null; 2043b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount } 2053b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount if (nodes.size() > 1) { 2063b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount L.e("More than one binding class declared in %s", file.getAbsolutePath()); 2073b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount } 2083b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount return nodes.get(0).getNodeValue(); 2093b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount } 2103b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount 2113bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar private List<Node> getBindingNodes(Document doc, XPath xPath) throws XPathExpressionException { 2123bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar return get(doc, xPath, XPATH_BINDING_ELEMENTS); 2133bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar } 2143bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar 2153bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar private List<Node> getVariableNodes(Document doc, XPath xPath) throws XPathExpressionException { 2163bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar return get(doc, xPath, XPATH_VARIABLE_DEFINITIONS); 2173bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar } 2183bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar 2193bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar private List<Node> getImportNodes(Document doc, XPath xPath) throws XPathExpressionException { 2203bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar return get(doc, xPath, XPATH_IMPORT_DEFINITIONS); 2213bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar } 2223bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar 22395d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount private List<Node> getNakedIds(Document doc, XPath xPath) throws XPathExpressionException { 22495d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount return get(doc, xPath, XPATH_ID_ELEMENTS); 22595d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount } 22695d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount 22796e1c821dd446d1ed78f8ae61131550588f60a24George Mount private boolean isMerge(Document doc, XPath xPath) throws XPathExpressionException { 22896e1c821dd446d1ed78f8ae61131550588f60a24George Mount return !get(doc, xPath, XPATH_MERGE_ELEMENT).isEmpty(); 22996e1c821dd446d1ed78f8ae61131550588f60a24George Mount } 23096e1c821dd446d1ed78f8ae61131550588f60a24George Mount 2313bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar private List<Node> get(Document doc, XPath xPath, String pattern) 2323bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar throws XPathExpressionException { 2333bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar final XPathExpression expr = xPath.compile(pattern); 2343bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar return toList((NodeList) expr.evaluate(doc, XPathConstants.NODESET)); 2353bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar } 2363bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar 2373bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar private List<Node> toList(NodeList nodeList) { 238895b618d9c6e3deb56465d0759cda57f50c46214Yigit Boyar List<Node> result = new ArrayList<Node>(); 2393bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar for (int i = 0; i < nodeList.getLength(); i ++) { 2403bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar result.add(nodeList.item(i)); 2413bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar } 2423bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar return result; 2433bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar } 2443bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar 2457ff60c24c6de7ba0c674fe65a82ad4a88dab2e5dGeorge Mount private String getViewName(Node viewNode) { 24695d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount String viewName = viewNode.getNodeName(); 24795d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount if ("view".equals(viewName)) { 24895d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount Node classNode = viewNode.getAttributes().getNamedItem("class"); 24995d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount if (classNode == null) { 25095d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount L.e("No class attribute for 'view' node"); 25195d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount } else { 25295d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount viewName = classNode.getNodeValue(); 25395d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount } 25495d1b38adeb5963ec5337e7dd6177b4bb5a03619George Mount } 2553bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar return viewName; 2563bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar } 2573bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar 2583bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar private void stripBindingTags(File xml, String newTag) throws IOException { 2590cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String res = XmlEditor.strip(xml, newTag); 2603bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar if (res != null) { 2613bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar L.d("file %s has changed, overwriting %s", xml.getName(), xml.getAbsolutePath()); 2623bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar FileUtils.writeStringToFile(xml, res); 2633bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar } 2643bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar } 2653bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar 2663bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar private File stripFileAndGetOriginal(File xml, String binderId) 2673bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar throws ParserConfigurationException, IOException, SAXException, 2683bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar XPathExpressionException { 2698e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount L.d("parsing resource file %s", xml.getAbsolutePath()); 2703bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 2713bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar DocumentBuilder builder = factory.newDocumentBuilder(); 2723bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar Document doc = builder.parse(xml); 2733bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar XPathFactory xPathFactory = XPathFactory.newInstance(); 2743bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar XPath xPath = xPathFactory.newXPath(); 2753bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar final XPathExpression commentElementExpr = xPath 2763bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar .compile("//comment()[starts-with(., \" From: file:\")][last()]"); 2773bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar final NodeList commentElementNodes = (NodeList) commentElementExpr 2783bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar .evaluate(doc, XPathConstants.NODESET); 2793bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar L.d("comment element nodes count %s", commentElementNodes.getLength()); 2803bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar if (commentElementNodes.getLength() == 0) { 2813bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar L.d("cannot find comment element to find the actual file"); 2823bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar return null; 2833bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar } 2843bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar final Node first = commentElementNodes.item(0); 28575da807698ae0f3b857d8160541cb03bb5ee1028Yigit Boyar String actualFilePath = first.getNodeValue().substring(" From:".length()).trim(); 2863bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar L.d("actual file to parse: %s", actualFilePath); 28775da807698ae0f3b857d8160541cb03bb5ee1028Yigit Boyar File actualFile = urlToFile(new java.net.URL(actualFilePath)); 2883bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar if (!actualFile.canRead()) { 2893bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar L.d("cannot find original, skipping. %s", actualFile.getAbsolutePath()); 2903bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar return null; 2913bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar } 2923bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar 2933bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar // now if file has any binding expressions, find and delete them 2943bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar // TODO we should rely on namespace to avoid parsing file twice 2953b8fedce6e9ee814ffe7b63d07aa5456f08fb9b2George Mount boolean changed = isBindingLayout(doc, xPath); 2963bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar if (changed) { 2973bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar stripBindingTags(xml, binderId); 2983bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar } 2993bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar return actualFile; 3003bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar } 30175da807698ae0f3b857d8160541cb03bb5ee1028Yigit Boyar 30275da807698ae0f3b857d8160541cb03bb5ee1028Yigit Boyar public static File urlToFile(String url) throws MalformedURLException { 30375da807698ae0f3b857d8160541cb03bb5ee1028Yigit Boyar return urlToFile(new URL(url)); 30475da807698ae0f3b857d8160541cb03bb5ee1028Yigit Boyar } 30575da807698ae0f3b857d8160541cb03bb5ee1028Yigit Boyar 30675da807698ae0f3b857d8160541cb03bb5ee1028Yigit Boyar public static File urlToFile(URL url) throws MalformedURLException { 30775da807698ae0f3b857d8160541cb03bb5ee1028Yigit Boyar try { 30875da807698ae0f3b857d8160541cb03bb5ee1028Yigit Boyar return new File(url.toURI()); 30975da807698ae0f3b857d8160541cb03bb5ee1028Yigit Boyar } 31075da807698ae0f3b857d8160541cb03bb5ee1028Yigit Boyar catch (IllegalArgumentException e) { 31175da807698ae0f3b857d8160541cb03bb5ee1028Yigit Boyar MalformedURLException ex = new MalformedURLException(e.getLocalizedMessage()); 31275da807698ae0f3b857d8160541cb03bb5ee1028Yigit Boyar ex.initCause(e); 31375da807698ae0f3b857d8160541cb03bb5ee1028Yigit Boyar throw ex; 31475da807698ae0f3b857d8160541cb03bb5ee1028Yigit Boyar } 31575da807698ae0f3b857d8160541cb03bb5ee1028Yigit Boyar catch (URISyntaxException e) { 31675da807698ae0f3b857d8160541cb03bb5ee1028Yigit Boyar return new File(url.getPath()); 31775da807698ae0f3b857d8160541cb03bb5ee1028Yigit Boyar } 31875da807698ae0f3b857d8160541cb03bb5ee1028Yigit Boyar } 3193bd87eef217d80a233677d7e267224d0ed3c2c55Yigit Boyar} 320