| // Copyright 2014 Google Inc. All rights reserved. |
| // |
| // 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 com.google.devtools.build.lib.events; |
| |
| import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; |
| import com.google.devtools.build.lib.vfs.Path; |
| import com.google.devtools.build.lib.vfs.PathFragment; |
| |
| import java.io.Serializable; |
| |
| /** |
| * A Location is a range of characters within a file. |
| * |
| * The start and end locations may be the same, in which case the Location |
| * denotes a point in the file, not a range. The path may be null, indicating |
| * an unknown file. |
| * |
| * Implementations of Location should be optimised for speed of construction, |
| * not speed of attribute access, as far more Locations are created during |
| * parsing than are ever used to display error messages. |
| */ |
| public abstract class Location implements Serializable { |
| |
| @Immutable |
| private static final class LocationWithPathAndStartColumn extends Location { |
| private final PathFragment path; |
| private final LineAndColumn startLineAndColumn; |
| |
| private LocationWithPathAndStartColumn(Path path, int startOffSet, int endOffSet, |
| LineAndColumn startLineAndColumn) { |
| super(startOffSet, endOffSet); |
| this.path = path != null ? path.asFragment() : null; |
| this.startLineAndColumn = startLineAndColumn; |
| } |
| |
| @Override |
| public PathFragment getPath() { return path; } |
| |
| @Override |
| public LineAndColumn getStartLineAndColumn() { |
| return startLineAndColumn; |
| } |
| } |
| |
| protected final int startOffset; |
| protected final int endOffset; |
| |
| /** |
| * Returns a Location with a given Path, start and end offset and start line and column info. |
| */ |
| public static Location fromPathAndStartColumn(Path path, int startOffSet, int endOffSet, |
| LineAndColumn startLineAndColumn) { |
| return new LocationWithPathAndStartColumn(path, startOffSet, endOffSet, startLineAndColumn); |
| } |
| |
| /** |
| * Returns a Location relating to file 'path', but not to any specific part |
| * region within the file. Try to use a more specific location if possible. |
| */ |
| public static Location fromFile(Path path) { |
| return fromFileAndOffsets(path, 0, 0); |
| } |
| |
| /** |
| * Returns a Location relating to the subset of file 'path', starting at |
| * 'startOffset' and ending at 'endOffset'. |
| */ |
| public static Location fromFileAndOffsets(final Path path, |
| int startOffset, |
| int endOffset) { |
| return new LocationWithPathAndStartColumn(path, startOffset, endOffset, null); |
| } |
| |
| protected Location(int startOffset, int endOffset) { |
| this.startOffset = startOffset; |
| this.endOffset = endOffset; |
| } |
| |
| /** |
| * Returns the start offset relative to the beginning of the file the object |
| * resides in. |
| */ |
| public final int getStartOffset() { |
| return startOffset; |
| } |
| |
| /** |
| * Returns the end offset relative to the beginning of the file the object |
| * resides in. |
| * |
| * <p>The end offset is one position past the actual end position, making this method |
| * behave in a compatible fashion with {@link String#substring(int, int)}. |
| * |
| * <p>To compute the length of this location, use {@code getEndOffset() - getStartOffset()}. |
| */ |
| public final int getEndOffset() { |
| return endOffset; |
| } |
| |
| /** |
| * Returns the path of the file to which the start/end offsets refer. May be |
| * null if the file name information is not available. |
| * |
| * This method is intentionally abstract, as a space optimisation. Some |
| * subclass instances implement sharing of common data (e.g. tables for |
| * convering offsets into line numbers) and this enables them to share the |
| * Path value in the same way. |
| */ |
| public abstract PathFragment getPath(); |
| |
| /** |
| * Returns a (line, column) pair corresponding to the position denoted by |
| * getStartOffset. Returns null if this information is not available. |
| */ |
| public LineAndColumn getStartLineAndColumn() { |
| return null; |
| } |
| |
| /** |
| * Returns a (line, column) pair corresponding to the position denoted by |
| * getEndOffset. Returns null if this information is not available. |
| */ |
| public LineAndColumn getEndLineAndColumn() { |
| return null; |
| } |
| |
| /** |
| * A default implementation of toString() that formats the location in the |
| * following ways based on the amount of information available: |
| * <pre> |
| * "foo.cc:23:2" |
| * "23:2" |
| * "foo.cc:char offsets 123--456" |
| * "char offsets 123--456" |
| * </pre> |
| */ |
| public String print() { |
| StringBuilder buf = new StringBuilder(); |
| if (getPath() != null) { |
| buf.append(getPath()).append(':'); |
| } |
| LineAndColumn start = getStartLineAndColumn(); |
| if (start == null) { |
| if (getStartOffset() == 0 && getEndOffset() == 0) { |
| buf.append("1"); // i.e. line 1 (special case: no information at all) |
| } else { |
| buf.append("char offsets "). |
| append(getStartOffset()).append("--").append(getEndOffset()); |
| } |
| } else { |
| buf.append(start.getLine()).append(':').append(start.getColumn()); |
| } |
| return buf.toString(); |
| } |
| |
| /** |
| * Prints the object in a sort of reasonable way. This should never be used in user-visible |
| * places, only for debugging and testing. |
| */ |
| @Override |
| public String toString() { |
| return print(); |
| } |
| |
| /** |
| * A value class that describes the line and column of an offset in a file. |
| */ |
| @Immutable |
| public static final class LineAndColumn { |
| private final int line; |
| private final int column; |
| |
| public LineAndColumn(int line, int column) { |
| this.line = line; |
| this.column = column; |
| } |
| |
| public int getLine() { |
| return line; |
| } |
| |
| public int getColumn() { |
| return column; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (o == this) { |
| return true; |
| } |
| if (!(o instanceof LineAndColumn)) { |
| return false; |
| } |
| LineAndColumn lac = (LineAndColumn) o; |
| return lac.line == line && lac.column == column; |
| } |
| |
| @Override |
| public int hashCode() { |
| return line * 81 + column; |
| } |
| } |
| } |