| /* |
| * 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; |
| import java.util.Objects; |
| |
| /** |
| * 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) { |
| return Arrays.equals(this.chars, this.offset, this.offset + this.length, |
| other.chars, other.offset, other.offset + other.length); |
| } |
| |
| /** Signed int order comparison */ |
| @Override |
| public int compareTo(CharsRef other) { |
| return Arrays.compare(this.chars, this.offset, this.offset + this.length, |
| other.chars, other.offset, other.offset + 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 |
| Objects.checkIndex(index, length); |
| 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 |
| Objects.checkFromToIndex(start, end, length); |
| 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) { |
| int aEnd = a.offset + a.length; |
| int bEnd = b.offset + b.length; |
| int i = Arrays.mismatch(a.chars, a.offset, aEnd, |
| b.chars, b.offset, bEnd); |
| |
| if (i >= 0 && i < Math.min(a.length, b.length)) { |
| // http://icu-project.org/docs/papers/utf16_code_point_order.html |
| |
| char aChar = a.chars[a.offset + i]; |
| char bChar = b.chars[b.offset + i]; |
| /* 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(ArrayUtil.copyOfSubArray(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; |
| } |
| } |