All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.android.tools.lint.detector.api.CharSequences Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2016 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.android.utils.XmlUtils;
import java.io.IOException;
import java.io.Reader;
import java.nio.CharBuffer;
import java.util.Arrays;
import org.w3c.dom.Document;

/**
 * A number of utility methods around {@link CharSequence} handling, which
 * adds methods that are available on Strings (such as {@code indexOf},
 * {@code startsWith} and {@code regionMatches} and provides equivalent methods
 * for character sequences.
 * 

* 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. */ public class CharSequences { public static int indexOf(@NonNull CharSequence sequence, char c) { return indexOf(sequence, c, 0); } public static int indexOf(@NonNull CharSequence sequence, char c, int start) { for (int i = start; i < sequence.length(); i++) { if (sequence.charAt(i) == c) { return i; } } return -1; } public static int lastIndexOf(@NonNull CharSequence haystack, @NonNull String needle, int start) { int length = haystack.length(); int needleLength = needle.length(); if (needleLength <= length && start >= 0) { if (needleLength > 0) { if (start > length - needleLength) { start = length - needleLength; } char firstChar = needle.charAt(0); while (true) { int i = lastIndexOf(haystack, firstChar, start); if (i == -1) { return -1; } int o1 = i, o2 = 0; //noinspection StatementWithEmptyBody while (++o2 < needleLength && haystack.charAt(++o1) == needle.charAt(o2)) { } if (o2 == needleLength) { return i; } start = i - 1; } } return start < length ? start : length; } return -1; } public static int lastIndexOf(@NonNull CharSequence sequence, char c) { return lastIndexOf(sequence, c, sequence.length()); } public static int lastIndexOf(@NonNull CharSequence sequence, int c, int start) { int length = sequence.length(); if (start >= 0) { if (start >= length) { start = length - 1; } for (int i = start; i >= 0; i--) { if (sequence.charAt(i) == c) { return i; } } } return -1; } public static int lastIndexOf(@NonNull CharSequence haystack, @NonNull String needle) { return lastIndexOf(haystack, needle, haystack.length()); } public static boolean regionMatches( @NonNull CharSequence sequence, int thisStart, @NonNull CharSequence string, int start, int length) { if (start < 0 || string.length() - start < length) { return false; } if (thisStart < 0 || sequence.length() - thisStart < length) { return false; } if (length <= 0) { return true; } for (int i = 0; i < length; ++i) { if (sequence.charAt(thisStart + i) != string.charAt(start + i)) { return false; } } return true; } public static boolean regionMatches( @NonNull CharSequence sequence, boolean ignoreCase, int thisStart, @NonNull CharSequence string, int start, int length) { if (!ignoreCase) { return regionMatches(sequence, thisStart, string, start, length); } if (thisStart < 0 || length > sequence.length() - thisStart) { return false; } if (start < 0 || length > string.length() - start) { return false; } int end = thisStart + length; while (thisStart < end) { char c1 = sequence.charAt(thisStart++); char c2 = string.charAt(start++); if (c1 != c2 && foldCase(c1) != foldCase(c2)) { return false; } } return true; } private static char foldCase(char ch) { if (ch < 128) { if ('A' <= ch && ch <= 'Z') { return (char) (ch + ('a' - 'A')); } return ch; } return Character.toLowerCase(Character.toUpperCase(ch)); } public static boolean startsWith(@NonNull CharSequence sequence, @NonNull CharSequence prefix) { return startsWith(sequence, prefix, 0); } public static boolean startsWith(@NonNull CharSequence sequence, @NonNull CharSequence prefix, int start) { int sequenceLength = sequence.length(); int prefixLength = prefix.length(); if (sequenceLength + start < prefixLength) { return false; } for (int i = start, j = 0; j < prefixLength; i++, j++) { if (sequence.charAt(i) != prefix.charAt(j)) { return false; } } return true; } public static int indexOf(@NonNull CharSequence haystack, CharSequence needle) { return indexOf(haystack, needle, 0); } public static int indexOf(@NonNull CharSequence haystack, CharSequence needle, int start) { int needleLength = needle.length(); if (needleLength == 0) { return start; } char first = needle.charAt(0); if (needleLength == 1) { return indexOf(haystack, first); } search: for (int i = start, max = haystack.length() - needleLength; i <= max; i++) { if (haystack.charAt(i) == first) { for (int h = i + 1, n = 1; n < needleLength; h++, n++) { if (haystack.charAt(h) != needle.charAt(n)) { continue search; } } return i; } } return -1; } @NonNull public static CharSequence createSequence(@NonNull char[] data) { return new LintCharSequence(data); } @NonNull public static CharSequence createSequence(@NonNull char[] data, int offset, int length) { return new LintCharSequence(data, offset, length); } @NonNull public static char[] getCharArray(@NonNull CharSequence sequence) { if (sequence instanceof LintCharSequence) { return ((LintCharSequence)sequence).getCharArray(); } return sequence.toString().toCharArray(); } @NonNull public static Reader getReader(@NonNull CharSequence data, boolean stripBom) { CharSequenceReader reader = new CharSequenceReader(data); if (stripBom) { if (data.length() > 0 && data.charAt(0) == '\uFEFF') { // Skip BOM try { //noinspection ResultOfMethodCallIgnored reader.read(); } catch (IOException ignore) { // I/O errors can't happen for char sequence backed readers } } } return reader; } @Nullable public static Document parseDocumentSilently(@NonNull CharSequence xml, boolean namespaceAware) { try { Reader reader = getReader(xml, true); return XmlUtils.parseDocument(reader, namespaceAware); } catch (Exception e) { // pass // This method is deliberately silent; will return null } return null; } /** * A {@link CharSequence} intended for use by lint; it is a char[]-backed * {@linkplain CharSequence} which can provide its backing array to lint * (which is useful to avoid having duplicated data, since for example the * ECJ-based backend needs char[] instances of the source files instead * of Strings, and the String class always insists on having its own * private copy of the char array. */ private static class LintCharSequence implements CharSequence { public final char[] data; private final int offset; private final int length; public LintCharSequence(@NonNull char[] data) { this(data, 0, data.length); } public LintCharSequence(@NonNull char[] data, int offset, int length) { this.data = data; this.offset = offset; this.length = length; } @NonNull public char[] getCharArray() { if (offset == 0 && length == data.length) { return data; } else { return Arrays.copyOfRange(data, offset, offset + length); } } @Override public int length() { return length; } @Override public char charAt(int index) { return data[offset + index]; } @Override public CharSequence subSequence(int start, int end) { return new LintCharSequence(data, offset + start, end - start); } @NonNull @Override public String toString() { return new String(data, offset, length); } } // Copy from package private Guava implementation (com.google.common.io), // minus precondition checks, plus annotations private static final class CharSequenceReader extends Reader { private CharSequence seq; private int pos; private int mark; public CharSequenceReader(@NonNull CharSequence seq) { this.seq = seq; } private boolean hasRemaining() { return remaining() > 0; } private int remaining() { return seq.length() - pos; } @Override public synchronized int read(@NonNull CharBuffer target) throws IOException { if (!hasRemaining()) { return -1; } int charsToRead = Math.min(target.remaining(), remaining()); for (int i = 0; i < charsToRead; i++) { target.put(seq.charAt(pos++)); } return charsToRead; } @Override public synchronized int read() throws IOException { return hasRemaining() ? seq.charAt(pos++) : -1; } @Override public synchronized int read(@NonNull char[] cbuf, int off, int len) throws IOException { if (!hasRemaining()) { return -1; } int charsToRead = Math.min(len, remaining()); for (int i = 0; i < charsToRead; i++) { cbuf[off + i] = seq.charAt(pos++); } return charsToRead; } @Override public synchronized long skip(long n) throws IOException { int charsToSkip = (int) Math.min(remaining(), n); pos += charsToSkip; return charsToSkip; } @Override public synchronized boolean ready() throws IOException { return true; } @Override public boolean markSupported() { return true; } @Override public synchronized void mark(int readAheadLimit) throws IOException { mark = pos; } @Override public synchronized void reset() throws IOException { pos = mark; } @Override public synchronized void close() throws IOException { seq = null; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy