/*
* Copyright (C) 2013 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.idea.gradle.output.parser.aapt;
import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
import com.intellij.util.text.StringSearcher;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.util.List;
/**
* A read-only representation of the text of a file.
*/
class ReadOnlyDocument {
@NotNull private final String myContents;
@NotNull 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(@NotNull 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);
}
myContents = xml;
myFile = file;
myLastModified = file.lastModified();
myOffsets = Lists.newArrayListWithExpectedSize(myContents.length() / 30);
myOffsets.add(0);
for (int i = 0; i < myContents.length(); i++) {
char c = myContents.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 text 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(String text, int offset) {
StringSearcher searcher = new StringSearcher(text, true, true);
return searcher.scan(myContents, offset, myContents.length());
}
int findTextBackwards(String text, int offset) {
StringSearcher searcher = new StringSearcher(text, true, false);
return searcher.scan(myContents, 0, 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 myContents.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 myContents.substring(start, end == -1 ? myContents.length() : end);
}
/**
* Returns the contents of the document
* @return the contents
*/
String getContents() {
return myContents;
}
/**
* @return the size (or length) of the document.
*/
int length() {
return myContents.length();
}
}