Location.java revision 731b74f7f44e67312a1fc4161c4e0aae221b2417
13516800b611a79339a3c188332d13a26e9086b09Adam Lesinski/*
23516800b611a79339a3c188332d13a26e9086b09Adam Lesinski * Copyright (C) 2015 The Android Open Source Project
33516800b611a79339a3c188332d13a26e9086b09Adam Lesinski *
43516800b611a79339a3c188332d13a26e9086b09Adam Lesinski * Licensed under the Apache License, Version 2.0 (the "License");
53516800b611a79339a3c188332d13a26e9086b09Adam Lesinski * you may not use this file except in compliance with the License.
63516800b611a79339a3c188332d13a26e9086b09Adam Lesinski * You may obtain a copy of the License at
73516800b611a79339a3c188332d13a26e9086b09Adam Lesinski *
83516800b611a79339a3c188332d13a26e9086b09Adam Lesinski *      http://www.apache.org/licenses/LICENSE-2.0
93516800b611a79339a3c188332d13a26e9086b09Adam Lesinski *
103516800b611a79339a3c188332d13a26e9086b09Adam Lesinski * Unless required by applicable law or agreed to in writing, software
113516800b611a79339a3c188332d13a26e9086b09Adam Lesinski * distributed under the License is distributed on an "AS IS" BASIS,
123516800b611a79339a3c188332d13a26e9086b09Adam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133516800b611a79339a3c188332d13a26e9086b09Adam Lesinski * See the License for the specific language governing permissions and
143516800b611a79339a3c188332d13a26e9086b09Adam Lesinski * limitations under the License.
153516800b611a79339a3c188332d13a26e9086b09Adam Lesinski */
163516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
173516800b611a79339a3c188332d13a26e9086b09Adam Lesinskipackage android.databinding.tool.store;
187f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinskiimport org.antlr.v4.runtime.ParserRuleContext;
193516800b611a79339a3c188332d13a26e9086b09Adam Lesinskiimport org.antlr.v4.runtime.Token;
203516800b611a79339a3c188332d13a26e9086b09Adam Lesinskiimport org.apache.commons.lang3.StringUtils;
213516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
223516800b611a79339a3c188332d13a26e9086b09Adam Lesinskiimport javax.xml.bind.annotation.XmlAccessType;
233516800b611a79339a3c188332d13a26e9086b09Adam Lesinskiimport javax.xml.bind.annotation.XmlAccessorType;
243516800b611a79339a3c188332d13a26e9086b09Adam Lesinskiimport javax.xml.bind.annotation.XmlAttribute;
253516800b611a79339a3c188332d13a26e9086b09Adam Lesinskiimport javax.xml.bind.annotation.XmlElement;
263516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
273516800b611a79339a3c188332d13a26e9086b09Adam Lesinski/**
283516800b611a79339a3c188332d13a26e9086b09Adam Lesinski * Identifies the range of a code block inside a file or a string.
293516800b611a79339a3c188332d13a26e9086b09Adam Lesinski * Note that, unlike antlr4 tokens, the line positions start from 0 (to be compatible with Studio).
303516800b611a79339a3c188332d13a26e9086b09Adam Lesinski * <p>
313516800b611a79339a3c188332d13a26e9086b09Adam Lesinski * Both start and end line/column indices are inclusive.
323516800b611a79339a3c188332d13a26e9086b09Adam Lesinski */
333516800b611a79339a3c188332d13a26e9086b09Adam Lesinski@XmlAccessorType(XmlAccessType.NONE)
343516800b611a79339a3c188332d13a26e9086b09Adam Lesinskipublic class Location {
353516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    public static final int NaN = -1;
363516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    @XmlAttribute(name = "startLine")
373516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    public int startLine;
383516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    @XmlAttribute(name = "startOffset")
393516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    public int startOffset;
403516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    @XmlAttribute(name = "endLine")
413516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    public int endLine;
423516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    @XmlAttribute(name = "endOffset")
433516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    public int endOffset;
443516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    @XmlElement(name = "parentLocation")
453516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    public Location parentLocation;
463516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
473516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    // for XML unmarshalling
483516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    public Location() {
493516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        startOffset = endOffset = startLine = endLine = NaN;
503516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    }
513516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
523516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    public Location(Location other) {
533516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        startOffset = other.startOffset;
543516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        endOffset = other.endOffset;
553516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        startLine = other.startLine;
563516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        endLine = other.endLine;
573516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    }
583516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
593516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    public Location(Token start, Token end) {
603516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        if (start == null) {
613516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            startLine = startOffset = NaN;
623516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        } else {
633516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            startLine = start.getLine() - 1; //token lines start from 1
643516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            startOffset = start.getCharPositionInLine();
653516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        }
667f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski
677f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        if (end == null) {
687f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            endLine = endOffset = NaN;
697f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        } else {
707f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            endLine = end.getLine() - 1; // token lines start from 1
71c8e8729244d75584ce71a74d29c452fe538a22c5Adam Lesinski            String endText = end.getText();
72c8e8729244d75584ce71a74d29c452fe538a22c5Adam Lesinski            int lastLineStart = endText.lastIndexOf(System.lineSeparator());
73978a1ed5aa2752cd36ff51df91d2d2d8be2171d9Adam Lesinski            String lastLine = lastLineStart < 0 ? endText : endText.substring(lastLineStart + 1);
74c8e8729244d75584ce71a74d29c452fe538a22c5Adam Lesinski            endOffset = end.getCharPositionInLine() + lastLine.length() - 1;//end is inclusive
75c8e8729244d75584ce71a74d29c452fe538a22c5Adam Lesinski        }
76c8e8729244d75584ce71a74d29c452fe538a22c5Adam Lesinski    }
77c8e8729244d75584ce71a74d29c452fe538a22c5Adam Lesinski
78c8e8729244d75584ce71a74d29c452fe538a22c5Adam Lesinski    public Location(ParserRuleContext context) {
79c8e8729244d75584ce71a74d29c452fe538a22c5Adam Lesinski        this(context == null ? null : context.getStart(),
80978a1ed5aa2752cd36ff51df91d2d2d8be2171d9Adam Lesinski                context == null ? null : context.getStop());
81978a1ed5aa2752cd36ff51df91d2d2d8be2171d9Adam Lesinski    }
823516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
833516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    public Location(int startLine, int startOffset, int endLine, int endOffset) {
849d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski        this.startOffset = startOffset;
859d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski        this.startLine = startLine;
869d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski        this.endLine = endLine;
879d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski        this.endOffset = endOffset;
889d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski    }
899d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski
903516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    @Override
913516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    public String toString() {
923516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        return "Location{" +
933516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                "startLine=" + startLine +
943516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                ", startOffset=" + startOffset +
953516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                ", endLine=" + endLine +
963516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                ", endOffset=" + endOffset +
973516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                ", parentLocation=" + parentLocation +
983516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                '}';
993516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    }
1003516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
1013516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    public void setParentLocation(Location parentLocation) {
1027f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        this.parentLocation = parentLocation;
1037f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski    }
1047f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski
1057f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski    @Override
1067f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski    public boolean equals(Object o) {
1077f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        if (this == o) {
1089d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski            return true;
1099d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski        }
1109d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski        if (o == null || getClass() != o.getClass()) {
1119d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski            return false;
1129d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski        }
1139d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski
1149d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski        Location location = (Location) o;
1159d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski
1169d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski        if (endLine != location.endLine) {
1179d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski            return false;
1189d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski        }
1199d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski        if (endOffset != location.endOffset) {
1203516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            return false;
1213516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        }
1223516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        if (startLine != location.startLine) {
123cc562a811da508b275254f275d6e0c1758a47d07Adam Lesinski            return false;
124cc562a811da508b275254f275d6e0c1758a47d07Adam Lesinski        }
125cc562a811da508b275254f275d6e0c1758a47d07Adam Lesinski        if (startOffset != location.startOffset) {
1263516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            return false;
1273516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        }
1283516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        if (parentLocation != null ? !parentLocation.equals(location.parentLocation)
1293516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                : location.parentLocation != null) {
1303516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            return false;
1313516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        }
1323516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
1333516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        return true;
1343516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    }
1353516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
1363516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    @Override
1373516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    public int hashCode() {
1383516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        int result = startLine;
1393516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        result = 31 * result + startOffset;
1407f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        result = 31 * result + endLine;
1417f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        result = 31 * result + endOffset;
1427f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        return result;
1437f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski    }
1447f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski
1457f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski    public boolean isValid() {
1467f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        return startLine != NaN && endLine != NaN && startOffset != NaN && endOffset != NaN;
1477f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski    }
1483516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
1493516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    public boolean contains(Location other) {
1503516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        if (startLine > other.startLine) {
1513516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            return false;
1523516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        }
1533516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        if (startLine == other.startLine && startOffset > other.startOffset) {
1543516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            return false;
1553516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        }
1563516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        if (endLine < other.endLine) {
1573516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            return false;
1583516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        }
1593516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        if (endLine == other.endLine && endOffset < other.endOffset) {
1603516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            return false;
1613516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        }
1623516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        return true;
1639d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski    }
1643516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
1653516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    private Location getValidParentAbsoluteLocation() {
1663516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        if (parentLocation == null) {
1673516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            return null;
1683516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        }
1693516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        if (parentLocation.isValid()) {
1703516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            return parentLocation.toAbsoluteLocation();
1713516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        }
1723516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        return parentLocation.getValidParentAbsoluteLocation();
1739d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski    }
1743516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
1753516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    public Location toAbsoluteLocation() {
1763516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        Location absoluteParent = getValidParentAbsoluteLocation();
1773516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        if (absoluteParent == null) {
1783516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            return this;
1793516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        }
1803516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        Location copy = new Location(this);
1813516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        boolean sameLine = copy.startLine == copy.endLine;
1823516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        if (copy.startLine == 0) {
1833516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            copy.startOffset += absoluteParent.startOffset;
1843516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        }
1853516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        if (sameLine) {
1863516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            copy.endOffset += absoluteParent.startOffset;
1873516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        }
1883516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
1893516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        copy.startLine += absoluteParent.startLine;
1903516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        copy.endLine += absoluteParent.startLine;
1913516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        return copy;
1923516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    }
1933516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
1943516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    public String toUserReadableString() {
1953516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        return startLine + ":" + startOffset + " - " + endLine + ":" + endOffset;
1963516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    }
1979d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski
1989d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski    public static Location fromUserReadableString(String str) {
1993516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        int glue = str.indexOf('-');
2003516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        if (glue == -1) {
2013516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            return new Location();
2023516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        }
2033516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        String start = str.substring(0, glue);
2043516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        String end = str.substring(glue + 1);
2053516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        int[] point = new int[]{-1, -1};
2063516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        Location location = new Location();
2073516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        parsePoint(start, point);
2083516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        location.startLine = point[0];
2093516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        location.startOffset = point[1];
2103516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        point[0] = point[1] = -1;
2113516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        parsePoint(end, point);
2123516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        location.endLine = point[0];
2133516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        location.endOffset = point[1];
2143516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        return location;
2153516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    }
2163516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
2173516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    private static boolean parsePoint(String content, int[] into) {
2183516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        int index = content.indexOf(':');
2193516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        if (index == -1) {
2203516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            return false;
2213516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        }
2223516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        into[0] = Integer.parseInt(content.substring(0, index).trim());
2233516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        into[1] = Integer.parseInt(content.substring(index + 1).trim());
2243516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        return true;
2253516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    }
2267f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski}
2279d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski