/*
 * @(#)Segment.java	1.25 06/04/07
 *
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package javax.swing.text;

import java.text.CharacterIterator;

/**
 * A segment of a character array representing a fragment
 * of text.  It should be treated as immutable even though
 * the array is directly accessible.  This gives fast access
 * to fragments of text without the overhead of copying
 * around characters.  This is effectively an unprotected
 * String.
 * <p>
 * The Segment implements the java.text.CharacterIterator
 * interface to support use with the i18n support without
 * copying text into a string.
 *
 * @author  Timothy Prinzing
 * @version 1.25 04/07/06
 */
public class Segment implements Cloneable, CharacterIterator, CharSequence {

    /**
     * This is the array containing the text of
     * interest.  This array should never be modified;
     * it is available only for efficiency.
     */
    public char[] array;

    /**
     * This is the offset into the array that
     * the desired text begins.
     */
    public int offset;

    /**
     * This is the number of array elements that
     * make up the text of interest.
     */
    public int count;

    private boolean partialReturn;

    /**
     * Creates a new segment.
     */
    public Segment() {
	this(null, 0, 0);
    }

    /**
     * Creates a new segment referring to an existing array.
     *
     * @param array the array to refer to
     * @param offset the offset into the array
     * @param count the number of characters
     */
    public Segment(char[] array, int offset, int count) {
	this.array = array;
	this.offset = offset;
	this.count = count;
	partialReturn = false;
    }

    /** 
     * Flag to indicate that partial returns are valid.  If the flag is true, 
     * an implementation of the interface method Document.getText(position,length,Segment) 
     * should return as much text as possible without making a copy.  The default 
     * state of the flag is false which will cause Document.getText(position,length,Segment) 
     * to provide the same return behavior it always had, which may or may not 
     * make a copy of the text depending upon the request.   
     * 
     * @param p whether or not partial returns are valid.
     * @since 1.4
     */
    public void setPartialReturn(boolean p) {
	partialReturn = p;
    }

    /**
     * Flag to indicate that partial returns are valid. 
     *
     * @return whether or not partial returns are valid.
     * @since 1.4
     */
    public boolean isPartialReturn() {
	return partialReturn;
    }

    /**
     * Converts a segment into a String.
     *
     * @return the string
     */
    public String toString() {
	if (array != null) {
	    return new String(array, offset, count);
	}
	return new String();
    }

    // --- CharacterIterator methods -------------------------------------

    /**
     * Sets the position to getBeginIndex() and returns the character at that
     * position.
     * @return the first character in the text, or DONE if the text is empty
     * @see #getBeginIndex
     * @since 1.3
     */
    public char first() {
	pos = offset;
	if (count != 0) {
	    return array[pos];
	}
	return DONE;
    }

    /**
     * Sets the position to getEndIndex()-1 (getEndIndex() if the text is empty)
     * and returns the character at that position.
     * @return the last character in the text, or DONE if the text is empty
     * @see #getEndIndex
     * @since 1.3
     */
    public char last() {
	pos = offset + count;
	if (count != 0) {
	    pos -= 1;
	    return array[pos];
	}
	return DONE;
    }

    /**
     * Gets the character at the current position (as returned by getIndex()).
     * @return the character at the current position or DONE if the current
     * position is off the end of the text.
     * @see #getIndex
     * @since 1.3
     */
    public char current() {
	if (count != 0 && pos < offset + count) {
	    return array[pos];
	}
	return DONE;
    }

    /**
     * Increments the iterator's index by one and returns the character
     * at the new index.  If the resulting index is greater or equal
     * to getEndIndex(), the current index is reset to getEndIndex() and
     * a value of DONE is returned.
     * @return the character at the new position or DONE if the new
     * position is off the end of the text range.
     * @since 1.3
     */
    public char next() {
	pos += 1;
	int end = offset + count;
	if (pos >= end) {
	    pos = end;
	    return DONE;
	}
	return current();
    }

    /**
     * Decrements the iterator's index by one and returns the character
     * at the new index. If the current index is getBeginIndex(), the index
     * remains at getBeginIndex() and a value of DONE is returned.
     * @return the character at the new position or DONE if the current
     * position is equal to getBeginIndex().
     * @since 1.3
     */
    public char previous() {
	if (pos == offset) {
	    return DONE;
	}
	pos -= 1;
	return current();
    }

    /**
     * Sets the position to the specified position in the text and returns that
     * character.
     * @param position the position within the text.  Valid values range from
     * getBeginIndex() to getEndIndex().  An IllegalArgumentException is thrown
     * if an invalid value is supplied.
     * @return the character at the specified position or DONE if the specified position is equal to getEndIndex()
     * @since 1.3
     */
    public char setIndex(int position) {
	int end = offset + count;
	if ((position < offset) || (position > end)) {
	    throw new IllegalArgumentException("bad position: " + position);
	}
	pos = position;
	if ((pos != end) && (count != 0)) {
	    return array[pos];
	}
	return DONE;
    }

    /**
     * Returns the start index of the text.
     * @return the index at which the text begins.
     * @since 1.3
     */
    public int getBeginIndex() {
	return offset;
    }

    /**
     * Returns the end index of the text.  This index is the index of the first
     * character following the end of the text.
     * @return the index after the last character in the text
     * @since 1.3
     */
    public int getEndIndex() {
	return offset + count;
    }

    /**
     * Returns the current index.
     * @return the current index.
     * @since 1.3
     */
    public int getIndex() {
	return pos;
    }

    // --- CharSequence methods -------------------------------------
    
    /** 
     * {@inheritDoc} 
     * @since 1.6
     */
    public char charAt(int index) {
        if (index < 0 
            || index >= count) {
            throw new StringIndexOutOfBoundsException(index);
        }
        return array[offset + index];
    }

    /** 
     * {@inheritDoc} 
     * @since 1.6
     */
    public int length() {
        return count;
    }

    /** 
     * {@inheritDoc} 
     * @since 1.6
     */
    public CharSequence subSequence(int start, int end) {
        if (start < 0) {
            throw new StringIndexOutOfBoundsException(start);
        }
        if (end > count) {
            throw new StringIndexOutOfBoundsException(end);
        }
        if (start > end) {
            throw new StringIndexOutOfBoundsException(end - start);
        }
        Segment segment = new Segment();
        segment.array = this.array;
        segment.offset = this.offset + start;
        segment.count = end - start;
        return segment;
    }
    
    /**
     * Creates a shallow copy.
     *
     * @return the copy
     */
    public Object clone() {
	Object o;
	try {
	    o = super.clone();
	} catch (CloneNotSupportedException cnse) {
	    o = null;
	}
	return o;
    }

    private int pos;


}


