/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 org.apache.lucene.util; import java.util.Arrays; import java.util.Comparator; /** * Represents char[], as a slice (offset + length) into an existing char[]. * The {@link #chars} member should never be null; use * {@link #EMPTY_CHARS} if necessary. * @lucene.internal */ public final class CharsRef implements Comparable<CharsRef>, CharSequence, Cloneable { /** An empty character array for convenience */ public static final char[] EMPTY_CHARS = new char[0]; /** The contents of the CharsRef. Should never be {@code null}. */ public char[] chars; /** Offset of first valid character. */ public int offset; /** Length of used characters. */ public int length; /** * Creates a new {@link CharsRef} initialized an empty array zero-length */ public CharsRef() { this(EMPTY_CHARS, 0, 0); } /** * Creates a new {@link CharsRef} initialized with an array of the given * capacity */ public CharsRef(int capacity) { chars = new char[capacity]; } /** * Creates a new {@link CharsRef} initialized with the given array, offset and * length */ public CharsRef(char[] chars, int offset, int length) { this.chars = chars; this.offset = offset; this.length = length; assert isValid(); } /** * Creates a new {@link CharsRef} initialized with the given Strings character * array */ public CharsRef(String string) { this.chars = string.toCharArray(); this.offset = 0; this.length = chars.length; } /** * Returns a shallow clone of this instance (the underlying characters are * <b>not</b> copied and will be shared by both the returned object and this * object. * * @see #deepCopyOf */ @Override public CharsRef clone() { return new CharsRef(chars, offset, length); } @Override public int hashCode() { final int prime = 31; int result = 0; final int end = offset + length; for (int i = offset; i < end; i++) { result = prime * result + chars[i]; } return result; } @Override public boolean equals(Object other) { if (other == null) { return false; } if (other instanceof CharsRef) { return this.charsEquals((CharsRef) other); } return false; } public boolean charsEquals(CharsRef other) { if (length == other.length) { int otherUpto = other.offset; final char[] otherChars = other.chars; final int end = offset + length; for (int upto = offset; upto < end; upto++, otherUpto++) { if (chars[upto] != otherChars[otherUpto]) { return false; } } return true; } else { return false; } } /** Signed int order comparison */ @Override public int compareTo(CharsRef other) { if (this == other) return 0; final char[] aChars = this.chars; int aUpto = this.offset; final char[] bChars = other.chars; int bUpto = other.offset; final int aStop = aUpto + Math.min(this.length, other.length); while (aUpto < aStop) { int aInt = aChars[aUpto++]; int bInt = bChars[bUpto++]; if (aInt > bInt) { return 1; } else if (aInt < bInt) { return -1; } } // One is a prefix of the other, or, they are equal: return this.length - other.length; } @Override public String toString() { return new String(chars, offset, length); } @Override public int length() { return length; } @Override public char charAt(int index) { // NOTE: must do a real check here to meet the specs of CharSequence if (index < 0 || index >= length) { throw new IndexOutOfBoundsException(); } return chars[offset + index]; } @Override public CharSequence subSequence(int start, int end) { // NOTE: must do a real check here to meet the specs of CharSequence if (start < 0 || end > length || start > end) { throw new IndexOutOfBoundsException(); } return new CharsRef(chars, offset + start, end - start); } /** @deprecated This comparator is only a transition mechanism */ @Deprecated private final static Comparator<CharsRef> utf16SortedAsUTF8SortOrder = new UTF16SortedAsUTF8Comparator(); /** @deprecated This comparator is only a transition mechanism */ @Deprecated public static Comparator<CharsRef> getUTF16SortedAsUTF8Comparator() { return utf16SortedAsUTF8SortOrder; } /** @deprecated This comparator is only a transition mechanism */ @Deprecated private static class UTF16SortedAsUTF8Comparator implements Comparator<CharsRef> { // Only singleton private UTF16SortedAsUTF8Comparator() {}; @Override public int compare(CharsRef a, CharsRef b) { if (a == b) return 0; final char[] aChars = a.chars; int aUpto = a.offset; final char[] bChars = b.chars; int bUpto = b.offset; final int aStop = aUpto + Math.min(a.length, b.length); while (aUpto < aStop) { char aChar = aChars[aUpto++]; char bChar = bChars[bUpto++]; if (aChar != bChar) { // http://icu-project.org/docs/papers/utf16_code_point_order.html /* aChar != bChar, fix up each one if they're both in or above the surrogate range, then compare them */ if (aChar >= 0xd800 && bChar >= 0xd800) { if (aChar >= 0xe000) { aChar -= 0x800; } else { aChar += 0x2000; } if (bChar >= 0xe000) { bChar -= 0x800; } else { bChar += 0x2000; } } /* now aChar and bChar are in code point order */ return (int)aChar - (int)bChar; /* int must be 32 bits wide */ } } // One is a prefix of the other, or, they are equal: return a.length - b.length; } } /** * Creates a new CharsRef that points to a copy of the chars from * <code>other</code> * <p> * The returned CharsRef will have a length of other.length * and an offset of zero. */ public static CharsRef deepCopyOf(CharsRef other) { return new CharsRef(Arrays.copyOfRange(other.chars, other.offset, other.offset + other.length), 0, other.length); } /** * Performs internal consistency checks. * Always returns true (or throws IllegalStateException) */ public boolean isValid() { if (chars == null) { throw new IllegalStateException("chars is null"); } if (length < 0) { throw new IllegalStateException("length is negative: " + length); } if (length > chars.length) { throw new IllegalStateException("length is out of bounds: " + length + ",chars.length=" + chars.length); } if (offset < 0) { throw new IllegalStateException("offset is negative: " + offset); } if (offset > chars.length) { throw new IllegalStateException("offset out of bounds: " + offset + ",chars.length=" + chars.length); } if (offset + length < 0) { throw new IllegalStateException("offset+length is negative: offset=" + offset + ",length=" + length); } if (offset + length > chars.length) { throw new IllegalStateException("offset+length out of bounds: offset=" + offset + ",length=" + length + ",chars.length=" + chars.length); } return true; } }