10cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar/* 20cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar * Copyright (C) 2014 The Android Open Source Project 30cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar * 40cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License"); 50cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar * you may not use this file except in compliance with the License. 60cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar * You may obtain a copy of the License at 70cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar * 80cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar * http://www.apache.org/licenses/LICENSE-2.0 90cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar * 100cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar * Unless required by applicable law or agreed to in writing, software 110cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS, 120cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 130cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar * See the License for the specific language governing permissions and 140cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar * limitations under the License. 150cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar */ 160cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 170cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarpackage android.databinding.tool.util; 180cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 19d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mountimport android.databinding.parser.BindingExpressionLexer; 20d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mountimport android.databinding.parser.BindingExpressionParser; 21d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mountimport android.databinding.parser.XMLLexer; 22d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mountimport android.databinding.parser.XMLParser; 23d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mountimport android.databinding.parser.XMLParser.AttributeContext; 24d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mountimport android.databinding.parser.XMLParser.ElementContext; 25d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount 264ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Guptaimport com.google.common.base.Joiner; 274ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Guptaimport com.google.common.xml.XmlEscapers; 284ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta 290cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarimport org.antlr.v4.runtime.ANTLRInputStream; 300cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarimport org.antlr.v4.runtime.CommonTokenStream; 310cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarimport org.antlr.v4.runtime.Token; 320cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarimport org.antlr.v4.runtime.tree.TerminalNode; 330cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarimport org.apache.commons.io.FileUtils; 340cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 350cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarimport java.io.File; 36f1081f6a15e6b905701bd3bbcb5d598731d05afbGeorge Mountimport java.io.FileInputStream; 370cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarimport java.io.IOException; 38f1081f6a15e6b905701bd3bbcb5d598731d05afbGeorge Mountimport java.io.InputStreamReader; 390cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarimport java.util.ArrayList; 400cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarimport java.util.Collections; 410cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarimport java.util.Comparator; 420cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarimport java.util.List; 430cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 440cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar/** 450cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar * Ugly inefficient class to strip unwanted tags from XML. 460cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar * Band-aid solution to unblock development 470cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar */ 480cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarpublic class XmlEditor { 490cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 50f1081f6a15e6b905701bd3bbcb5d598731d05afbGeorge Mount public static String strip(File f, String newTag, String encoding) throws IOException { 51f1081f6a15e6b905701bd3bbcb5d598731d05afbGeorge Mount FileInputStream fin = new FileInputStream(f); 52f1081f6a15e6b905701bd3bbcb5d598731d05afbGeorge Mount InputStreamReader reader = new InputStreamReader(fin, encoding); 53f1081f6a15e6b905701bd3bbcb5d598731d05afbGeorge Mount ANTLRInputStream inputStream = new ANTLRInputStream(reader); 540cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar XMLLexer lexer = new XMLLexer(inputStream); 550cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar CommonTokenStream tokenStream = new CommonTokenStream(lexer); 560cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar XMLParser parser = new XMLParser(tokenStream); 570cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar XMLParser.DocumentContext expr = parser.document(); 584ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta ElementContext root = expr.element(); 590cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 600cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar if (root == null || !"layout".equals(nodeName(root))) { 610cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return null; // not a binding layout 620cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 630cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 640c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar List<? extends ElementContext> childrenOfRoot = elements(root); 654ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta List<? extends ElementContext> dataNodes = filterNodesByName("data", childrenOfRoot); 660c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar if (dataNodes.size() > 1) { 670c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar L.e("Multiple binding data tags in %s. Expecting a maximum of one.", 680c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar f.getAbsolutePath()); 690c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar } 700cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 719784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar ArrayList<String> lines = new ArrayList<String>(); 720cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar lines.addAll(FileUtils.readLines(f, "utf-8")); 730cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 744ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta for (ElementContext it : dataNodes) { 750cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar replace(lines, toPosition(it.getStart()), toEndPosition(it.getStop()), ""); 760cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 774ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta List<? extends ElementContext> layoutNodes = 780c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar excludeNodesByName("data", childrenOfRoot); 790c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar if (layoutNodes.size() != 1) { 800c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar L.e("Only one layout element and one data element are allowed. %s has %d", 810c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar f.getAbsolutePath(), layoutNodes.size()); 820c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar } 830cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 844ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta final ElementContext layoutNode = layoutNodes.get(0); 850cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 864ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta ArrayList<TagAndContext> noTag = new ArrayList<TagAndContext>(); 870cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 880cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar recurseReplace(layoutNode, lines, noTag, newTag, 0); 890cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 900cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar // Remove the <layout> 910cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar Position rootStartTag = toPosition(root.getStart()); 920cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar Position rootEndTag = toPosition(root.content().getStart()); 930cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar replace(lines, rootStartTag, rootEndTag, ""); 940cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 950cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar // Remove the </layout> 964ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta PositionPair endLayoutPositions = findTerminalPositions(root, lines); 970cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar replace(lines, endLayoutPositions.left, endLayoutPositions.right, ""); 980cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 990cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar StringBuilder rootAttributes = new StringBuilder(); 1000cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar for (AttributeContext attr : attributes(root)) { 1010cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar rootAttributes.append(' ').append(attr.getText()); 1020cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 1034ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta TagAndContext noTagRoot = null; 1044ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta for (TagAndContext tagAndContext : noTag) { 1054ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta if (tagAndContext.getContext() == layoutNode) { 1064ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta noTagRoot = tagAndContext; 1070c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar break; 1080c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar } 1090c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar } 1100c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar if (noTagRoot != null) { 1114ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta TagAndContext newRootTag = new TagAndContext( 1124ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta noTagRoot.getTag() + rootAttributes.toString(), layoutNode); 1130cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar int index = noTag.indexOf(noTagRoot); 1140cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar noTag.set(index, newRootTag); 1150cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } else { 1164ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta TagAndContext newRootTag = 1174ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta new TagAndContext(rootAttributes.toString(), layoutNode); 1180cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar noTag.add(newRootTag); 1190cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 1200cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar //noinspection NullableProblems 1214ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta Collections.sort(noTag, new Comparator<TagAndContext>() { 1220cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar @Override 1234ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta public int compare(TagAndContext o1, TagAndContext o2) { 1244ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta Position start1 = toPosition(o1.getContext().getStart()); 1254ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta Position start2 = toPosition(o2.getContext().getStart()); 1269784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar int lineCmp = start2.line - start1.line; 1270cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar if (lineCmp != 0) { 1280cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return lineCmp; 1290cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 1309784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar return start2.charIndex - start1.charIndex; 1310cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 1320cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar }); 1334ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta for (TagAndContext it : noTag) { 1344ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta ElementContext element = it.getContext(); 1354ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta String tag = it.getTag(); 1360cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar Position endTagPosition = endTagPosition(element); 1370cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar fixPosition(lines, endTagPosition); 1380cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String line = lines.get(endTagPosition.line); 1390cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String newLine = line.substring(0, endTagPosition.charIndex) + " " + tag + 1400cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar line.substring(endTagPosition.charIndex); 1410cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar lines.set(endTagPosition.line, newLine); 1420cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 1434ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta return Joiner.on(StringUtils.LINE_SEPARATOR).join(lines); 1440cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 1450cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 1460c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar private static <T extends XMLParser.ElementContext> List<T> 1470c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar filterNodesByName(String name, Iterable<T> items) { 1489784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar List<T> result = new ArrayList<T>(); 1490c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar for (T item : items) { 1500c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar if (name.equals(nodeName(item))) { 1510c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar result.add(item); 1520c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar } 1530c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar } 1540c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar return result; 1550c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar } 1560c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar 1570c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar private static <T extends XMLParser.ElementContext> List<T> 1580c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar excludeNodesByName(String name, Iterable<T> items) { 1599784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar List<T> result = new ArrayList<T>(); 1600c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar for (T item : items) { 1610c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar if (!name.equals(nodeName(item))) { 1620c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar result.add(item); 1630c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar } 1640c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar } 1650c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar return result; 1660c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar } 1670c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar 1680cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar private static Position toPosition(Token token) { 1690cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return new Position(token.getLine() - 1, token.getCharPositionInLine()); 1700cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 1710cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 1720cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar private static Position toEndPosition(Token token) { 1730cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return new Position(token.getLine() - 1, 1740cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar token.getCharPositionInLine() + token.getText().length()); 1750cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 1760cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 1774ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta public static String nodeName(ElementContext elementContext) { 1780cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return elementContext.elmName.getText(); 1790cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 1800cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 1814ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta public static List<? extends AttributeContext> attributes(ElementContext elementContext) { 1829784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar if (elementContext.attribute() == null) 1839784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar return new ArrayList<AttributeContext>(); 1849784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar else { 1850cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return elementContext.attribute(); 1860cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 1870cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 1880cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 1894ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta public static List<? extends AttributeContext> expressionAttributes( 1904ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta ElementContext elementContext) { 1919784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar List<AttributeContext> result = new ArrayList<AttributeContext>(); 1920c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar for (AttributeContext input : attributes(elementContext)) { 1930c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar String attrName = input.attrName.getText(); 194d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount boolean isExpression = attrName.equals("android:tag"); 195d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount if (!isExpression) { 196d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount final String value = input.attrValue.getText(); 197d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount isExpression = isExpressionText(input.attrValue.getText()); 198d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount } 199d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount if (isExpression) { 2000c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar result.add(input); 2010cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 2020c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar } 2030c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar return result; 2040cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 2050cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 206d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount private static boolean isExpressionText(String value) { 207d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount // Check if the expression ends with "}" and starts with "@{" or "@={", ignoring 208d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount // the surrounding quotes. 209d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount return (value.length() > 5 && value.charAt(value.length() - 2) == '}' && 210d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount ("@{".equals(value.substring(1, 3)) || "@={".equals(value.substring(1, 4)))); 211d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount } 212d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount 2134ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta private static Position endTagPosition(ElementContext context) { 2140cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar if (context.content() == null) { 215f1081f6a15e6b905701bd3bbcb5d598731d05afbGeorge Mount // no content, so just choose the start of the "/>" 216f1081f6a15e6b905701bd3bbcb5d598731d05afbGeorge Mount Position endTag = toPosition(context.getStop()); 2170c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar if (endTag.charIndex <= 0) { 2180c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar L.e("invalid input in %s", context); 2190c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar } 2200cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return endTag; 2210cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } else { 2220cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar // tag with no attributes, but with content 2230cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar Position position = toPosition(context.content().getStart()); 2240c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar if (position.charIndex <= 0) { 2250c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar L.e("invalid input in %s", context); 2260c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar } 2270cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar position.charIndex--; 2280cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return position; 2290cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 2300cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 2310cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 2324ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta public static List<? extends ElementContext> elements(ElementContext context) { 2330cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar if (context.content() != null && context.content().element() != null) { 2340cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return context.content().element(); 2350cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 2369784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar return new ArrayList<ElementContext>(); 2370cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 2380cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 2390cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar private static boolean replace(ArrayList<String> lines, Position start, Position end, 2400cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String text) { 2410cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar fixPosition(lines, start); 2420cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar fixPosition(lines, end); 2430cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar if (start.line != end.line) { 2440cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String startLine = lines.get(start.line); 2450cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String newStartLine = startLine.substring(0, start.charIndex) + text; 2460cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar lines.set(start.line, newStartLine); 2470cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar for (int i = start.line + 1; i < end.line; i++) { 2480cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String line = lines.get(i); 2490cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar lines.set(i, replaceWithSpaces(line, 0, line.length() - 1)); 2500cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 2510cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String endLine = lines.get(end.line); 2520cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String newEndLine = replaceWithSpaces(endLine, 0, end.charIndex - 1); 2530cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar lines.set(end.line, newEndLine); 2540cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return true; 2550cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } else if (end.charIndex - start.charIndex >= text.length()) { 2560cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String line = lines.get(start.line); 2570cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar int endTextIndex = start.charIndex + text.length(); 2580cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String replacedText = replaceRange(line, start.charIndex, endTextIndex, text); 2590cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String spacedText = replaceWithSpaces(replacedText, endTextIndex, end.charIndex - 1); 2600cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar lines.set(start.line, spacedText); 2610cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return true; 2620cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } else { 2630cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String line = lines.get(start.line); 2640cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String newLine = replaceWithSpaces(line, start.charIndex, end.charIndex - 1); 2650cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar lines.set(start.line, newLine); 2660cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return false; 2670cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 2680cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 2690cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 2700cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar private static String replaceRange(String line, int start, int end, String newText) { 2710cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return line.substring(0, start) + newText + line.substring(end); 2720cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 2730cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 2744ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta public static boolean hasExpressionAttributes(ElementContext context) { 2750c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar List<? extends AttributeContext> expressions = expressionAttributes(context); 2760c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar int size = expressions.size(); 27792a428505b9102bc0560d2d5be1768da097909c2George Mount if (size == 0) { 27892a428505b9102bc0560d2d5be1768da097909c2George Mount return false; 27992a428505b9102bc0560d2d5be1768da097909c2George Mount } else if (size > 1) { 28092a428505b9102bc0560d2d5be1768da097909c2George Mount return true; 28192a428505b9102bc0560d2d5be1768da097909c2George Mount } else { 28292a428505b9102bc0560d2d5be1768da097909c2George Mount // android:tag is included, regardless, so we must only count as an expression 28392a428505b9102bc0560d2d5be1768da097909c2George Mount // if android:tag has a binding expression. 284d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount return isExpressionText(expressions.get(0).attrValue.getText()); 28592a428505b9102bc0560d2d5be1768da097909c2George Mount } 2860cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 2870cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 2884ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta private static int recurseReplace(ElementContext node, ArrayList<String> lines, 2894ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta ArrayList<TagAndContext> noTag, 2900cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String newTag, int bindingIndex) { 2910cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar int nextBindingIndex = bindingIndex; 2920cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar boolean isMerge = "merge".equals(nodeName(node)); 2930c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar final boolean containsInclude = filterNodesByName("include", elements(node)).size() > 0; 2942ac58b34e5200a34b0ba63884c375a68c9a84303George Mount if (!isMerge && (hasExpressionAttributes(node) || newTag != null || containsInclude)) { 2950cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String tag = ""; 2960cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar if (newTag != null) { 2970cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar tag = "android:tag=\"" + newTag + "_" + bindingIndex + "\""; 2980cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar nextBindingIndex++; 2990cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } else if (!"include".equals(nodeName(node))) { 3000cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar tag = "android:tag=\"binding_" + bindingIndex + "\""; 3010cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar nextBindingIndex++; 3020cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3030cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar for (AttributeContext it : expressionAttributes(node)) { 3040cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar Position start = toPosition(it.getStart()); 3050cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar Position end = toEndPosition(it.getStop()); 3060cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String defaultVal = defaultReplacement(it); 3070cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar if (defaultVal != null) { 3080cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar replace(lines, start, end, it.attrName.getText() + "=\"" + defaultVal + "\""); 3090cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } else if (replace(lines, start, end, tag)) { 3100cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar tag = ""; 3110cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3120cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3130cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar if (tag.length() != 0) { 3144ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta noTag.add(new TagAndContext(tag, node)); 3150cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3160cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3170cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 3180cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String nextTag; 3190cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar if (bindingIndex == 0 && isMerge) { 3200cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar nextTag = newTag; 3210cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } else { 3220cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar nextTag = null; 3230cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3244ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta for (ElementContext it : elements(node)) { 3250cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar nextBindingIndex = recurseReplace(it, lines, noTag, nextTag, nextBindingIndex); 3260cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3270cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return nextBindingIndex; 3280cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3290cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 3300cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar private static String defaultReplacement(XMLParser.AttributeContext attr) { 3310cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String textWithQuotes = attr.attrValue.getText(); 3320cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String escapedText = textWithQuotes.substring(1, textWithQuotes.length() - 1); 333d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount final boolean isTwoWay = escapedText.startsWith("@={"); 334d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount final boolean isOneWay = escapedText.startsWith("@{"); 335d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount if ((!isTwoWay && !isOneWay) || !escapedText.endsWith("}")) { 3360cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return null; 3370cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 338d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount final int startIndex = isTwoWay ? 3 : 2; 339d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount final int endIndex = escapedText.length() - 1; 340d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount String text = StringUtils.unescapeXml(escapedText.substring(startIndex, endIndex)); 3410cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar ANTLRInputStream inputStream = new ANTLRInputStream(text); 3420cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar BindingExpressionLexer lexer = new BindingExpressionLexer(inputStream); 3430cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar CommonTokenStream tokenStream = new CommonTokenStream(lexer); 3440cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar BindingExpressionParser parser = new BindingExpressionParser(tokenStream); 3450cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar BindingExpressionParser.BindingSyntaxContext root = parser.bindingSyntax(); 3460cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar BindingExpressionParser.DefaultsContext defaults = root.defaults(); 3470cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar if (defaults != null) { 3480cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar BindingExpressionParser.ConstantValueContext constantValue = defaults 3490cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar .constantValue(); 3500cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar BindingExpressionParser.LiteralContext literal = constantValue.literal(); 3510cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar if (literal != null) { 3520cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar BindingExpressionParser.StringLiteralContext stringLiteral = literal 3530cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar .stringLiteral(); 3540cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar if (stringLiteral != null) { 3550cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar TerminalNode doubleQuote = stringLiteral.DoubleQuoteString(); 3560cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar if (doubleQuote != null) { 3570cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String quotedStr = doubleQuote.getText(); 3580cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String unquoted = quotedStr.substring(1, quotedStr.length() - 1); 3594ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta return XmlEscapers.xmlAttributeEscaper().escape(unquoted); 3600cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } else { 3610cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String quotedStr = stringLiteral.SingleQuoteString().getText(); 3620cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String unquoted = quotedStr.substring(1, quotedStr.length() - 1); 3630cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String unescaped = unquoted.replace("\"", "\\\"").replace("\\`", "`"); 3644ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta return XmlEscapers.xmlAttributeEscaper().escape(unescaped); 3650cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3660cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3670cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3680cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return constantValue.getText(); 3690cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3700cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return null; 3710cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3720cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 3734ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta private static PositionPair findTerminalPositions(ElementContext node, 3744ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta ArrayList<String> lines) { 3750cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar Position endPosition = toEndPosition(node.getStop()); 3760cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar Position startPosition = toPosition(node.getStop()); 3770cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar int index; 3780cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar do { 3790cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar index = lines.get(startPosition.line).lastIndexOf("</"); 3800cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar startPosition.line--; 3810cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } while (index < 0); 3820cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar startPosition.line++; 3830cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar startPosition.charIndex = index; 3840cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar //noinspection unchecked 3854ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta return new PositionPair(startPosition, endPosition); 3860cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3870cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 3880cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar private static String replaceWithSpaces(String line, int start, int end) { 3890cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar StringBuilder lineBuilder = new StringBuilder(line); 3900cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar for (int i = start; i <= end; i++) { 3910cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar lineBuilder.setCharAt(i, ' '); 3920cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3930cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return lineBuilder.toString(); 3940cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3950cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 3960cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar private static void fixPosition(ArrayList<String> lines, Position pos) { 3970cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String line = lines.get(pos.line); 3980cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar while (pos.charIndex > line.length()) { 3990cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar pos.charIndex--; 4000cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 4010cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 4020cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 4030cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar private static class Position { 4040cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 4050cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar int line; 4060cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar int charIndex; 4070cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 4080cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar public Position(int line, int charIndex) { 4090cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar this.line = line; 4100cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar this.charIndex = charIndex; 4110cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 4120cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 4130cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 4144ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta private static class TagAndContext { 4154ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta private final String mTag; 4164ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta private final ElementContext mElementContext; 4174ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta 4184ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta private TagAndContext(String tag, ElementContext elementContext) { 4194ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta mTag = tag; 4204ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta mElementContext = elementContext; 4214ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta } 4224ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta 4234ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta private ElementContext getContext() { 4244ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta return mElementContext; 4254ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta } 4264ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta 4274ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta private String getTag() { 4284ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta return mTag; 4294ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta } 4304ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta } 4314ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta 4324ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta private static class PositionPair { 4334ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta private final Position left; 4344ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta private final Position right; 4354ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta 4364ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta private PositionPair(Position left, Position right) { 4374ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta this.left = left; 4384ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta this.right = right; 4394ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta } 4404ba16229a40e9758db86d4fb1df5119fdcb8aa2aDeepanshu Gupta } 4410cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar} 442