Home | History | Annotate | Download | only in store
      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
     54     public Location() {
     55         startOffset = endOffset = startLine = endLine = NaN;
     56     }
     57 
     58     public Location(Location other) {
     59         startOffset = other.startOffset;
     60         endOffset = other.endOffset;
     61         startLine = other.startLine;
     62         endLine = other.endLine;
     63     }
     64 
     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 
    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 
    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 
    194     public String toUserReadableString() {
    195         return startLine + ":" + startOffset + " - " + endLine + ":" + endOffset;
    196     }
    197 
    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 
    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 
    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