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 import org.antlr.v4.runtime.ParserRuleContext; 19 import org.antlr.v4.runtime.Token; 20 import org.apache.commons.lang3.StringUtils; 21 22 import android.databinding.tool.processing.scopes.LocationScopeProvider; 23 24 import java.util.Arrays; 25 import java.util.List; 26 27 import javax.xml.bind.annotation.XmlAccessType; 28 import javax.xml.bind.annotation.XmlAccessorType; 29 import javax.xml.bind.annotation.XmlAttribute; 30 import javax.xml.bind.annotation.XmlElement; 31 32 /** 33 * Identifies the range of a code block inside a file or a string. 34 * Note that, unlike antlr4 tokens, the line positions start from 0 (to be compatible with Studio). 35 * <p> 36 * Both start and end line/column indices are inclusive. 37 */ 38 @XmlAccessorType(XmlAccessType.NONE) 39 public class Location { 40 public static final int NaN = -1; 41 @XmlAttribute(name = "startLine") 42 public int startLine; 43 @XmlAttribute(name = "startOffset") 44 public int startOffset; 45 @XmlAttribute(name = "endLine") 46 public int endLine; 47 @XmlAttribute(name = "endOffset") 48 public int endOffset; 49 @XmlElement(name = "parentLocation") 50 public Location parentLocation; 51 52 // for XML unmarshalling Location()53 public Location() { 54 startOffset = endOffset = startLine = endLine = NaN; 55 } 56 Location(Location other)57 public Location(Location other) { 58 startOffset = other.startOffset; 59 endOffset = other.endOffset; 60 startLine = other.startLine; 61 endLine = other.endLine; 62 } 63 Location(Token start, Token end)64 public Location(Token start, Token end) { 65 if (start == null) { 66 startLine = startOffset = NaN; 67 } else { 68 startLine = start.getLine() - 1; //token lines start from 1 69 startOffset = start.getCharPositionInLine(); 70 } 71 72 if (end == null) { 73 endLine = endOffset = NaN; 74 } else { 75 endLine = end.getLine() - 1; // token lines start from 1 76 String endText = end.getText(); 77 int lastLineStart = endText.lastIndexOf(System.lineSeparator()); 78 String lastLine = lastLineStart < 0 ? endText : endText.substring(lastLineStart + 1); 79 endOffset = end.getCharPositionInLine() + lastLine.length() - 1;//end is inclusive 80 } 81 } 82 83 public Location(ParserRuleContext context) { 84 this(context == null ? null : context.getStart(), 85 context == null ? null : context.getStop()); 86 } 87 88 public Location(int startLine, int startOffset, int endLine, int endOffset) { 89 this.startOffset = startOffset; 90 this.startLine = startLine; 91 this.endLine = endLine; 92 this.endOffset = endOffset; 93 } 94 95 @Override 96 public String toString() { 97 return "Location{" + 98 "startLine=" + startLine + 99 ", startOffset=" + startOffset + 100 ", endLine=" + endLine + 101 ", endOffset=" + endOffset + 102 ", parentLocation=" + parentLocation + 103 '}'; 104 } 105 106 public void setParentLocation(Location parentLocation) { 107 this.parentLocation = parentLocation; 108 } 109 110 @Override 111 public boolean equals(Object o) { 112 if (this == o) { 113 return true; 114 } 115 if (o == null || getClass() != o.getClass()) { 116 return false; 117 } 118 119 Location location = (Location) o; 120 121 if (endLine != location.endLine) { 122 return false; 123 } 124 if (endOffset != location.endOffset) { 125 return false; 126 } 127 if (startLine != location.startLine) { 128 return false; 129 } 130 if (startOffset != location.startOffset) { 131 return false; 132 } 133 if (parentLocation != null ? !parentLocation.equals(location.parentLocation) 134 : location.parentLocation != null) { 135 return false; 136 } 137 138 return true; 139 } 140 141 @Override 142 public int hashCode() { 143 int result = startLine; 144 result = 31 * result + startOffset; 145 result = 31 * result + endLine; 146 result = 31 * result + endOffset; 147 return result; 148 } 149 150 public boolean isValid() { 151 return startLine != NaN && endLine != NaN && startOffset != NaN && endOffset != NaN; 152 } 153 154 public boolean contains(Location other) { 155 if (startLine > other.startLine) { 156 return false; 157 } 158 if (startLine == other.startLine && startOffset > other.startOffset) { 159 return false; 160 } 161 if (endLine < other.endLine) { 162 return false; 163 } 164 if (endLine == other.endLine && endOffset < other.endOffset) { 165 return false; 166 } 167 return true; 168 } 169 getValidParentAbsoluteLocation()170 private Location getValidParentAbsoluteLocation() { 171 if (parentLocation == null) { 172 return null; 173 } 174 if (parentLocation.isValid()) { 175 return parentLocation.toAbsoluteLocation(); 176 } 177 return parentLocation.getValidParentAbsoluteLocation(); 178 } 179 toAbsoluteLocation()180 public Location toAbsoluteLocation() { 181 Location absoluteParent = getValidParentAbsoluteLocation(); 182 if (absoluteParent == null) { 183 return this; 184 } 185 Location copy = new Location(this); 186 boolean sameLine = copy.startLine == copy.endLine; 187 if (copy.startLine == 0) { 188 copy.startOffset += absoluteParent.startOffset; 189 } 190 if (sameLine) { 191 copy.endOffset += absoluteParent.startOffset; 192 } 193 194 copy.startLine += absoluteParent.startLine; 195 copy.endLine += absoluteParent.startLine; 196 return copy; 197 } 198 toUserReadableString()199 public String toUserReadableString() { 200 return startLine + ":" + startOffset + " - " + endLine + ":" + endOffset; 201 } 202 fromUserReadableString(String str)203 public static Location fromUserReadableString(String str) { 204 int glue = str.indexOf('-'); 205 if (glue == -1) { 206 return new Location(); 207 } 208 String start = str.substring(0, glue); 209 String end = str.substring(glue + 1); 210 int[] point = new int[]{-1, -1}; 211 Location location = new Location(); 212 parsePoint(start, point); 213 location.startLine = point[0]; 214 location.startOffset = point[1]; 215 point[0] = point[1] = -1; 216 parsePoint(end, point); 217 location.endLine = point[0]; 218 location.endOffset = point[1]; 219 return location; 220 } 221 parsePoint(String content, int[] into)222 private static boolean parsePoint(String content, int[] into) { 223 int index = content.indexOf(':'); 224 if (index == -1) { 225 return false; 226 } 227 into[0] = Integer.parseInt(content.substring(0, index).trim()); 228 into[1] = Integer.parseInt(content.substring(index + 1).trim()); 229 return true; 230 } 231 createScope()232 public LocationScopeProvider createScope() { 233 return new LocationScopeProvider() { 234 @Override 235 public List<Location> provideScopeLocation() { 236 return Arrays.asList(Location.this); 237 } 238 }; 239 } 240 } 241