content: package org.apache.lucene.analysis.tokenattributes;
  
  /**
   * 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.
   */
  
  import java.io.Serializable;
  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.RamUsageEstimator;
  
  /**
   * The term text of a Token.
   */
  public class CharTermAttributeImpl extends AttributeImpl implements CharTermAttribute, TermAttribute, Cloneable, Serializable {
    private static int MIN_BUFFER_SIZE = 10;
    
    private char[] termBuffer = new char[ArrayUtil.oversize(MIN_BUFFER_SIZE, RamUsageEstimator.NUM_BYTES_CHAR)];
    private int termLength = 0;
    
    @Deprecated
    public String term() {
      // don't delegate to toString() here!
      return new String(termBuffer, 0, termLength);
    }
  
    public final void copyBuffer(char[] buffer, int offset, int length) {
      growTermBuffer(length);
      System.arraycopy(buffer, offset, termBuffer, 0, length);
      termLength = length;
    }
  
    @Deprecated
    public void setTermBuffer(char[] buffer, int offset, int length) {
      copyBuffer(buffer, offset, length);
    }
  
    @Deprecated
    public void setTermBuffer(String buffer) {
      int length = buffer.length();
      growTermBuffer(length);
      buffer.getChars(0, length, termBuffer, 0);
      termLength = length;
    }
  
    @Deprecated
    public void setTermBuffer(String buffer, int offset, int length) {
      assert offset <= buffer.length();
      assert offset + length <= buffer.length();
      growTermBuffer(length);
      buffer.getChars(offset, offset + length, termBuffer, 0);
      termLength = length;
    }
  
    public final char[] buffer() {
      return termBuffer;
    }
  
    @Deprecated
    public char[] termBuffer() {
      return termBuffer;
    }
    
    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, RamUsageEstimator.NUM_BYTES_CHAR)];
        System.arraycopy(termBuffer, 0, newCharBuffer, 0, termBuffer.length);
        termBuffer = newCharBuffer;
      }
      return termBuffer;   
    }
  
    @Deprecated
    public char[] resizeTermBuffer(int newSize) {
      return resizeBuffer(newSize);
    }
    
    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, RamUsageEstimator.NUM_BYTES_CHAR)];
      }
    }
    
    @Deprecated
    public int termLength() {
      return termLength;
    }
  
    public final CharTermAttribute setLength(int length) {
      if (length > termBuffer.length)
        throw new IllegalArgumentException("length " + length + " exceeds the size of the termBuffer (" + termBuffer.length + ")");
      termLength = length;
      return this;
    }
    
    public final CharTermAttribute setEmpty() {
      termLength = 0;
      return this;
    }
    
    @Deprecated
    public void setTermLength(int length) {
      setLength(length);
    }
    
    // *** CharSequence interface ***
    public final int length() {
      return termLength;
    }
    
    public final char charAt(int index) {
      if (index >= termLength)
        throw new IndexOutOfBoundsException();
      return termBuffer[index];
    }
    
    public final CharSequence subSequence(final int start, final int end) {
      if (start > termLength || end > termLength)
        throw new IndexOutOfBoundsException();
      return new String(termBuffer, start, end - start);
    }
    
    // *** Appendable interface ***
  
    public final CharTermAttribute append(CharSequence csq) {
      if (csq == null) // needed for Appendable compliance
        return appendNull();
      return append(csq, 0, csq.length());
    }
    
    public final CharTermAttribute append(CharSequence csq, int start, int end) {
      if (csq == null) // needed for Appendable compliance
        csq = "null";
      final int len = end - start, csqlen = csq.length();
      if (len < 0 || start > csqlen || end > csqlen)
        throw new IndexOutOfBoundsException();
      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;
      }
    }
    
    public final CharTermAttribute append(char c) {
      resizeBuffer(termLength + 1)[termLength++] = c;
      return this;
    }
    
    // *** For performance some convenience methods in addition to CSQ's ***
    
    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;
    }
    
    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;
    }
    
    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 Object 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);
      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.
     * <p>This method changed the behavior with Lucene 3.1,
     * before it returned a String representation of the whole
     * term with all attributes.
     * This affects especially the
     * {@link org.apache.lucene.analysis.Token} subclass.
     */
    @Override
    public String toString() {
      // CharSequence requires that only the contents are returned, but this is orginal code: "term=" + new String(termBuffer, 0, termLength)
      return new String(termBuffer, 0, termLength);
    }
    
    @Override
    public void reflectWith(AttributeReflector reflector) {
      reflector.reflect(CharTermAttribute.class, "term", toString());
    }
    
    @Override
    public void copyTo(AttributeImpl target) {
      if (target instanceof CharTermAttribute) {
        CharTermAttribute t = (CharTermAttribute) target;
        t.copyBuffer(termBuffer, 0, termLength);
      } else {
        TermAttribute t = (TermAttribute) target;
        t.setTermBuffer(termBuffer, 0, termLength);
      }
    }
  
  }
  