/*
 * 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 CharTermAttribute.
/** 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 getBytesRef().
/** 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
/** 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 CharSequence interface.
/** * 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); } }