/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.databinding.tool.store; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.Token; import org.apache.commons.lang3.StringUtils; import android.databinding.tool.processing.scopes.LocationScopeProvider; import java.util.Arrays; import java.util.List; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; /** * Identifies the range of a code block inside a file or a string. * Note that, unlike antlr4 tokens, the line positions start from 0 (to be compatible with Studio). *

* Both start and end line/column indices are inclusive. */ @XmlAccessorType(XmlAccessType.NONE) public class Location { public static final int NaN = -1; @XmlAttribute(name = "startLine") public int startLine; @XmlAttribute(name = "startOffset") public int startOffset; @XmlAttribute(name = "endLine") public int endLine; @XmlAttribute(name = "endOffset") public int endOffset; @XmlElement(name = "parentLocation") public Location parentLocation; // for XML unmarshalling public Location() { startOffset = endOffset = startLine = endLine = NaN; } public Location(Location other) { startOffset = other.startOffset; endOffset = other.endOffset; startLine = other.startLine; endLine = other.endLine; } public Location(Token start, Token end) { if (start == null) { startLine = startOffset = NaN; } else { startLine = start.getLine() - 1; //token lines start from 1 startOffset = start.getCharPositionInLine(); } if (end == null) { endLine = endOffset = NaN; } else { endLine = end.getLine() - 1; // token lines start from 1 String endText = end.getText(); int lastLineStart = endText.lastIndexOf(System.lineSeparator()); String lastLine = lastLineStart < 0 ? endText : endText.substring(lastLineStart + 1); endOffset = end.getCharPositionInLine() + lastLine.length() - 1;//end is inclusive } } public Location(ParserRuleContext context) { this(context == null ? null : context.getStart(), context == null ? null : context.getStop()); } public Location(int startLine, int startOffset, int endLine, int endOffset) { this.startOffset = startOffset; this.startLine = startLine; this.endLine = endLine; this.endOffset = endOffset; } @Override public String toString() { return "Location{" + "startLine=" + startLine + ", startOffset=" + startOffset + ", endLine=" + endLine + ", endOffset=" + endOffset + ", parentLocation=" + parentLocation + '}'; } public void setParentLocation(Location parentLocation) { this.parentLocation = parentLocation; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Location location = (Location) o; if (endLine != location.endLine) { return false; } if (endOffset != location.endOffset) { return false; } if (startLine != location.startLine) { return false; } if (startOffset != location.startOffset) { return false; } if (parentLocation != null ? !parentLocation.equals(location.parentLocation) : location.parentLocation != null) { return false; } return true; } @Override public int hashCode() { int result = startLine; result = 31 * result + startOffset; result = 31 * result + endLine; result = 31 * result + endOffset; return result; } public boolean isValid() { return startLine != NaN && endLine != NaN && startOffset != NaN && endOffset != NaN; } public boolean contains(Location other) { if (startLine > other.startLine) { return false; } if (startLine == other.startLine && startOffset > other.startOffset) { return false; } if (endLine < other.endLine) { return false; } if (endLine == other.endLine && endOffset < other.endOffset) { return false; } return true; } private Location getValidParentAbsoluteLocation() { if (parentLocation == null) { return null; } if (parentLocation.isValid()) { return parentLocation.toAbsoluteLocation(); } return parentLocation.getValidParentAbsoluteLocation(); } public Location toAbsoluteLocation() { Location absoluteParent = getValidParentAbsoluteLocation(); if (absoluteParent == null) { return this; } Location copy = new Location(this); boolean sameLine = copy.startLine == copy.endLine; if (copy.startLine == 0) { copy.startOffset += absoluteParent.startOffset; } if (sameLine) { copy.endOffset += absoluteParent.startOffset; } copy.startLine += absoluteParent.startLine; copy.endLine += absoluteParent.startLine; return copy; } public String toUserReadableString() { return startLine + ":" + startOffset + " - " + endLine + ":" + endOffset; } public static Location fromUserReadableString(String str) { int glue = str.indexOf('-'); if (glue == -1) { return new Location(); } String start = str.substring(0, glue); String end = str.substring(glue + 1); int[] point = new int[]{-1, -1}; Location location = new Location(); parsePoint(start, point); location.startLine = point[0]; location.startOffset = point[1]; point[0] = point[1] = -1; parsePoint(end, point); location.endLine = point[0]; location.endOffset = point[1]; return location; } private static boolean parsePoint(String content, int[] into) { int index = content.indexOf(':'); if (index == -1) { return false; } into[0] = Integer.parseInt(content.substring(0, index).trim()); into[1] = Integer.parseInt(content.substring(index + 1).trim()); return true; } public LocationScopeProvider createScope() { return new LocationScopeProvider() { @Override public List provideScopeLocation() { return Arrays.asList(Location.this); } }; } }