• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.databinding.tool.store;
18 
19 import org.antlr.v4.runtime.ParserRuleContext;
20 import org.antlr.v4.runtime.Token;
21 
22 import android.databinding.tool.processing.scopes.LocationScopeProvider;
23 import android.databinding.tool.util.StringUtils;
24 
25 import java.util.Collections;
26 import java.util.List;
27 
28 import javax.xml.bind.annotation.XmlAccessType;
29 import javax.xml.bind.annotation.XmlAccessorType;
30 import javax.xml.bind.annotation.XmlAttribute;
31 import javax.xml.bind.annotation.XmlElement;
32 
33 /**
34  * Identifies the range of a code block inside a file or a string.
35  * Note that, unlike antlr4 tokens, the line positions start from 0 (to be compatible with Studio).
36  * <p>
37  * Both start and end line/column indices are inclusive.
38  */
39 @XmlAccessorType(XmlAccessType.NONE)
40 public class Location {
41     public static final int NaN = -1;
42     @XmlAttribute(name = "startLine")
43     public int startLine;
44     @XmlAttribute(name = "startOffset")
45     public int startOffset;
46     @XmlAttribute(name = "endLine")
47     public int endLine;
48     @XmlAttribute(name = "endOffset")
49     public int endOffset;
50     @XmlElement(name = "parentLocation")
51     public Location parentLocation;
52 
53     // for XML unmarshalling
Location()54     public Location() {
55         startOffset = endOffset = startLine = endLine = NaN;
56     }
57 
Location(Location other)58     public Location(Location other) {
59         startOffset = other.startOffset;
60         endOffset = other.endOffset;
61         startLine = other.startLine;
62         endLine = other.endLine;
63     }
64 
Location(Token start, Token end)65     public Location(Token start, Token end) {
66         if (start == null) {
67             startLine = startOffset = NaN;
68         } else {
69             startLine = start.getLine() - 1; //token lines start from 1
70             startOffset = start.getCharPositionInLine();
71         }
72 
73         if (end == null) {
74             endLine = endOffset = NaN;
75         } else {
76             endLine = end.getLine() - 1; // token lines start from 1
77             String endText = end.getText();
78             int lastLineStart = endText.lastIndexOf(StringUtils.LINE_SEPARATOR);
79             String lastLine = lastLineStart < 0 ? endText : endText.substring(lastLineStart + 1);
80             endOffset = end.getCharPositionInLine() + lastLine.length() - 1;//end is inclusive
81         }
82     }
83 
84     public Location(ParserRuleContext context) {
85         this(context == null ? null : context.getStart(),
86                 context == null ? null : context.getStop());
87     }
88 
89     public Location(int startLine, int startOffset, int endLine, int endOffset) {
90         this.startOffset = startOffset;
91         this.startLine = startLine;
92         this.endLine = endLine;
93         this.endOffset = endOffset;
94     }
95 
96     @Override
97     public String toString() {
98         return "Location{" +
99                 "startLine=" + startLine +
100                 ", startOffset=" + startOffset +
101                 ", endLine=" + endLine +
102                 ", endOffset=" + endOffset +
103                 ", parentLocation=" + parentLocation +
104                 '}';
105     }
106 
107     public void setParentLocation(Location parentLocation) {
108         this.parentLocation = parentLocation;
109     }
110 
111     @Override
112     public boolean equals(Object o) {
113         if (this == o) {
114             return true;
115         }
116         if (o == null || getClass() != o.getClass()) {
117             return false;
118         }
119 
120         Location location = (Location) o;
121 
122         if (endLine != location.endLine) {
123             return false;
124         }
125         if (endOffset != location.endOffset) {
126             return false;
127         }
128         if (startLine != location.startLine) {
129             return false;
130         }
131         if (startOffset != location.startOffset) {
132             return false;
133         }
134         return !(parentLocation != null ? !parentLocation.equals(location.parentLocation)
135                 : location.parentLocation != null);
136 
137     }
138 
139     @Override
140     public int hashCode() {
141         int result = startLine;
142         result = 31 * result + startOffset;
143         result = 31 * result + endLine;
144         result = 31 * result + endOffset;
145         return result;
146     }
147 
148     public boolean isValid() {
149         return startLine != NaN && endLine != NaN && startOffset != NaN && endOffset != NaN;
150     }
151 
152     public boolean contains(Location other) {
153         if (startLine > other.startLine) {
154             return false;
155         }
156         if (startLine == other.startLine && startOffset > other.startOffset) {
157             return false;
158         }
159         if (endLine < other.endLine) {
160             return false;
161         }
162         return !(endLine == other.endLine && endOffset < other.endOffset);
163     }
164 
getValidParentAbsoluteLocation()165     private Location getValidParentAbsoluteLocation() {
166         if (parentLocation == null) {
167             return null;
168         }
169         if (parentLocation.isValid()) {
170             return parentLocation.toAbsoluteLocation();
171         }
172         return parentLocation.getValidParentAbsoluteLocation();
173     }
174 
toAbsoluteLocation()175     public Location toAbsoluteLocation() {
176         Location absoluteParent = getValidParentAbsoluteLocation();
177         if (absoluteParent == null) {
178             return this;
179         }
180         Location copy = new Location(this);
181         boolean sameLine = copy.startLine == copy.endLine;
182         if (copy.startLine == 0) {
183             copy.startOffset += absoluteParent.startOffset;
184         }
185         if (sameLine) {
186             copy.endOffset += absoluteParent.startOffset;
187         }
188 
189         copy.startLine += absoluteParent.startLine;
190         copy.endLine += absoluteParent.startLine;
191         return copy;
192     }
193 
toUserReadableString()194     public String toUserReadableString() {
195         return startLine + ":" + startOffset + " - " + endLine + ":" + endOffset;
196     }
197 
fromUserReadableString(String str)198     public static Location fromUserReadableString(String str) {
199         int glue = str.indexOf('-');
200         if (glue == -1) {
201             return new Location();
202         }
203         String start = str.substring(0, glue);
204         String end = str.substring(glue + 1);
205         int[] point = new int[]{-1, -1};
206         Location location = new Location();
207         parsePoint(start, point);
208         location.startLine = point[0];
209         location.startOffset = point[1];
210         point[0] = point[1] = -1;
211         parsePoint(end, point);
212         location.endLine = point[0];
213         location.endOffset = point[1];
214         return location;
215     }
216 
parsePoint(String content, int[] into)217     private static boolean parsePoint(String content, int[] into) {
218         int index = content.indexOf(':');
219         if (index == -1) {
220             return false;
221         }
222         into[0] = Integer.parseInt(content.substring(0, index).trim());
223         into[1] = Integer.parseInt(content.substring(index + 1).trim());
224         return true;
225     }
226 
createScope()227     public LocationScopeProvider createScope() {
228         return new LocationScopeProvider() {
229             @Override
230             public List<Location> provideScopeLocation() {
231                 return Collections.singletonList(Location.this);
232             }
233         };
234     }
235 }
236