XmlEditor.java revision 92a428505b9102bc0560d2d5be1768da097909c2
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 190cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarimport org.antlr.v4.runtime.ANTLRInputStream; 200cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarimport org.antlr.v4.runtime.CommonTokenStream; 210cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarimport org.antlr.v4.runtime.Token; 220cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarimport org.antlr.v4.runtime.tree.TerminalNode; 230cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarimport org.apache.commons.io.FileUtils; 240cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarimport org.apache.commons.lang3.StringEscapeUtils; 250cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarimport org.apache.commons.lang3.StringUtils; 260cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarimport org.apache.commons.lang3.tuple.ImmutablePair; 270cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarimport org.apache.commons.lang3.tuple.Pair; 280cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 290cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarimport android.databinding.parser.BindingExpressionLexer; 300cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarimport android.databinding.parser.BindingExpressionParser; 310cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarimport android.databinding.parser.XMLLexer; 320cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarimport android.databinding.parser.XMLParser; 330cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarimport android.databinding.parser.XMLParser.AttributeContext; 342ac58b34e5200a34b0ba63884c375a68c9a84303George Mountimport android.databinding.parser.XMLParser.ElementContext; 350cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 360cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarimport java.io.File; 370cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarimport java.io.FileReader; 380cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyarimport java.io.IOException; 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 500cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar public static String strip(File f, String newTag) throws IOException { 510cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar ANTLRInputStream inputStream = new ANTLRInputStream(new FileReader(f)); 520cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar XMLLexer lexer = new XMLLexer(inputStream); 530cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar CommonTokenStream tokenStream = new CommonTokenStream(lexer); 540cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar XMLParser parser = new XMLParser(tokenStream); 550cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar XMLParser.DocumentContext expr = parser.document(); 560cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar XMLParser.ElementContext root = expr.element(); 570cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 580cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar if (root == null || !"layout".equals(nodeName(root))) { 590cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return null; // not a binding layout 600cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 610cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 620c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar List<? extends ElementContext> childrenOfRoot = elements(root); 630c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar List<? extends XMLParser.ElementContext> dataNodes = filterNodesByName("data", 640c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar childrenOfRoot); 650c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar if (dataNodes.size() > 1) { 660c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar L.e("Multiple binding data tags in %s. Expecting a maximum of one.", 670c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar f.getAbsolutePath()); 680c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar } 690cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 700c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar ArrayList<String> lines = new ArrayList<>(); 710cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar lines.addAll(FileUtils.readLines(f, "utf-8")); 720cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 730cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar for (android.databinding.parser.XMLParser.ElementContext it : dataNodes) { 740cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar replace(lines, toPosition(it.getStart()), toEndPosition(it.getStop()), ""); 750cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 760c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar List<? extends XMLParser.ElementContext> layoutNodes = 770c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar excludeNodesByName("data", childrenOfRoot); 780c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar if (layoutNodes.size() != 1) { 790c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar L.e("Only one layout element and one data element are allowed. %s has %d", 800c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar f.getAbsolutePath(), layoutNodes.size()); 810c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar } 820cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 830c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar final XMLParser.ElementContext layoutNode = layoutNodes.get(0); 840cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 850c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar ArrayList<Pair<String, android.databinding.parser.XMLParser.ElementContext>> noTag = 860c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar new ArrayList<>(); 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> 960cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar ImmutablePair<Position, Position> 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 } 1030c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar Pair<String, XMLParser.ElementContext> noTagRoot = null; 1040c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar for (Pair<String, XMLParser.ElementContext> pair : noTag) { 1050c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar if (pair.getRight() == layoutNode) { 1060c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar noTagRoot = pair; 1070c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar break; 1080c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar } 1090c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar } 1100c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar if (noTagRoot != null) { 1110cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar ImmutablePair<String, XMLParser.ElementContext> 1120cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar newRootTag = new ImmutablePair<>( 1130cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar noTagRoot.getLeft() + rootAttributes.toString(), layoutNode); 1140cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar int index = noTag.indexOf(noTagRoot); 1150cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar noTag.set(index, newRootTag); 1160cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } else { 1170cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar ImmutablePair<String, XMLParser.ElementContext> newRootTag = 1180cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar new ImmutablePair<>(rootAttributes.toString(), layoutNode); 1190cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar noTag.add(newRootTag); 1200cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 1210cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar //noinspection NullableProblems 1220cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar Collections.sort(noTag, new Comparator<Pair<String, XMLParser.ElementContext>>() { 1230cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar @Override 1240cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar public int compare(Pair<String, XMLParser.ElementContext> o1, 1250cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar Pair<String, XMLParser.ElementContext> o2) { 1260cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar Position start1 = toPosition(o1.getRight().getStart()); 1270cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar Position start2 = toPosition(o2.getRight().getStart()); 1280cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar int lineCmp = Integer.compare(start2.line, start1.line); 1290cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar if (lineCmp != 0) { 1300cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return lineCmp; 1310cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 1320cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return Integer.compare(start2.charIndex, start1.charIndex); 1330cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 1340cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar }); 1350cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar for (Pair<String, android.databinding.parser.XMLParser.ElementContext> it : noTag) { 1360cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar XMLParser.ElementContext element = it.getRight(); 1370cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String tag = it.getLeft(); 1380cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar Position endTagPosition = endTagPosition(element); 1390cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar fixPosition(lines, endTagPosition); 1400cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String line = lines.get(endTagPosition.line); 1410cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String newLine = line.substring(0, endTagPosition.charIndex) + " " + tag + 1420cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar line.substring(endTagPosition.charIndex); 1430cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar lines.set(endTagPosition.line, newLine); 1440cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 1450cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return StringUtils.join(lines, System.getProperty("line.separator")); 1460cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 1470cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 1480c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar private static <T extends XMLParser.ElementContext> List<T> 1490c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar filterNodesByName(String name, Iterable<T> items) { 1500c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar List<T> result = new ArrayList<>(); 1510c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar for (T item : items) { 1520c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar if (name.equals(nodeName(item))) { 1530c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar result.add(item); 1540c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar } 1550c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar } 1560c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar return result; 1570c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar } 1580c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar 1590c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar private static <T extends XMLParser.ElementContext> List<T> 1600c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar excludeNodesByName(String name, Iterable<T> items) { 1610c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar List<T> result = new ArrayList<>(); 1620c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar for (T item : items) { 1630c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar if (!name.equals(nodeName(item))) { 1640c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar result.add(item); 1650c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar } 1660c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar } 1670c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar return result; 1680c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar } 1690c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar 1700cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar private static Position toPosition(Token token) { 1710cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return new Position(token.getLine() - 1, token.getCharPositionInLine()); 1720cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 1730cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 1740cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar private static Position toEndPosition(Token token) { 1750cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return new Position(token.getLine() - 1, 1760cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar token.getCharPositionInLine() + token.getText().length()); 1770cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 1780cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 179c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar public static String nodeName(XMLParser.ElementContext elementContext) { 1800cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return elementContext.elmName.getText(); 1810cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 1820cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 183c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar public static List<? extends AttributeContext> attributes(XMLParser.ElementContext elementContext) { 1840cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar if (elementContext.attribute() == null) { 1850c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar return new ArrayList<>(); 1860cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } else { 1870cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return elementContext.attribute(); 1880cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 1890cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 1900cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 191c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar public static List<? extends AttributeContext> expressionAttributes ( 1920cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar XMLParser.ElementContext elementContext) { 1930c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar List<AttributeContext> result = new ArrayList<>(); 1940c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar for (AttributeContext input : attributes(elementContext)) { 1950c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar String attrName = input.attrName.getText(); 1960c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar String value = input.attrValue.getText(); 1970c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar if (attrName.equals("android:tag") || 1980c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar (value.startsWith("\"@{") && value.endsWith("}\"")) || 1990c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar (value.startsWith("'@{") && value.endsWith("}'"))) { 2000c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar result.add(input); 2010cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 2020c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar } 2030c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar return result; 2040cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 2050cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 2060cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar private static Position endTagPosition(XMLParser.ElementContext context) { 2070cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar if (context.content() == null) { 2080cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar // no content, so just subtract from the "/>" 2090cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar Position endTag = toEndPosition(context.getStop()); 2100c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar if (endTag.charIndex <= 0) { 2110c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar L.e("invalid input in %s", context); 2120c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar } 2130cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar endTag.charIndex -= 2; 2140cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return endTag; 2150cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } else { 2160cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar // tag with no attributes, but with content 2170cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar Position position = toPosition(context.content().getStart()); 2180c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar if (position.charIndex <= 0) { 2190c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar L.e("invalid input in %s", context); 2200c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar } 2210cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar position.charIndex--; 2220cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return position; 2230cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 2240cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 2250cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 226c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar public static List<? extends android.databinding.parser.XMLParser.ElementContext> elements( 2270cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar XMLParser.ElementContext context) { 2280cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar if (context.content() != null && context.content().element() != null) { 2290cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return context.content().element(); 2300cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 2310c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar return new ArrayList<>(); 2320cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 2330cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 2340cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar private static boolean replace(ArrayList<String> lines, Position start, Position end, 2350cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String text) { 2360cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar fixPosition(lines, start); 2370cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar fixPosition(lines, end); 2380cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar if (start.line != end.line) { 2390cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String startLine = lines.get(start.line); 2400cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String newStartLine = startLine.substring(0, start.charIndex) + text; 2410cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar lines.set(start.line, newStartLine); 2420cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar for (int i = start.line + 1; i < end.line; i++) { 2430cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String line = lines.get(i); 2440cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar lines.set(i, replaceWithSpaces(line, 0, line.length() - 1)); 2450cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 2460cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String endLine = lines.get(end.line); 2470cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String newEndLine = replaceWithSpaces(endLine, 0, end.charIndex - 1); 2480cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar lines.set(end.line, newEndLine); 2490cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return true; 2500cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } else if (end.charIndex - start.charIndex >= text.length()) { 2510cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String line = lines.get(start.line); 2520cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar int endTextIndex = start.charIndex + text.length(); 2530cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String replacedText = replaceRange(line, start.charIndex, endTextIndex, text); 2540cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String spacedText = replaceWithSpaces(replacedText, endTextIndex, end.charIndex - 1); 2550cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar lines.set(start.line, spacedText); 2560cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return true; 2570cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } else { 2580cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String line = lines.get(start.line); 2590cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String newLine = replaceWithSpaces(line, start.charIndex, end.charIndex - 1); 2600cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar lines.set(start.line, newLine); 2610cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return false; 2620cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 2630cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 2640cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 2650cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar private static String replaceRange(String line, int start, int end, String newText) { 2660cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return line.substring(0, start) + newText + line.substring(end); 2670cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 2680cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 269c1560e6b00b398867da12fbdc5a1fcd1d50b801cYigit Boyar public static boolean hasExpressionAttributes(XMLParser.ElementContext context) { 2700c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar List<? extends AttributeContext> expressions = expressionAttributes(context); 2710c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar int size = expressions.size(); 27292a428505b9102bc0560d2d5be1768da097909c2George Mount if (size == 0) { 27392a428505b9102bc0560d2d5be1768da097909c2George Mount return false; 27492a428505b9102bc0560d2d5be1768da097909c2George Mount } else if (size > 1) { 27592a428505b9102bc0560d2d5be1768da097909c2George Mount return true; 27692a428505b9102bc0560d2d5be1768da097909c2George Mount } else { 27792a428505b9102bc0560d2d5be1768da097909c2George Mount // android:tag is included, regardless, so we must only count as an expression 27892a428505b9102bc0560d2d5be1768da097909c2George Mount // if android:tag has a binding expression. 27992a428505b9102bc0560d2d5be1768da097909c2George Mount String value = expressions.get(0).attrValue.getText(); 28092a428505b9102bc0560d2d5be1768da097909c2George Mount return value.startsWith("\"@{") || value.startsWith("'@{"); 28192a428505b9102bc0560d2d5be1768da097909c2George Mount } 2820cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 2830cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 2840cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar private static int recurseReplace(XMLParser.ElementContext node, ArrayList<String> lines, 2850cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar ArrayList<Pair<String, XMLParser.ElementContext>> noTag, 2860cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String newTag, int bindingIndex) { 2870cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar int nextBindingIndex = bindingIndex; 2880cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar boolean isMerge = "merge".equals(nodeName(node)); 2890c2ed0cbaee2f206e926bfc780b05e9f1e52b551Yigit Boyar final boolean containsInclude = filterNodesByName("include", elements(node)).size() > 0; 2902ac58b34e5200a34b0ba63884c375a68c9a84303George Mount if (!isMerge && (hasExpressionAttributes(node) || newTag != null || containsInclude)) { 2910cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String tag = ""; 2920cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar if (newTag != null) { 2930cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar tag = "android:tag=\"" + newTag + "_" + bindingIndex + "\""; 2940cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar nextBindingIndex++; 2950cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } else if (!"include".equals(nodeName(node))) { 2960cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar tag = "android:tag=\"binding_" + bindingIndex + "\""; 2970cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar nextBindingIndex++; 2980cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 2990cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar for (AttributeContext it : expressionAttributes(node)) { 3000cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar Position start = toPosition(it.getStart()); 3010cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar Position end = toEndPosition(it.getStop()); 3020cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String defaultVal = defaultReplacement(it); 3030cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar if (defaultVal != null) { 3040cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar replace(lines, start, end, it.attrName.getText() + "=\"" + defaultVal + "\""); 3050cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } else if (replace(lines, start, end, tag)) { 3060cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar tag = ""; 3070cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3080cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3090cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar if (tag.length() != 0) { 3100cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar noTag.add(new ImmutablePair<>(tag, node)); 3110cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3120cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3130cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 3140cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String nextTag; 3150cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar if (bindingIndex == 0 && isMerge) { 3160cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar nextTag = newTag; 3170cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } else { 3180cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar nextTag = null; 3190cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3200cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar for (XMLParser.ElementContext it : elements(node)) { 3210cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar nextBindingIndex = recurseReplace(it, lines, noTag, nextTag, nextBindingIndex); 3220cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3230cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return nextBindingIndex; 3240cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3250cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 3260cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar private static String defaultReplacement(XMLParser.AttributeContext attr) { 3270cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String textWithQuotes = attr.attrValue.getText(); 3280cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String escapedText = textWithQuotes.substring(1, textWithQuotes.length() - 1); 3290cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar if (!escapedText.startsWith("@{") || !escapedText.endsWith("}")) { 3300cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return null; 3310cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3320cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String text = StringEscapeUtils 3330cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar .unescapeXml(escapedText.substring(2, escapedText.length() - 1)); 3340cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar ANTLRInputStream inputStream = new ANTLRInputStream(text); 3350cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar BindingExpressionLexer lexer = new BindingExpressionLexer(inputStream); 3360cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar CommonTokenStream tokenStream = new CommonTokenStream(lexer); 3370cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar BindingExpressionParser parser = new BindingExpressionParser(tokenStream); 3380cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar BindingExpressionParser.BindingSyntaxContext root = parser.bindingSyntax(); 3390cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar BindingExpressionParser.DefaultsContext defaults = root.defaults(); 3400cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar if (defaults != null) { 3410cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar BindingExpressionParser.ConstantValueContext constantValue = defaults 3420cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar .constantValue(); 3430cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar BindingExpressionParser.LiteralContext literal = constantValue.literal(); 3440cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar if (literal != null) { 3450cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar BindingExpressionParser.StringLiteralContext stringLiteral = literal 3460cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar .stringLiteral(); 3470cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar if (stringLiteral != null) { 3480cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar TerminalNode doubleQuote = stringLiteral.DoubleQuoteString(); 3490cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar if (doubleQuote != null) { 3500cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String quotedStr = doubleQuote.getText(); 3510cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String unquoted = quotedStr.substring(1, quotedStr.length() - 1); 3520cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return StringEscapeUtils.escapeXml10(unquoted); 3530cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } else { 3540cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String quotedStr = stringLiteral.SingleQuoteString().getText(); 3550cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String unquoted = quotedStr.substring(1, quotedStr.length() - 1); 3560cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String unescaped = unquoted.replace("\"", "\\\"").replace("\\`", "`"); 3570cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return StringEscapeUtils.escapeXml10(unescaped); 3580cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3590cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3600cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3610cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return constantValue.getText(); 3620cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3630cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return null; 3640cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3650cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 3660cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar private static ImmutablePair<Position, Position> findTerminalPositions( 3670cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar XMLParser.ElementContext node, ArrayList<String> lines) { 3680cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar Position endPosition = toEndPosition(node.getStop()); 3690cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar Position startPosition = toPosition(node.getStop()); 3700cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar int index; 3710cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar do { 3720cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar index = lines.get(startPosition.line).lastIndexOf("</"); 3730cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar startPosition.line--; 3740cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } while (index < 0); 3750cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar startPosition.line++; 3760cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar startPosition.charIndex = index; 3770cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar //noinspection unchecked 3780cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return new ImmutablePair<>(startPosition, endPosition); 3790cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3800cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 3810cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar private static String replaceWithSpaces(String line, int start, int end) { 3820cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar StringBuilder lineBuilder = new StringBuilder(line); 3830cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar for (int i = start; i <= end; i++) { 3840cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar lineBuilder.setCharAt(i, ' '); 3850cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3860cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar return lineBuilder.toString(); 3870cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3880cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 3890cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar private static void fixPosition(ArrayList<String> lines, Position pos) { 3900cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar String line = lines.get(pos.line); 3910cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar while (pos.charIndex > line.length()) { 3920cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar pos.charIndex--; 3930cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3940cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 3950cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 3960cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar private static class Position { 3970cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 3980cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar int line; 3990cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar int charIndex; 4000cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 4010cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar public Position(int line, int charIndex) { 4020cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar this.line = line; 4030cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar this.charIndex = charIndex; 4040cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 4050cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar } 4060cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar 4070cb9fbb96197af013f4f879ed6cddf2681b88fd6Yigit Boyar} 408