blob: 7fb92b263144c0e8a5206abb0217cf3b9bd08a6a [file] [log] [blame]
/*
* 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 com.android.ide.common.blame.parser.aapt;
import com.android.annotations.NonNull;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
/**
* A read-only representation of the text of a file.
*/
class ReadOnlyDocument {
@NonNull
private final String mFileContents;
@NonNull
private final List<Integer> myOffsets;
private File myFile;
private long myLastModified;
/**
* Creates a new {@link ReadOnlyDocument} for the given file.
*
* @param file the file whose text will be stored in the document. UTF-8 charset is used to
* decode the contents of the file.
* @throws java.io.IOException if an error occurs while reading the file.
*/
@SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
ReadOnlyDocument(@NonNull File file) throws IOException {
String xml = Files.toString(file, Charsets.UTF_8);
if (xml.startsWith("\uFEFF")) { // Strip byte order mark if necessary
xml = xml.substring(1);
}
mFileContents = xml;
myFile = file;
myLastModified = file.lastModified();
myOffsets = Lists.newArrayListWithExpectedSize(mFileContents.length() / 30);
myOffsets.add(0);
for (int i = 0; i < mFileContents.length(); i++) {
char c = mFileContents.charAt(i);
if (c == '\n') {
myOffsets.add(i + 1);
}
}
}
/**
* Returns true if the document contents are stale (e.g. no longer current)
*/
public boolean isStale() {
long now = myFile.lastModified();
return now == 0L || myLastModified < now;
}
/**
* Returns the offset of the given line number, relative to the beginning of the document.
*
* @param lineNumber the given line number.
* @return the offset of the given line. -1 is returned if the document is empty, or if the
* given line number is negative or greater than the number of lines in the document.
*/
int lineOffset(int lineNumber) {
int index = lineNumber - 1;
if (index < 0 || index >= myOffsets.size()) {
return -1;
}
return myOffsets.get(index);
}
/**
* Returns the line number of the given offset.
*
* @param offset the given offset.
* @return the line number of the given offset. -1 is returned if the document is empty or if
* the offset is greater than the position of the last character in the document.
*/
int lineNumber(int offset) {
for (int i = 0; i < myOffsets.size(); i++) {
int savedOffset = myOffsets.get(i);
if (offset <= savedOffset) {
return i;
}
}
return -1;
}
/**
* Finds the given text in the document, starting from the given offset.
*
* @param needle the text to find.
* @param offset the starting point of the search.
* @return the offset of the found result, or -1 if no match was found.
*/
int findText(@NonNull String needle, int offset) {
Preconditions.checkPositionIndex(offset, mFileContents.length());
return mFileContents.indexOf(needle, offset);
}
int findTextBackwards(String needle, int offset) {
Preconditions.checkPositionIndex(offset, mFileContents.length());
return mFileContents.lastIndexOf(needle, offset);
}
/**
* Returns the character at the given offset.
*
* @param offset the position, relative to the beginning of the document, of the character to
* return.
* @return the character at the given offset.
* @throws IndexOutOfBoundsException if the {@code offset} argument is negative or not less than
* the document's size.
*/
char charAt(int offset) {
return mFileContents.charAt(offset);
}
/**
* Returns the sub sequence for the given range.
*
* @param start the starting offset.
* @param end the ending offset, or -1 for the end of the file.
* @return the sub sequence.
*/
String subsequence(int start, int end) {
return mFileContents.substring(start, end == -1 ? mFileContents.length() : end);
}
/**
* Returns the contents of the document
*
* @return the contents
*/
String getContents() {
return mFileContents;
}
/**
* @return the size (or length) of the document.
*/
int length() {
return mFileContents.length();
}
}