blob: f28cc7819e7198472636d9786adef00440974363 [file] [log] [blame]
/*
* 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.analysis.tokenattributes;
import java.nio.CharBuffer;
import org.apache.lucene.util.ArrayUtil;
import org.apache.lucene.util.AttributeImpl;
import org.apache.lucene.util.AttributeReflector;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.BytesRefBuilder;
import org.apache.lucene.util.FutureObjects;
/** Default implementation of {@link CharTermAttribute}. */
public class CharTermAttributeImpl extends AttributeImpl implements CharTermAttribute, TermToBytesRefAttribute, Cloneable {
private static int MIN_BUFFER_SIZE = 10;
private char[] termBuffer = new char[ArrayUtil.oversize(MIN_BUFFER_SIZE, Character.BYTES)];
private int termLength = 0;
/** May be used by subclasses to convert to different charsets / encodings for implementing {@link #getBytesRef()}. */
protected BytesRefBuilder builder = new BytesRefBuilder();
/** Initialize this attribute with empty term text */
public CharTermAttributeImpl() {}
@Override
public final void copyBuffer(char[] buffer, int offset, int length) {
growTermBuffer(length);
System.arraycopy(buffer, offset, termBuffer, 0, length);
termLength = length;
}
@Override
public final char[] buffer() {
return termBuffer;
}
@Override
public final char[] resizeBuffer(int newSize) {
if(termBuffer.length < newSize){
// Not big enough; create a new array with slight
// over allocation and preserve content
final char[] newCharBuffer = new char[ArrayUtil.oversize(newSize, Character.BYTES)];
System.arraycopy(termBuffer, 0, newCharBuffer, 0, termBuffer.length);
termBuffer = newCharBuffer;
}
return termBuffer;
}
private void growTermBuffer(int newSize) {
if(termBuffer.length < newSize){
// Not big enough; create a new array with slight
// over allocation:
termBuffer = new char[ArrayUtil.oversize(newSize, Character.BYTES)];
}
}
@Override
public final CharTermAttribute setLength(int length) {
FutureObjects.checkFromIndexSize(0, length, termBuffer.length);
termLength = length;
return this;
}
@Override
public final CharTermAttribute setEmpty() {
termLength = 0;
return this;
}
// *** TermToBytesRefAttribute interface ***
@Override
public BytesRef getBytesRef() {
builder.copyChars(termBuffer, 0, termLength);
return builder.get();
}
// *** CharSequence interface ***
@Override
public final int length() {
return termLength;
}
@Override
public final char charAt(int index) {
FutureObjects.checkIndex(index, termLength);
return termBuffer[index];
}
@Override
public final CharSequence subSequence(final int start, final int end) {
FutureObjects.checkFromToIndex(start, end, termLength);
return new String(termBuffer, start, end - start);
}
// *** Appendable interface ***
@Override
public final CharTermAttribute append(CharSequence csq) {
if (csq == null) // needed for Appendable compliance
return appendNull();
return append(csq, 0, csq.length());
}
@Override
public final CharTermAttribute append(CharSequence csq, int start, int end) {
if (csq == null) // needed for Appendable compliance
csq = "null";
// TODO: the optimized cases (jdk methods) will already do such checks, maybe re-organize this?
FutureObjects.checkFromToIndex(start, end, csq.length());
final int len = end - start;
if (len == 0)
return this;
resizeBuffer(termLength + len);
if (len > 4) { // only use instanceof check series for longer CSQs, else simply iterate
if (csq instanceof String) {
((String) csq).getChars(start, end, termBuffer, termLength);
} else if (csq instanceof StringBuilder) {
((StringBuilder) csq).getChars(start, end, termBuffer, termLength);
} else if (csq instanceof CharTermAttribute) {
System.arraycopy(((CharTermAttribute) csq).buffer(), start, termBuffer, termLength, len);
} else if (csq instanceof CharBuffer && ((CharBuffer) csq).hasArray()) {
final CharBuffer cb = (CharBuffer) csq;
System.arraycopy(cb.array(), cb.arrayOffset() + cb.position() + start, termBuffer, termLength, len);
} else if (csq instanceof StringBuffer) {
((StringBuffer) csq).getChars(start, end, termBuffer, termLength);
} else {
while (start < end)
termBuffer[termLength++] = csq.charAt(start++);
// no fall-through here, as termLength is updated!
return this;
}
termLength += len;
return this;
} else {
while (start < end)
termBuffer[termLength++] = csq.charAt(start++);
return this;
}
}
@Override
public final CharTermAttribute append(char c) {
resizeBuffer(termLength + 1)[termLength++] = c;
return this;
}
// *** For performance some convenience methods in addition to CSQ's ***
@Override
public final CharTermAttribute append(String s) {
if (s == null) // needed for Appendable compliance
return appendNull();
final int len = s.length();
s.getChars(0, len, resizeBuffer(termLength + len), termLength);
termLength += len;
return this;
}
@Override
public final CharTermAttribute append(StringBuilder s) {
if (s == null) // needed for Appendable compliance
return appendNull();
final int len = s.length();
s.getChars(0, len, resizeBuffer(termLength + len), termLength);
termLength += len;
return this;
}
@Override
public final CharTermAttribute append(CharTermAttribute ta) {
if (ta == null) // needed for Appendable compliance
return appendNull();
final int len = ta.length();
System.arraycopy(ta.buffer(), 0, resizeBuffer(termLength + len), termLength, len);
termLength += len;
return this;
}
private CharTermAttribute appendNull() {
resizeBuffer(termLength + 4);
termBuffer[termLength++] = 'n';
termBuffer[termLength++] = 'u';
termBuffer[termLength++] = 'l';
termBuffer[termLength++] = 'l';
return this;
}
// *** AttributeImpl ***
@Override
public int hashCode() {
int code = termLength;
code = code * 31 + ArrayUtil.hashCode(termBuffer, 0, termLength);
return code;
}
@Override
public void clear() {
termLength = 0;
}
@Override
public CharTermAttributeImpl clone() {
CharTermAttributeImpl t = (CharTermAttributeImpl)super.clone();
// Do a deep clone
t.termBuffer = new char[this.termLength];
System.arraycopy(this.termBuffer, 0, t.termBuffer, 0, this.termLength);
t.builder = new BytesRefBuilder();
t.builder.copyBytes(builder.get());
return t;
}
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}
if (other instanceof CharTermAttributeImpl) {
final CharTermAttributeImpl o = ((CharTermAttributeImpl) other);
if (termLength != o.termLength)
return false;
for(int i=0;i<termLength;i++) {
if (termBuffer[i] != o.termBuffer[i]) {
return false;
}
}
return true;
}
return false;
}
/**
* Returns solely the term text as specified by the
* {@link CharSequence} interface.
*/
@Override
public String toString() {
return new String(termBuffer, 0, termLength);
}
@Override
public void reflectWith(AttributeReflector reflector) {
reflector.reflect(CharTermAttribute.class, "term", toString());
reflector.reflect(TermToBytesRefAttribute.class, "bytes", getBytesRef());
}
@Override
public void copyTo(AttributeImpl target) {
CharTermAttribute t = (CharTermAttribute) target;
t.copyBuffer(termBuffer, 0, termLength);
}
}