/*******************************************************************************
 * Copyright (c) 2009, 2010 Wind River Systems, Inc. and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Markus Schorn - initial API and implementation
 *     Sergey Prigogin (Google)
 *******************************************************************************/ 
package org.eclipse.cdt.internal.core.parser.scanner;

/**
 * Abstract class for providing input to the lexer.
 * @since 5.2
 */
public abstract class AbstractCharArray {
	/**
	 * Returns the length of this array or -1 if it is yet, unknown. This method may be called
	 * before the array has been traversed.
	 */
	public abstract int tryGetLength();
	
	/**
	 * Returns the length of the array. This method is called only after the lexer has worked its
	 * way through the array. Therefore for subclasses it is efficient enough to read through to
	 * the end of the array and provide the length. 
	 */
	public abstract int getLength();

	/** 
	 * Checks whether the given offset is valid for this array. Subclasses may assume
	 * that offset is non-negative.
	 */
	public abstract boolean isValidOffset(int offset);

	/**
	 * Computes 64-bit hash value of the character array. This method doesn't cause any I/O if
	 * called after the array has been traversed.
	 * @return The hash value of the contents of the array.
	 */
	public abstract long getContentsHash();

	/**
	 * Returns the character at the given position, subclasses do not have to do range checks.
	 */
	public abstract char get(int offset);

	/**
	 * Copies a range of characters to the given destination. Subclasses do not have to do any
	 * range checks.
	 */
	public abstract void arraycopy(int offset, char[] destination, int destinationPos, int length);

	/**
	 * Returns the {@link CharSequence} representing a range in the character array.
	 */
	public CharSequence subSequence(int start, int end) {
		return new SubArray(start, end);
	}

	/**
	 * Returns {@code true} if there were I/O errors while retrieving contents of this array.
	 */
	public abstract boolean hasError();

	/**
	 * This method is slow. Use only for debugging.
	 */
	@Override
	public String toString() {
		StringBuilder buf = new StringBuilder();
		for (int pos = 0; isValidOffset(pos); pos++) {
			buf.append(get(pos));
		}
		return buf.toString();
	}

	private class SubArray implements CharSequence {
		private final int start;
		private final int end;

		SubArray(int start, int end) {
			checkStartEnd(start, end);
			this.start = start;
			this.end = end;
		}

		@Override
		public int length() {
			return end - start;
		}

		@Override
		public char charAt(int index) {
			return get(start + index);
		}

		@Override
		public CharSequence subSequence(int start, int end) {
			checkStartEnd(start, end);
			if (end > this.end - this.start)
				throw new IndexOutOfBoundsException(String.valueOf(end));
			return new SubArray(this.start + start, this.start + end);
		}

		private void checkStartEnd(int start, int end) {
			if (start < 0)
				throw new IndexOutOfBoundsException(String.valueOf(start));
			if (end < start)
				throw new IndexOutOfBoundsException(String.valueOf(end) + " < " + String.valueOf(start)); //$NON-NLS-1$
		}
	}
}
