diff options
Diffstat (limited to 'lint/libs/lint_api/src/com/android/tools/lint/detector/api/Location.java')
-rw-r--r-- | lint/libs/lint_api/src/com/android/tools/lint/detector/api/Location.java | 712 |
1 files changed, 0 insertions, 712 deletions
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Location.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Location.java deleted file mode 100644 index f574189..0000000 --- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Location.java +++ /dev/null @@ -1,712 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.tools.lint.detector.api; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.google.common.annotations.Beta; - -import java.io.File; - -/** - * Location information for a warning - * <p/> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - */ -@Beta -public class Location { - private static final String SUPER_KEYWORD = "super"; //$NON-NLS-1$ - - private final File mFile; - private final Position mStart; - private final Position mEnd; - private String mMessage; - private Location mSecondary; - private Object mClientData; - - /** - * (Private constructor, use one of the factory methods - * {@link Location#create(File)}, - * {@link Location#create(File, Position, Position)}, or - * {@link Location#create(File, String, int, int)}. - * <p> - * Constructs a new location range for the given file, from start to end. If - * the length of the range is not known, end may be null. - * - * @param file the associated file (but see the documentation for - * {@link #getFile()} for more information on what the file - * represents) - * @param start the starting position, or null - * @param end the ending position, or null - */ - protected Location(@NonNull File file, @Nullable Position start, @Nullable Position end) { - super(); - mFile = file; - mStart = start; - mEnd = end; - } - - /** - * Returns the file containing the warning. Note that the file *itself* may - * not yet contain the error. When editing a file in the IDE for example, - * the tool could generate warnings in the background even before the - * document is saved. However, the file is used as a identifying token for - * the document being edited, and the IDE integration can map this back to - * error locations in the editor source code. - * - * @return the file handle for the location - */ - @NonNull - public File getFile() { - return mFile; - } - - /** - * The start position of the range - * - * @return the start position of the range, or null - */ - @Nullable - public Position getStart() { - return mStart; - } - - /** - * The end position of the range - * - * @return the start position of the range, may be null for an empty range - */ - @Nullable - public Position getEnd() { - return mEnd; - } - - /** - * Returns a secondary location associated with this location (if - * applicable), or null. - * - * @return a secondary location or null - */ - @Nullable - public Location getSecondary() { - return mSecondary; - } - - /** - * Sets a secondary location for this location. - * - * @param secondary a secondary location associated with this location - */ - public void setSecondary(@Nullable Location secondary) { - mSecondary = secondary; - } - - /** - * Sets a custom message for this location. This is typically used for - * secondary locations, to describe the significance of this alternate - * location. For example, for a duplicate id warning, the primary location - * might say "This is a duplicate id", pointing to the second occurrence of - * id declaration, and then the secondary location could point to the - * original declaration with the custom message "Originally defined here". - * - * @param message the message to apply to this location - */ - public void setMessage(@NonNull String message) { - mMessage = message; - } - - /** - * Returns the custom message for this location, if any. This is typically - * used for secondary locations, to describe the significance of this - * alternate location. For example, for a duplicate id warning, the primary - * location might say "This is a duplicate id", pointing to the second - * occurrence of id declaration, and then the secondary location could point - * to the original declaration with the custom message - * "Originally defined here". - * - * @return the custom message for this location, or null - */ - @Nullable - public String getMessage() { - return mMessage; - } - - /** - * Sets the client data associated with this location. This is an optional - * field which can be used by the creator of the {@link Location} to store - * temporary state associated with the location. - * - * @param clientData the data to store with this location - */ - public void setClientData(@Nullable Object clientData) { - mClientData = clientData; - } - - /** - * Returns the client data associated with this location - an optional field - * which can be used by the creator of the {@link Location} to store - * temporary state associated with the location. - * - * @return the data associated with this location - */ - @Nullable - public Object getClientData() { - return mClientData; - } - - @Override - public String toString() { - return "Location [file=" + mFile + ", start=" + mStart + ", end=" + mEnd + ", message=" - + mMessage + "]"; - } - - /** - * Creates a new location for the given file - * - * @param file the file to create a location for - * @return a new location - */ - @NonNull - public static Location create(@NonNull File file) { - return new Location(file, null /*start*/, null /*end*/); - } - - /** - * Creates a new location for the given file and starting and ending - * positions. - * - * @param file the file containing the positions - * @param start the starting position - * @param end the ending position - * @return a new location - */ - @NonNull - public static Location create( - @NonNull File file, - @NonNull Position start, - @Nullable Position end) { - return new Location(file, start, end); - } - - /** - * Creates a new location for the given file, with the given contents, for - * the given offset range. - * - * @param file the file containing the location - * @param contents the current contents of the file - * @param startOffset the starting offset - * @param endOffset the ending offset - * @return a new location - */ - @NonNull - public static Location create( - @NonNull File file, - @Nullable String contents, - int startOffset, - int endOffset) { - if (startOffset < 0 || endOffset < startOffset) { - throw new IllegalArgumentException("Invalid offsets"); - } - - if (contents == null) { - return new Location(file, - new DefaultPosition(-1, -1, startOffset), - new DefaultPosition(-1, -1, endOffset)); - } - - int size = contents.length(); - endOffset = Math.min(endOffset, size); - startOffset = Math.min(startOffset, endOffset); - Position start = null; - int line = 0; - int lineOffset = 0; - for (int offset = 0; offset <= size; offset++) { - if (offset == startOffset) { - start = new DefaultPosition(line, offset - lineOffset, offset); - } - if (offset == endOffset) { - Position end = new DefaultPosition(line, offset - lineOffset, offset); - return new Location(file, start, end); - } - char c = contents.charAt(offset); - if (c == '\n') { - lineOffset = offset + 1; - line++; - } - } - return Location.create(file); - } - - /** - * Creates a new location for the given file, with the given contents, for - * the given line number. - * - * @param file the file containing the location - * @param contents the current contents of the file - * @param line the line number (0-based) for the position - * @return a new location - */ - @NonNull - public static Location create(@NonNull File file, @NonNull String contents, int line) { - return create(file, contents, line, null, null, null); - } - - /** - * Creates a new location for the given file, with the given contents, for - * the given line number. - * - * @param file the file containing the location - * @param contents the current contents of the file - * @param line the line number (0-based) for the position - * @param patternStart an optional pattern to search for from the line - * match; if found, adjust the column and offsets to begin at the - * pattern start - * @param patternEnd an optional pattern to search for behind the start - * pattern; if found, adjust the end offset to match the end of - * the pattern - * @param hints optional additional information regarding the pattern search - * @return a new location - */ - @NonNull - public static Location create(@NonNull File file, @NonNull String contents, int line, - @Nullable String patternStart, @Nullable String patternEnd, - @Nullable SearchHints hints) { - int currentLine = 0; - int offset = 0; - while (currentLine < line) { - offset = contents.indexOf('\n', offset); - if (offset == -1) { - return Location.create(file); - } - currentLine++; - offset++; - } - - if (line == currentLine) { - if (patternStart != null) { - int index = offset; - - SearchDirection direction = SearchDirection.NEAREST; - if (hints != null) { - direction = hints.mDirection; - } - - if (direction == SearchDirection.BACKWARD) { - index = findPreviousMatch(contents, offset, patternStart, hints); - line = adjustLine(contents, line, offset, index); - } else if (direction == SearchDirection.EOL_BACKWARD) { - int lineEnd = contents.indexOf('\n', offset); - if (lineEnd == -1) { - lineEnd = contents.length(); - } - - index = findPreviousMatch(contents, lineEnd, patternStart, hints); - line = adjustLine(contents, line, offset, index); - } else if (direction == SearchDirection.FORWARD) { - index = findNextMatch(contents, offset, patternStart, hints); - line = adjustLine(contents, line, offset, index); - } else { - assert direction == SearchDirection.NEAREST; - - int before = findPreviousMatch(contents, offset, patternStart, hints); - int after = findNextMatch(contents, offset, patternStart, hints); - - if (before == -1) { - index = after; - line = adjustLine(contents, line, offset, index); - } else if (after == -1) { - index = before; - line = adjustLine(contents, line, offset, index); - } else if (offset - before < after - offset) { - index = before; - line = adjustLine(contents, line, offset, index); - } else { - index = after; - line = adjustLine(contents, line, offset, index); - } - } - - if (index != -1) { - int lineStart = contents.lastIndexOf('\n', index); - if (lineStart == -1) { - lineStart = 0; - } else { - lineStart++; // was pointing to the previous line's CR, not line start - } - int column = index - lineStart; - if (patternEnd != null) { - int end = contents.indexOf(patternEnd, offset + patternStart.length()); - if (end != -1) { - return new Location(file, new DefaultPosition(line, column, index), - new DefaultPosition(line, -1, end + patternEnd.length())); - } - } else if (hints != null && (hints.isJavaSymbol() || hints.isWholeWord())) { - if (hints.isConstructor() && contents.startsWith(SUPER_KEYWORD, index)) { - patternStart = SUPER_KEYWORD; - } - return new Location(file, new DefaultPosition(line, column, index), - new DefaultPosition(line, column + patternStart.length(), - index + patternStart.length())); - } - return new Location(file, new DefaultPosition(line, column, index), - new DefaultPosition(line, column, index + patternStart.length())); - } - } - - Position position = new DefaultPosition(line, -1, offset); - return new Location(file, position, position); - } - - return Location.create(file); - } - - private static int findPreviousMatch(@NonNull String contents, int offset, String pattern, - @Nullable SearchHints hints) { - while (true) { - int index = contents.lastIndexOf(pattern, offset); - if (index == -1) { - return -1; - } else { - if (isMatch(contents, index, pattern, hints)) { - return index; - } else { - offset = index - pattern.length(); - } - } - } - } - - private static int findNextMatch(@NonNull String contents, int offset, String pattern, - @Nullable SearchHints hints) { - int constructorIndex = -1; - if (hints != null && hints.isConstructor()) { - // Special condition: See if the call is referenced as "super" instead. - assert hints.isWholeWord(); - int index = contents.indexOf(SUPER_KEYWORD, offset); - if (index != -1 && isMatch(contents, index, SUPER_KEYWORD, hints)) { - constructorIndex = index; - } - } - - while (true) { - int index = contents.indexOf(pattern, offset); - if (index == -1) { - return constructorIndex; - } else { - if (isMatch(contents, index, pattern, hints)) { - if (constructorIndex != -1) { - return Math.min(constructorIndex, index); - } - return index; - } else { - offset = index + pattern.length(); - } - } - } - } - - private static boolean isMatch(@NonNull String contents, int offset, String pattern, - @Nullable SearchHints hints) { - if (!contents.startsWith(pattern, offset)) { - return false; - } - - if (hints != null) { - char prevChar = offset > 0 ? contents.charAt(offset - 1) : 0; - int lastIndex = offset + pattern.length() - 1; - char nextChar = lastIndex < contents.length() - 1 ? contents.charAt(lastIndex + 1) : 0; - - if (hints.isWholeWord() && (Character.isLetter(prevChar) - || Character.isLetter(nextChar))) { - return false; - - } - - if (hints.isJavaSymbol()) { - if (Character.isJavaIdentifierPart(prevChar) - || Character.isJavaIdentifierPart(nextChar)) { - return false; - } - - if (prevChar == '"') { - return false; - } - - // TODO: Additional validation to see if we're in a comment, string, etc. - // This will require lexing from the beginning of the buffer. - } - - if (hints.isConstructor() && SUPER_KEYWORD.equals(pattern)) { - // Only looking for super(), not super.x, so assert that the next - // non-space character is ( - int index = lastIndex + 1; - while (index < contents.length() - 1) { - char c = contents.charAt(index); - if (c == '(') { - break; - } else if (!Character.isWhitespace(c)) { - return false; - } - index++; - } - } - } - - return true; - } - - private static int adjustLine(String doc, int line, int offset, int newOffset) { - if (newOffset == -1) { - return line; - } - - if (newOffset < offset) { - return line - countLines(doc, newOffset, offset); - } else { - return line + countLines(doc, offset, newOffset); - } - } - - private static int countLines(String doc, int start, int end) { - int lines = 0; - for (int offset = start; offset < end; offset++) { - char c = doc.charAt(offset); - if (c == '\n') { - lines++; - } - } - - return lines; - } - - /** - * Reverses the secondary location list initiated by the given location - * - * @param location the first location in the list - * @return the first location in the reversed list - */ - public static Location reverse(Location location) { - Location next = location.getSecondary(); - location.setSecondary(null); - while (next != null) { - Location nextNext = next.getSecondary(); - next.setSecondary(location); - location = next; - next = nextNext; - } - - return location; - } - - /** - * A {@link Handle} is a reference to a location. The point of a location - * handle is to be able to create them cheaply, and then resolve them into - * actual locations later (if needed). This makes it possible to for example - * delay looking up line numbers, for locations that are offset based. - */ - public static interface Handle { - /** - * Compute a full location for the given handle - * - * @return create a location for this handle - */ - @NonNull - Location resolve(); - - /** - * Sets the client data associated with this location. This is an optional - * field which can be used by the creator of the {@link Location} to store - * temporary state associated with the location. - * - * @param clientData the data to store with this location - */ - public void setClientData(@Nullable Object clientData); - - /** - * Returns the client data associated with this location - an optional field - * which can be used by the creator of the {@link Location} to store - * temporary state associated with the location. - * - * @return the data associated with this location - */ - @Nullable - public Object getClientData(); - } - - /** A default {@link Handle} implementation for simple file offsets */ - public static class DefaultLocationHandle implements Handle { - private File mFile; - private String mContents; - private int mStartOffset; - private int mEndOffset; - private Object mClientData; - - /** - * Constructs a new {@link DefaultLocationHandle} - * - * @param context the context pointing to the file and its contents - * @param startOffset the start offset within the file - * @param endOffset the end offset within the file - */ - public DefaultLocationHandle(@NonNull Context context, int startOffset, int endOffset) { - mFile = context.file; - mContents = context.getContents(); - mStartOffset = startOffset; - mEndOffset = endOffset; - } - - @Override - @NonNull - public Location resolve() { - return Location.create(mFile, mContents, mStartOffset, mEndOffset); - } - - @Override - public void setClientData(@Nullable Object clientData) { - mClientData = clientData; - } - - @Override - @Nullable - public Object getClientData() { - return mClientData; - } - } - - /** - * Whether to look forwards, or backwards, or in both directions, when - * searching for a pattern in the source code to determine the right - * position range for a given symbol. - * <p> - * When dealing with bytecode for example, there are only line number entries - * within method bodies, so when searching for the method declaration, we should only - * search backwards from the first line entry in the method. - */ - public enum SearchDirection { - /** Only search forwards */ - FORWARD, - - /** Only search backwards */ - BACKWARD, - - /** Search backwards from the current end of line (normally it's the beginning of - * the current line) */ - EOL_BACKWARD, - - /** - * Search both forwards and backwards from the given line, and prefer - * the match that is closest - */ - NEAREST, - } - - /** - * Extra information pertaining to finding a symbol in a source buffer, - * used by {@link Location#create(File, String, int, String, String, SearchHints)} - */ - public static class SearchHints { - /** - * the direction to search for the nearest match in (provided - * {@code patternStart} is non null) - */ - @NonNull - private SearchDirection mDirection; - - /** Whether the matched pattern should be a whole word */ - private boolean mWholeWord; - - /** - * Whether the matched pattern should be a Java symbol (so for example, - * a match inside a comment or string literal should not be used) - */ - private boolean mJavaSymbol; - - /** - * Whether the matched pattern corresponds to a constructor; if so, look for - * some other possible source aliases too, such as "super". - */ - private boolean mConstructor; - - private SearchHints(SearchDirection direction) { - super(); - mDirection = direction; - } - - /** - * Constructs a new {@link SearchHints} object - * - * @param direction the direction to search in for the pattern - * @return a new @link SearchHints} object - */ - public static SearchHints create(SearchDirection direction) { - return new SearchHints(direction); - } - - /** - * Indicates that pattern matches should apply to whole words only - - * @return this, for constructor chaining - */ - public SearchHints matchWholeWord() { - mWholeWord = true; - - return this; - } - - /** @return true if the pattern match should be for whole words only */ - public boolean isWholeWord() { - return mWholeWord; - } - - /** - * Indicates that pattern matches should apply to Java symbols only - * - * @return this, for constructor chaining - */ - public SearchHints matchJavaSymbol() { - mJavaSymbol = true; - mWholeWord = true; - - return this; - } - - /** @return true if the pattern match should be for Java symbols only */ - public boolean isJavaSymbol() { - return mJavaSymbol; - } - - /** - * Indicates that pattern matches should apply to constructors. If so, look for - * some other possible source aliases too, such as "super". - * - * @return this, for constructor chaining - */ - public SearchHints matchConstructor() { - mConstructor = true; - mWholeWord = true; - mJavaSymbol = true; - - return this; - } - - /** @return true if the pattern match should be for a constructor */ - public boolean isConstructor() { - return mConstructor; - } - } -} |