content: /*******************************************************************************
   * Copyright (c) 2000, 2010 IBM Corporation 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:
   *     IBM Corporation - initial API and implementation
   *******************************************************************************/
  package ts.eclipse.ide.jsdt.internal.ui.editor;
  
  import org.eclipse.core.runtime.Assert;
  import org.eclipse.jface.text.BadLocationException;
  import org.eclipse.jface.text.IDocument;
  import org.eclipse.jface.text.IRegion;
  import org.eclipse.wst.jsdt.core.JavaScriptCore;
  import org.eclipse.wst.jsdt.core.formatter.DefaultCodeFormatterConstants;
  import org.eclipse.wst.jsdt.internal.ui.text.DocumentCharacterIterator;
  import org.eclipse.wst.jsdt.internal.ui.text.JavaHeuristicScanner;
  import org.eclipse.wst.jsdt.internal.ui.text.Symbols;
  
  import ts.eclipse.ide.core.TypeScriptCorePlugin;
  import ts.eclipse.ide.core.preferences.TypeScriptCorePreferenceConstants;
  import ts.eclipse.ide.core.utils.PreferencesHelper;
  import ts.resources.ITypeScriptFile;
  
  /**
   * Uses the {@link org.eclipse.wst.jsdt.internal.ui.text.JavaHeuristicScanner}
   * to get the indentation level for a certain position in a document.
   *
   * <p>
   * An instance holds some internal position in the document and is therefore not
   * threadsafe.
   * </p>
   *
   * 
   */
  public final class TypeScriptIndenter {
  
  	/**
  	 * The JDT Core preferences.
  	 * 
  	 */
  	private final class CorePrefs {
  		final boolean prefUseTabs;
  		final int prefTabSize;
  		final int prefIndentationSize;
  		final boolean prefArrayDimensionsDeepIndent;
  		final int prefArrayIndent;
  		final boolean prefArrayDeepIndent;
  		final boolean prefTernaryDeepAlign;
  		final int prefTernaryIndent;
  		final int prefCaseIndent;
  		final int prefAssignmentIndent;
  		final int prefCaseBlockIndent;
  		final int prefSimpleIndent;
  		final int prefBracketIndent;
  		final boolean prefMethodDeclDeepIndent;
  		final int prefMethodDeclIndent;
  		final boolean prefMethodCallDeepIndent;
  		final int prefMethodCallIndent;
  		final boolean prefParenthesisDeepIndent;
  		final int prefParenthesisIndent;
  		final int prefBlockIndent;
  		final int prefMethodBodyIndent;
  		final int prefTypeIndent;
  		final boolean prefIndentBracesForBlocks;
  		final boolean prefIndentBracesForArrays;
  		final boolean prefIndentBracesForMethods;
  		final boolean prefIndentBracesForTypes;
  		final int prefContinuationIndent;
  		final boolean prefHasGenerics;
  		final String prefTabChar;
  
  		private final ITypeScriptFile fProject;
  
  		/**
  		 * Returns <code>true</code> if the class is used outside the workbench,
  		 * <code>false</code> in normal mode
  		 *
  		 * @return <code>true</code> if the plug-ins are not available
  		 */
  		private boolean isStandalone() {
  			return JavaScriptCore.getPlugin() == null;
  		}
  
  		/**
  		 * Returns the possibly project-specific core preference defined under
  		 * <code>key</code>.
  		 *
  		 * @param key
  		 *            the key of the preference
  		 * @return the value of the preference
  		 * 
  		 */
  		private String getCoreFormatterOption(String key) {
  			// if (fProject == null)
  			return JavaScriptCore.getOption(key);
  			// return fProject.getOption(key, true);
  		}
  
  		CorePrefs(ITypeScriptFile project) {
  			fProject = project;
  			if (isStandalone()) {
  				prefUseTabs = true;
  				prefTabSize = 4;
  				prefIndentationSize = 4;
  				prefArrayDimensionsDeepIndent = true;
  				prefContinuationIndent = 2;
  				prefBlockIndent = 1;
  				prefArrayIndent = prefContinuationIndent;
  				prefArrayDeepIndent = true;
  				prefTernaryDeepAlign = false;
  				prefTernaryIndent = prefContinuationIndent;
  				prefCaseIndent = 0;
  				prefAssignmentIndent = prefBlockIndent;
  				prefCaseBlockIndent = prefBlockIndent;
  				prefIndentBracesForBlocks = false;
  				prefSimpleIndent = (prefIndentBracesForBlocks && prefBlockIndent == 0) ? 1 : prefBlockIndent;
  				prefBracketIndent = prefBlockIndent;
  				prefMethodDeclDeepIndent = true;
  				prefMethodDeclIndent = 1;
  				prefMethodCallDeepIndent = false;
  				prefMethodCallIndent = 1;
  				prefParenthesisDeepIndent = false;
  				prefParenthesisIndent = prefContinuationIndent;
  				prefMethodBodyIndent = 1;
  				prefTypeIndent = 1;
  				prefIndentBracesForArrays = false;
  				prefIndentBracesForMethods = false;
  				prefIndentBracesForTypes = false;
  				prefHasGenerics = false;
  				prefTabChar = JavaScriptCore.TAB;
  			} else {
  				prefUseTabs = prefUseTabs();
  				prefTabSize = prefTabSize();
  				prefIndentationSize = prefIndentationSize();
  				prefArrayDimensionsDeepIndent = prefArrayDimensionsDeepIndent();
  				prefContinuationIndent = prefContinuationIndent();
  				prefBlockIndent = prefBlockIndent();
  				prefArrayIndent = prefArrayIndent();
  				prefArrayDeepIndent = prefArrayDeepIndent();
  				prefTernaryDeepAlign = prefTernaryDeepAlign();
  				prefTernaryIndent = prefTernaryIndent();
  				prefCaseIndent = prefCaseIndent();
  				prefAssignmentIndent = prefAssignmentIndent();
  				prefCaseBlockIndent = prefCaseBlockIndent();
  				prefIndentBracesForBlocks = prefIndentBracesForBlocks();
  				prefSimpleIndent = prefSimpleIndent();
  				prefBracketIndent = prefBracketIndent();
  				prefMethodDeclDeepIndent = prefMethodDeclDeepIndent();
  				prefMethodDeclIndent = prefMethodDeclIndent();
  				prefMethodCallDeepIndent = prefMethodCallDeepIndent();
  				prefMethodCallIndent = prefMethodCallIndent();
  				prefParenthesisDeepIndent = prefParenthesisDeepIndent();
  				prefParenthesisIndent = prefParenthesisIndent();
  				prefMethodBodyIndent = prefMethodBodyIndent();
  				prefTypeIndent = prefTypeIndent();
  				prefIndentBracesForArrays = prefIndentBracesForArrays();
  				prefIndentBracesForMethods = prefIndentBracesForMethods();
  				prefIndentBracesForTypes = prefIndentBracesForTypes();
  				prefHasGenerics = hasGenerics();
  				prefTabChar = getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR);
  			}
  		}
  
  		private boolean prefUseTabs() {
  			if (fProject != null) {
  				return !fProject.getFormatOptions().getConvertTabsToSpaces();
  			}
  			return PreferencesHelper.getWorkspacePreferences(TypeScriptCorePlugin.PLUGIN_ID).getBoolean(
  					TypeScriptCorePreferenceConstants.EDITOR_OPTIONS_CONVERT_TABS_TO_SPACES,
  					TypeScriptCorePreferenceConstants.EDITOR_OPTIONS_CONVERT_TABS_TO_SPACES_DEFAULT);
  		}
  
  		private int prefTabSize() {
  			if (fProject != null) {
  				return fProject.getFormatOptions().getTabSize();
  			}
  			return PreferencesHelper.getWorkspacePreferences(TypeScriptCorePlugin.PLUGIN_ID).getInt(
  					TypeScriptCorePreferenceConstants.EDITOR_OPTIONS_TAB_SIZE,
  					TypeScriptCorePreferenceConstants.EDITOR_OPTIONS_TAB_SIZE_DEFAULT);
  		}
  
  		private int prefIndentationSize() {
  			if (fProject != null) {
  				return fProject.getFormatOptions().getIndentSize();
  			}
  			return PreferencesHelper.getWorkspacePreferences(TypeScriptCorePlugin.PLUGIN_ID).getInt(
  					TypeScriptCorePreferenceConstants.EDITOR_OPTIONS_INDENT_SIZE,
  					TypeScriptCorePreferenceConstants.EDITOR_OPTIONS_INDENT_SIZE_DEFAULT);
  		}
  
  		private boolean prefArrayDimensionsDeepIndent() {
  			return true; // sensible default, no formatter setting
  		}
  
  		private int prefArrayIndent() {
  			String option = getCoreFormatterOption(
  					DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_EXPRESSIONS_IN_ARRAY_INITIALIZER);
  			try {
  				if (DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_BY_ONE)
  					return 1;
  			} catch (IllegalArgumentException e) {
  				// ignore and return default
  			}
  
  			return prefContinuationIndent(); // default
  		}
  
  		private boolean prefArrayDeepIndent() {
  			String option = getCoreFormatterOption(
  					DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_EXPRESSIONS_IN_ARRAY_INITIALIZER);
  			try {
  				return DefaultCodeFormatterConstants
  						.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
  			} catch (IllegalArgumentException e) {
  				// ignore and return default
  			}
  
  			return true;
  		}
  
  		private boolean prefTernaryDeepAlign() {
  			String option = getCoreFormatterOption(
  					DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_CONDITIONAL_EXPRESSION);
  			try {
  				return DefaultCodeFormatterConstants
  						.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
  			} catch (IllegalArgumentException e) {
  				// ignore and return default
  			}
  			return false;
  		}
  
  		private int prefTernaryIndent() {
  			String option = getCoreFormatterOption(
  					DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_CONDITIONAL_EXPRESSION);
  			try {
  				if (DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_BY_ONE)
  					return 1;
  				else
  					return prefContinuationIndent();
  			} catch (IllegalArgumentException e) {
  				// ignore and return default
  			}
  
  			return prefContinuationIndent();
  		}
  
  		private int prefCaseIndent() {
  			if (DefaultCodeFormatterConstants.TRUE.equals(getCoreFormatterOption(
  					DefaultCodeFormatterConstants.FORMATTER_INDENT_SWITCHSTATEMENTS_COMPARE_TO_SWITCH)))
  				return prefBlockIndent();
  			else
  				return 0;
  		}
  
  		private int prefAssignmentIndent() {
  			return prefBlockIndent();
  		}
  
  		private int prefCaseBlockIndent() {
  			if (true)
  				return prefBlockIndent();
  
  			if (DefaultCodeFormatterConstants.TRUE.equals(getCoreFormatterOption(
  					DefaultCodeFormatterConstants.FORMATTER_INDENT_SWITCHSTATEMENTS_COMPARE_TO_CASES)))
  				return prefBlockIndent();
  			else
  				return 0;
  		}
  
  		private int prefSimpleIndent() {
  			if (prefIndentBracesForBlocks() && prefBlockIndent() == 0)
  				return 1;
  			else
  				return prefBlockIndent();
  		}
  
  		private int prefBracketIndent() {
  			return prefBlockIndent();
  		}
  
  		private boolean prefMethodDeclDeepIndent() {
  			String option = getCoreFormatterOption(
  					DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_PARAMETERS_IN_METHOD_DECLARATION);
  			try {
  				return DefaultCodeFormatterConstants
  						.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
  			} catch (IllegalArgumentException e) {
  				// ignore and return default
  			}
  
  			return true;
  		}
  
  		private int prefMethodDeclIndent() {
  			String option = getCoreFormatterOption(
  					DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_PARAMETERS_IN_METHOD_DECLARATION);
  			try {
  				if (DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_BY_ONE)
  					return 1;
  				else
  					return prefContinuationIndent();
  			} catch (IllegalArgumentException e) {
  				// ignore and return default
  			}
  			return 1;
  		}
  
  		private boolean prefMethodCallDeepIndent() {
  			String option = getCoreFormatterOption(
  					DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_ARGUMENTS_IN_METHOD_INVOCATION);
  			try {
  				return DefaultCodeFormatterConstants
  						.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
  			} catch (IllegalArgumentException e) {
  				// ignore and return default
  			}
  			return false; // sensible default
  		}
  
  		private int prefMethodCallIndent() {
  			String option = getCoreFormatterOption(
  					DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_ARGUMENTS_IN_METHOD_INVOCATION);
  			try {
  				if (DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_BY_ONE)
  					return 1;
  				else
  					return prefContinuationIndent();
  			} catch (IllegalArgumentException e) {
  				// ignore and return default
  			}
  
  			return 1; // sensible default
  		}
  
  		private boolean prefParenthesisDeepIndent() {
  			if (true) // don't do parenthesis deep indentation
  				return false;
  
  			String option = getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_CONTINUATION_INDENTATION);
  			try {
  				return DefaultCodeFormatterConstants
  						.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
  			} catch (IllegalArgumentException e) {
  				// ignore and return default
  			}
  
  			return false; // sensible default
  		}
  
  		private int prefParenthesisIndent() {
  			return prefContinuationIndent();
  		}
  
  		private int prefBlockIndent() {
  			String option = getCoreFormatterOption(
  					DefaultCodeFormatterConstants.FORMATTER_INDENT_STATEMENTS_COMPARE_TO_BLOCK);
  			if (DefaultCodeFormatterConstants.FALSE.equals(option))
  				return 0;
  
  			return 1; // sensible default
  		}
  
  		private int prefMethodBodyIndent() {
  			if (DefaultCodeFormatterConstants.FALSE.equals(
  					getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_INDENT_STATEMENTS_COMPARE_TO_BODY)))
  				return 0;
  
  			return 1; // sensible default
  		}
  
  		private int prefTypeIndent() {
  			String option = getCoreFormatterOption(
  					DefaultCodeFormatterConstants.FORMATTER_INDENT_BODY_DECLARATIONS_COMPARE_TO_TYPE_HEADER);
  			if (DefaultCodeFormatterConstants.FALSE.equals(option))
  				return 0;
  
  			return 1; // sensible default
  		}
  
  		private boolean prefIndentBracesForBlocks() {
  			return DefaultCodeFormatterConstants.NEXT_LINE_SHIFTED
  					.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_BLOCK));
  		}
  
  		private boolean prefIndentBracesForArrays() {
  			return DefaultCodeFormatterConstants.NEXT_LINE_SHIFTED.equals(getCoreFormatterOption(
  					DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_ARRAY_INITIALIZER));
  		}
  
  		private boolean prefIndentBracesForMethods() {
  			return DefaultCodeFormatterConstants.NEXT_LINE_SHIFTED.equals(getCoreFormatterOption(
  					DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_METHOD_DECLARATION));
  		}
  
  		private boolean prefIndentBracesForTypes() {
  			return DefaultCodeFormatterConstants.NEXT_LINE_SHIFTED.equals(getCoreFormatterOption(
  					DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_TYPE_DECLARATION));
  		}
  
  		private int prefContinuationIndent() {
  			try {
  				return Integer.parseInt(
  						getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_CONTINUATION_INDENTATION));
  			} catch (NumberFormatException e) {
  				// ignore and return default
  			}
  
  			return 2; // sensible default
  		}
  
  		private boolean hasGenerics() {
  			return JavaScriptCore.VERSION_1_5.compareTo(getCoreFormatterOption(JavaScriptCore.COMPILER_SOURCE)) <= 0;
  		}
  	}
  
  	/** The document being scanned. */
  	private final IDocument fDocument;
  	/** The indentation accumulated by <code>findReferencePosition</code>. */
  	private int fIndent;
  	/**
  	 * The absolute (character-counted) indentation offset for special cases
  	 * (method defs, array initializers)
  	 */
  	private int fAlign;
  	/** The stateful scanposition for the indentation methods. */
  	private int fPosition;
  	/** The previous position. */
  	private int fPreviousPos;
  	/** The most recent token. */
  	private int fToken;
  	/** The line of <code>fPosition</code>. */
  	private int fLine;
  	/**
  	 * The scanner we will use to scan the document. It has to be installed on
  	 * the same document as the one we get.
  	 */
  	private final JavaHeuristicScanner fScanner;
  	/**
  	 * The JDT Core preferences.
  	 * 
  	 */
  	private final CorePrefs fPrefs;
  
  	/**
  	 * Creates a new instance.
  	 *
  	 * @param document
  	 *            the document to scan
  	 * @param scanner
  	 *            the {@link JavaHeuristicScanner} to be used for scanning the
  	 *            document. It must be installed on the same
  	 *            <code>IDocument</code>.
  	 */
  	public TypeScriptIndenter(IDocument document, JavaHeuristicScanner scanner) {
  		this(document, scanner, null);
  	}
  
  	/**
  	 * Creates a new instance.
  	 *
  	 * @param document
  	 *            the document to scan
  	 * @param scanner
  	 *            the {@link JavaHeuristicScanner}to be used for scanning the
  	 *            document. It must be installed on the same
  	 *            <code>IDocument</code>.
  	 * @param project
  	 *            the java project to get the formatter preferences from, or
  	 *            <code>null</code> to use the workspace settings
  	 * 
  	 */
  	public TypeScriptIndenter(IDocument document, JavaHeuristicScanner scanner, ITypeScriptFile project) {
  		Assert.isNotNull(document);
  		Assert.isNotNull(scanner);
  		fDocument = document;
  		fScanner = scanner;
  		fPrefs = new CorePrefs(project);
  	}
  
  	/**
  	 * Computes the indentation at the reference point of <code>position</code>.
  	 *
  	 * @param offset
  	 *            the offset in the document
  	 * @return a String which reflects the indentation at the line in which the
  	 *         reference position to <code>offset</code> resides, or
  	 *         <code>null</code> if it cannot be determined
  	 */
  	public StringBuffer getReferenceIndentation(int offset) {
  		return getReferenceIndentation(offset, false);
  	}
  
  	/**
  	 * Computes the indentation at the reference point of <code>position</code>.
  	 *
  	 * @param offset
  	 *            the offset in the document
  	 * @param assumeOpeningBrace
  	 *            <code>true</code> if an opening brace should be assumed
  	 * @return a String which reflects the indentation at the line in which the
  	 *         reference position to <code>offset</code> resides, or
  	 *         <code>null</code> if it cannot be determined
  	 */
  	private StringBuffer getReferenceIndentation(int offset, boolean assumeOpeningBrace) {
  
  		int unit;
  		if (assumeOpeningBrace)
  			unit = findReferencePosition(offset, Symbols.TokenLBRACE);
  		else
  			unit = findReferencePosition(offset, peekChar(offset));
  
  		// if we were unable to find anything, return null
  		if (unit == JavaHeuristicScanner.NOT_FOUND)
  			return null;
  
  		return getLeadingWhitespace(unit);
  
  	}
  
  	/**
  	 * Computes the indentation at <code>offset</code>.
  	 *
  	 * @param offset
  	 *            the offset in the document
  	 * @return a String which reflects the correct indentation for the line in
  	 *         which offset resides, or <code>null</code> if it cannot be
  	 *         determined
  	 */
  	public StringBuffer computeIndentation(int offset) {
  		return computeIndentation(offset, false);
  	}
  
  	/**
  	 * Computes the indentation at <code>offset</code>.
  	 *
  	 * @param offset
  	 *            the offset in the document
  	 * @param assumeOpeningBrace
  	 *            <code>true</code> if an opening brace should be assumed
  	 * @return a String which reflects the correct indentation for the line in
  	 *         which offset resides, or <code>null</code> if it cannot be
  	 *         determined
  	 */
  	public StringBuffer computeIndentation(int offset, boolean assumeOpeningBrace) {
  
  		StringBuffer reference = getReferenceIndentation(offset, assumeOpeningBrace);
  
  		// handle special alignment
  		if (fAlign != JavaHeuristicScanner.NOT_FOUND) {
  			try {
  				// a special case has been detected.
  				IRegion line = fDocument.getLineInformationOfOffset(fAlign);
  				int lineOffset = line.getOffset();
  				return createIndent(lineOffset, fAlign, false);
  			} catch (BadLocationException e) {
  				return null;
  			}
  		}
  
  		if (reference == null)
  			return null;
  
  		// add additional indent
  		return createReusingIndent(reference, fIndent);
  	}
  
  	/**
  	 * Computes the length of a <code>CharacterSequence</code>, counting a tab
  	 * character as the size until the next tab stop and every other character
  	 * as one.
  	 *
  	 * @param indent
  	 *            the string to measure
  	 * @return the visual length in characters
  	 */
  	private int computeVisualLength(CharSequence indent) {
  		final int tabSize = fPrefs.prefTabSize;
  		int length = 0;
  		for (int i = 0; i < indent.length(); i++) {
  			char ch = indent.charAt(i);
  			switch (ch) {
  			case '\t':
  				if (tabSize > 0) {
  					int reminder = length % tabSize;
  					length += tabSize - reminder;
  				}
  				break;
  			case ' ':
  				length++;
  				break;
  			}
  		}
  		return length;
  	}
  
  	/**
  	 * Strips any characters off the end of <code>reference</code> that exceed
  	 * <code>indentLength</code>.
  	 *
  	 * @param reference
  	 *            the string to measure
  	 * @param indentLength
  	 *            the maximum visual indentation length
  	 * @return the stripped <code>reference</code>
  	 */
  	private StringBuffer stripExceedingChars(StringBuffer reference, int indentLength) {
  		final int tabSize = fPrefs.prefTabSize;
  		int measured = 0;
  		int chars = reference.length();
  		int i = 0;
  		for (; measured < indentLength && i < chars; i++) {
  			char ch = reference.charAt(i);
  			switch (ch) {
  			case '\t':
  				if (tabSize > 0) {
  					int reminder = measured % tabSize;
  					measured += tabSize - reminder;
  				}
  				break;
  			case ' ':
  				measured++;
  				break;
  			}
  		}
  		int deleteFrom = measured > indentLength ? i - 1 : i;
  
  		return reference.delete(deleteFrom, chars);
  	}
  
  	/**
  	 * Returns the indentation of the line at <code>offset</code> as a
  	 * <code>StringBuffer</code>. If the offset is not valid, the empty string
  	 * is returned.
  	 *
  	 * @param offset
  	 *            the offset in the document
  	 * @return the indentation (leading whitespace) of the line in which
  	 *         <code>offset</code> is located
  	 */
  	private StringBuffer getLeadingWhitespace(int offset) {
  		StringBuffer indent = new StringBuffer();
  		try {
  			IRegion line = fDocument.getLineInformationOfOffset(offset);
  			int lineOffset = line.getOffset();
  			int nonWS = fScanner.findNonWhitespaceForwardInAnyPartition(lineOffset, lineOffset + line.getLength());
  			indent.append(fDocument.get(lineOffset, nonWS - lineOffset));
  			return indent;
  		} catch (BadLocationException e) {
  			return indent;
  		}
  	}
  
  	/**
  	 * Creates an indentation string of the length indent - start, consisting of
  	 * the content in <code>fDocument</code> in the range [start, indent), with
  	 * every character replaced by a space except for tabs, which are kept as
  	 * such.
  	 * <p>
  	 * If <code>convertSpaceRunsToTabs</code> is <code>true</code>, every run of
  	 * the number of spaces that make up a tab are replaced by a tab character.
  	 * If it is not set, no conversion takes place, but tabs in the original
  	 * range are still copied verbatim.
  	 * </p>
  	 *
  	 * @param start
  	 *            the start of the document region to copy the indent from
  	 * @param indent
  	 *            the exclusive end of the document region to copy the indent
  	 *            from
  	 * @param convertSpaceRunsToTabs
  	 *            whether to convert consecutive runs of spaces to tabs
  	 * @return the indentation corresponding to the document content specified
  	 *         by <code>start</code> and <code>indent</code>
  	 */
  	private StringBuffer createIndent(int start, final int indent, final boolean convertSpaceRunsToTabs) {
  		final boolean convertTabs = fPrefs.prefUseTabs && convertSpaceRunsToTabs;
  		final int tabLen = fPrefs.prefTabSize;
  		final StringBuffer ret = new StringBuffer();
  		try {
  			int spaces = 0;
  			while (start < indent) {
  
  				char ch = fDocument.getChar(start);
  				if (ch == '\t') {
  					ret.append('\t');
  					spaces = 0;
  				} else if (convertTabs) {
  					spaces++;
  					if (spaces == tabLen) {
  						ret.append('\t');
  						spaces = 0;
  					}
  				} else {
  					ret.append(' ');
  				}
  
  				start++;
  			}
  			// remainder
  			while (spaces-- > 0)
  				ret.append(' ');
  
  		} catch (BadLocationException e) {
  		}
  
  		return ret;
  	}
  
  	/**
  	 * Creates a string with a visual length of the given
  	 * <code>indentationSize</code>.
  	 *
  	 * @param buffer
  	 *            the original indent to reuse if possible
  	 * @param additional
  	 *            the additional indentation units to add or subtract to
  	 *            reference
  	 * @return the modified <code>buffer</code> reflecting the indentation
  	 *         adapted to <code>additional</code>
  	 */
  	private StringBuffer createReusingIndent(StringBuffer buffer, int additional) {
  		int refLength = computeVisualLength(buffer);
  		int addLength = fPrefs.prefIndentationSize * additional; // may be < 0
  		int totalLength = Math.max(0, refLength + addLength);
  
  		// copy the reference indentation for the indent up to the last tab
  		// stop within the maxCopy area
  		int minLength = Math.min(totalLength, refLength);
  		int tabSize = fPrefs.prefTabSize;
  		int maxCopyLength = tabSize > 0 ? minLength - minLength % tabSize : minLength; // maximum
  																						// indent
  																						// to
  																						// copy
  		stripExceedingChars(buffer, maxCopyLength);
  
  		// add additional indent
  		int missing = totalLength - maxCopyLength;
  		final int tabs, spaces;
  		// if (JavaScriptCore.SPACE.equals(fPrefs.prefTabChar)) {
  		if (!fPrefs.prefUseTabs) {
  			tabs = 0;
  			spaces = missing;
  		}
  		// } else if (JavaScriptCore.TAB.equals(fPrefs.prefTabChar)) {
  		else {
  			tabs = tabSize > 0 ? missing / tabSize : 0;
  			spaces = tabSize > 0 ? missing % tabSize : missing;
  		}
  		/*
  		 * else if
  		 * (DefaultCodeFormatterConstants.MIXED.equals(fPrefs.prefTabChar)) {
  		 * tabs= tabSize > 0 ? missing / tabSize : 0; spaces= tabSize > 0 ?
  		 * missing % tabSize : missing; } else { Assert.isTrue(false); return
  		 * null; }
  		 */
  		for (int i = 0; i < tabs; i++)
  			buffer.append('\t');
  		for (int i = 0; i < spaces; i++)
  			buffer.append(' ');
  		return buffer;
  	}
  
  	/**
  	 * Returns the reference position regarding to indentation for
  	 * <code>offset</code>, or <code>NOT_FOUND</code>. This method calls
  	 * {@link #findReferencePosition(int, int) findReferencePosition(offset,
  	 * nextChar)} where <code>nextChar</code> is the next character after
  	 * <code>offset</code>.
  	 *
  	 * @param offset
  	 *            the offset for which the reference is computed
  	 * @return the reference statement relative to which <code>offset</code>
  	 *         should be indented, or {@link JavaHeuristicScanner#NOT_FOUND}
  	 */
  	public int findReferencePosition(int offset) {
  		return findReferencePosition(offset, peekChar(offset));
  	}
  
  	/**
  	 * Peeks the next char in the document that comes after <code>offset</code>
  	 * on the same line as <code>offset</code>.
  	 *
  	 * @param offset
  	 *            the offset into document
  	 * @return the token symbol of the next element, or TokenEOF if there is
  	 *         none
  	 */
  	private int peekChar(int offset) {
  		if (offset < fDocument.getLength()) {
  			try {
  				IRegion line = fDocument.getLineInformationOfOffset(offset);
  				int lineOffset = line.getOffset();
  				int next = fScanner.nextToken(offset, lineOffset + line.getLength());
  				return next;
  			} catch (BadLocationException e) {
  			}
  		}
  		return Symbols.TokenEOF;
  	}
  
  	/**
  	 * Returns the reference position regarding to indentation for
  	 * <code>position</code>, or <code>NOT_FOUND</code>.
  	 *
  	 * <p>
  	 * If <code>peekNextChar</code> is <code>true</code>, the next token after
  	 * <code>offset</code> is read and taken into account when computing the
  	 * indentation. Currently, if the next token is the first token on the line
  	 * (i.e. only preceded by whitespace), the following tokens are specially
  	 * handled:
  	 * <ul>
  	 * <li><code>switch</code> labels are indented relative to the switch
  	 * block</li>
  	 * <li>opening curly braces are aligned correctly with the introducing
  	 * code</li>
  	 * <li>closing curly braces are aligned properly with the introducing code
  	 * of the matching opening brace</li>
  	 * <li>closing parenthesis' are aligned with their opening peer</li>
  	 * <li>the <code>else</code> keyword is aligned with its <code>if</code>,
  	 * anything else is aligned normally (i.e. with the base of any introducing
  	 * statements).</li>
  	 * <li>if there is no token on the same line after <code>offset</code>, the
  	 * indentation is the same as for an <code>else</code> keyword</li>
  	 * </ul>
  	 *
  	 * @param offset
  	 *            the offset for which the reference is computed
  	 * @param nextToken
  	 *            the next token to assume in the document
  	 * @return the reference statement relative to which <code>offset</code>
  	 *         should be indented, or {@link JavaHeuristicScanner#NOT_FOUND}
  	 */
  	public int findReferencePosition(int offset, int nextToken) {
  		boolean danglingElse = false;
  		boolean unindent = false;
  		boolean indent = false;
  		boolean matchBrace = false;
  		boolean matchParen = false;
  		boolean matchCase = false;
  
  		// account for un-indentation characters already typed in, but after
  		// position
  		// if they are on a line by themselves, the indentation gets adjusted
  		// accordingly
  		//
  		// also account for a dangling else
  		if (offset < fDocument.getLength()) {
  			try {
  				IRegion line = fDocument.getLineInformationOfOffset(offset);
  				int lineOffset = line.getOffset();
  				int prevPos = Math.max(offset - 1, 0);
  				boolean isFirstTokenOnLine = fDocument.get(lineOffset, prevPos + 1 - lineOffset).trim().length() == 0;
  				int prevToken = fScanner.previousToken(prevPos, JavaHeuristicScanner.UNBOUND);
  				boolean bracelessBlockStart = fScanner.isBracelessBlockStart(prevPos, JavaHeuristicScanner.UNBOUND);
  
  				switch (nextToken) {
  				case Symbols.TokenELSE:
  					danglingElse = true;
  					break;
  				case Symbols.TokenCASE:
  				case Symbols.TokenDEFAULT:
  					if (isFirstTokenOnLine)
  						matchCase = true;
  					break;
  				case Symbols.TokenLBRACE: // for opening-brace-on-new-line style
  					if (bracelessBlockStart && !fPrefs.prefIndentBracesForBlocks)
  						unindent = true;
  					else if ((prevToken == Symbols.TokenCOLON || prevToken == Symbols.TokenEQUAL
  							|| prevToken == Symbols.TokenRBRACKET) && !fPrefs.prefIndentBracesForArrays)
  						unindent = true;
  					else if (!bracelessBlockStart && fPrefs.prefIndentBracesForMethods)
  						indent = true;
  					break;
  				case Symbols.TokenRBRACE: // closing braces get unindented
  					if (isFirstTokenOnLine)
  						matchBrace = true;
  					break;
  				case Symbols.TokenRPAREN:
  					if (isFirstTokenOnLine)
  						matchParen = true;
  					break;
  				}
  			} catch (BadLocationException e) {
  			}
  		} else {
  			// don't assume an else could come if we are at the end of file
  			danglingElse = false;
  		}
  
  		int ref = findReferencePosition(offset, danglingElse, matchBrace, matchParen, matchCase);
  		if (unindent)
  			fIndent--;
  		if (indent)
  			fIndent++;
  		return ref;
  	}
  
  	/**
  	 * Returns the reference position regarding to indentation for
  	 * <code>position</code>, or <code>NOT_FOUND</code>.<code>fIndent</code>
  	 * will contain the relative indentation (in indentation units, not
  	 * characters) after the call. If there is a special alignment (e.g. for a
  	 * method declaration where parameters should be aligned),
  	 * <code>fAlign</code> will contain the absolute position of the alignment
  	 * reference in <code>fDocument</code>, otherwise <code>fAlign</code> is set
  	 * to <code>JavaHeuristicScanner.NOT_FOUND</code>.
  	 *
  	 * @param offset
  	 *            the offset for which the reference is computed
  	 * @param danglingElse
  	 *            whether a dangling else should be assumed at
  	 *            <code>position</code>
  	 * @param matchBrace
  	 *            whether the position of the matching brace should be returned
  	 *            instead of doing code analysis
  	 * @param matchParen
  	 *            whether the position of the matching parenthesis should be
  	 *            returned instead of doing code analysis
  	 * @param matchCase
  	 *            whether the position of a switch statement reference should be
  	 *            returned (either an earlier case statement or the switch block
  	 *            brace)
  	 * @return the reference statement relative to which <code>position</code>
  	 *         should be indented, or {@link JavaHeuristicScanner#NOT_FOUND}
  	 */
  	public int findReferencePosition(int offset, boolean danglingElse, boolean matchBrace, boolean matchParen,
  			boolean matchCase) {
  		fIndent = 0; // the indentation modification
  		fAlign = JavaHeuristicScanner.NOT_FOUND;
  		fPosition = offset;
  
  		// forward cases
  		// an unindentation happens sometimes if the next token is special,
  		// namely on braces, parens and case labels
  		// align braces, but handle the case where we align with the method
  		// declaration start instead of
  		// the opening brace.
  		if (matchBrace) {
  			if (skipScope(Symbols.TokenLBRACE, Symbols.TokenRBRACE)) {
  				try {
  					// align with the opening brace that is on a line by its own
  					int lineOffset = fDocument.getLineOffset(fLine);
  					if (lineOffset <= fPosition
  							&& fDocument.get(lineOffset, fPosition - lineOffset).trim().length() == 0)
  						return fPosition;
  				} catch (BadLocationException e) {
  					// concurrent modification - walk default path
  				}
  				// if the opening brace is not on the start of the line, skip to
  				// the start
  				int pos = skipToStatementStart(true, true);
  				fIndent = 0; // indent is aligned with reference position
  				return pos;
  			} else {
  				// if we can't find the matching brace, the heuristic is to
  				// unindent
  				// by one against the normal position
  				int pos = findReferencePosition(offset, danglingElse, false, matchParen, matchCase);
  				fIndent--;
  				return pos;
  			}
  		}
  
  		// align parenthesis'
  		if (matchParen) {
  			if (skipScope(Symbols.TokenLPAREN, Symbols.TokenRPAREN))
  				return fPosition;
  			else {
  				// if we can't find the matching paren, the heuristic is to
  				// unindent
  				// by one against the normal position
  				int pos = findReferencePosition(offset, danglingElse, matchBrace, false, matchCase);
  				fIndent--;
  				return pos;
  			}
  		}
  
  		// the only reliable way to get case labels aligned (due to many
  		// different styles of using braces in a block)
  		// is to go for another case statement, or the scope opening brace
  		if (matchCase) {
  			return matchCaseAlignment();
  		}
  
  		nextToken();
  		switch (fToken) {
  		case Symbols.TokenGREATERTHAN:
  		case Symbols.TokenRBRACKET:
  		case Symbols.TokenRBRACE:
  			// skip the block and fall through
  			// if we can't complete the scope, reset the scan position
  			int pos = fPosition;
  			if (!skipScope())
  				fPosition = pos;
  			//$FALL-THROUGH$
  		case Symbols.TokenSEMICOLON:
  			// this is the 90% case: after a statement block
  			// the end of the previous statement / block previous.end
  			// search to the end of the statement / block before the previous;
  			// the token just after that is previous.start
  			return skipToStatementStart(danglingElse, false);
  
  		// scope introduction: special treat who special is
  		case Symbols.TokenLPAREN:
  		case Symbols.TokenLBRACE:
  		case Symbols.TokenLBRACKET:
  			return handleScopeIntroduction(offset + 1);
  
  		case Symbols.TokenEOF:
  			// trap when hitting start of document
  			return JavaHeuristicScanner.NOT_FOUND;
  
  		case Symbols.TokenEQUAL:
  			// indent assignments
  			fIndent = fPrefs.prefAssignmentIndent;
  			return fPosition;
  
  		case Symbols.TokenCOLON:
  			// TODO handle ternary deep indentation
  			fIndent = fPrefs.prefCaseBlockIndent;
  			return fPosition;
  
  		case Symbols.TokenQUESTIONMARK:
  			if (fPrefs.prefTernaryDeepAlign) {
  				setFirstElementAlignment(fPosition, offset + 1);
  				return fPosition;
  			} else {
  				fIndent = fPrefs.prefTernaryIndent;
  				return fPosition;
  			}
  
  			// indentation for blockless introducers:
  		case Symbols.TokenDO:
  		case Symbols.TokenWHILE:
  		case Symbols.TokenELSE:
  			fIndent = fPrefs.prefSimpleIndent;
  			return fPosition;
  
  		case Symbols.TokenTRY:
  			return skipToStatementStart(danglingElse, false);
  		case Symbols.TokenRPAREN:
  			int line = fLine;
  			if (skipScope(Symbols.TokenLPAREN, Symbols.TokenRPAREN)) {
  				int scope = fPosition;
  				nextToken();
  				if (fToken == Symbols.TokenIF || fToken == Symbols.TokenWHILE || fToken == Symbols.TokenFOR) {
  					fIndent = fPrefs.prefSimpleIndent;
  					return fPosition;
  				}
  				fPosition = scope;
  				if (looksLikeMethodDecl()) {
  					return skipToStatementStart(danglingElse, false);
  				}
  				if (fToken == Symbols.TokenCATCH) {
  					return skipToStatementStart(danglingElse, false);
  				}
  				fPosition = scope;
  				if (looksLikeAnonymousTypeDecl()) {
  					return skipToStatementStart(danglingElse, false);
  				}
  			}
  			// restore
  			fPosition = offset;
  			fLine = line;
  			// else: fall through to default
  			// $FALL-THROUGH$
  		case Symbols.TokenCOMMA:
  			// inside a list of some type
  			// easy if there is already a list item before with its own
  			// indentation - we just align
  			// if not: take the start of the list ( LPAREN, LBRACE, LBRACKET )
  			// and either align or
  			// indent by list-indent
  		default:
  			// inside whatever we don't know about: similar to the list case:
  			// if we are inside a continued expression, then either align with a
  			// previous line that has indentation
  			// or indent from the expression start line (either a scope
  			// introducer or the start of the expr).
  			return skipToPreviousListItemOrListStart();
  
  		}
  	}
  
  	/**
  	 * Skips to the start of a statement that ends at the current position.
  	 *
  	 * @param danglingElse
  	 *            whether to indent aligned with the last <code>if</code>
  	 * @param isInBlock
  	 *            whether the current position is inside a block, which limits
  	 *            the search scope to the next scope introducer
  	 * @return the reference offset of the start of the statement
  	 */
  	private int skipToStatementStart(boolean danglingElse, boolean isInBlock) {
  		final int NOTHING = 0;
  		final int READ_PARENS = 1;
  		final int READ_IDENT = 2;
  		int mayBeMethodBody = NOTHING;
  		boolean isTypeBody = false;
  		while (true) {
  			nextToken();
  
  			if (isInBlock) {
  				switch (fToken) {
  				// exit on all block introducers
  				case Symbols.TokenIF:
  				case Symbols.TokenELSE:
  				case Symbols.TokenCATCH:
  				case Symbols.TokenDO:
  				case Symbols.TokenWHILE:
  				case Symbols.TokenFINALLY:
  				case Symbols.TokenFOR:
  				case Symbols.TokenTRY:
  				case Symbols.TokenCOLON:
  					return fPosition;
  
  				case Symbols.TokenSTATIC:
  					mayBeMethodBody = READ_IDENT; // treat static blocks like
  													// methods
  					break;
  
  				case Symbols.TokenSYNCHRONIZED:
  					// if inside a method declaration, use body indentation
  					// else use block indentation.
  					if (mayBeMethodBody != READ_IDENT)
  						return fPosition;
  					break;
  
  				case Symbols.TokenCLASS:
  				case Symbols.TokenINTERFACE:
  				case Symbols.TokenENUM:
  					isTypeBody = true;
  					break;
  
  				case Symbols.TokenSWITCH:
  					fIndent = fPrefs.prefCaseIndent;
  					return fPosition;
  				}
  			}
  
  			switch (fToken) {
  			// scope introduction through: LPAREN, LBRACE, LBRACKET
  			// search stop on SEMICOLON, RBRACE, COLON, EOF
  			// -> the next token is the start of the statement (i.e. previousPos
  			// when backward scanning)
  			case Symbols.TokenLPAREN:
  			case Symbols.TokenLBRACE:
  			case Symbols.TokenLBRACKET:
  			case Symbols.TokenSEMICOLON:
  			case Symbols.TokenEOF:
  				if (isInBlock)
  					fIndent = getBlockIndent(mayBeMethodBody == READ_IDENT, isTypeBody);
  				// else: fIndent set by previous calls
  				return fPreviousPos;
  
  			case Symbols.TokenCOLON:
  				int pos = fPreviousPos;
  				if (!isConditional())
  					return pos;
  				break;
  
  			case Symbols.TokenRBRACE:
  				// RBRACE is a little tricky: it can be the end of an array
  				// definition, but
  				// usually it is the end of a previous block
  				pos = fPreviousPos; // store state
  				if (skipScope() && looksLikeArrayInitializerIntro()) {
  					continue; // it's an array
  				} else {
  					if (isInBlock)
  						fIndent = getBlockIndent(mayBeMethodBody == READ_IDENT, isTypeBody);
  					return pos; // it's not - do as with all the above
  				}
  
  				// scopes: skip them
  			case Symbols.TokenRPAREN:
  				if (isInBlock)
  					mayBeMethodBody = READ_PARENS;
  				//$FALL-THROUGH$
  			case Symbols.TokenRBRACKET:
  			case Symbols.TokenGREATERTHAN:
  				pos = fPreviousPos;
  				if (skipScope())
  					break;
  				else
  					return pos;
  
  				// IF / ELSE: align the position after the conditional block
  				// with the if
  				// so we are ready for an else, except if danglingElse is false
  				// in order for this to work, we must skip an else to its if
  			case Symbols.TokenIF:
  				if (danglingElse)
  					return fPosition;
  				else
  					break;
  			case Symbols.TokenELSE:
  				// skip behind the next if, as we have that one covered
  				pos = fPosition;
  				if (skipNextIF())
  					break;
  				else
  					return pos;
  
  			case Symbols.TokenDO:
  				// align the WHILE position with its do
  				return fPosition;
  
  			case Symbols.TokenWHILE:
  				// this one is tricky: while can be the start of a while loop
  				// or the end of a do - while
  				pos = fPosition;
  				if (hasMatchingDo()) {
  					// continue searching from the DO on
  					break;
  				} else {
  					// continue searching from the WHILE on
  					fPosition = pos;
  					break;
  				}
  			case Symbols.TokenIDENT:
  				if (mayBeMethodBody == READ_PARENS)
  					mayBeMethodBody = READ_IDENT;
  				break;
  
  			default:
  				// keep searching
  
  			}
  
  		}
  	}
  
  	private int getBlockIndent(boolean isMethodBody, boolean isTypeBody) {
  		if (isTypeBody)
  			return fPrefs.prefTypeIndent + (fPrefs.prefIndentBracesForTypes ? 1 : 0);
  		else if (isMethodBody)
  			return fPrefs.prefMethodBodyIndent + (fPrefs.prefIndentBracesForMethods ? 1 : 0);
  		else
  			return fIndent;
  	}
  
  	/**
  	 * Returns true if the colon at the current position is part of a
  	 * conditional (ternary) expression, false otherwise.
  	 *
  	 * @return true if the colon at the current position is part of a
  	 *         conditional
  	 */
  	private boolean isConditional() {
  		while (true) {
  			nextToken();
  			switch (fToken) {
  
  			// search for case labels, which consist of (possibly qualified)
  			// identifiers or numbers
  			case Symbols.TokenIDENT:
  			case Symbols.TokenOTHER: // dots for qualified constants
  				continue;
  			case Symbols.TokenCASE:
  				return false;
  
  			default:
  				return true;
  			}
  		}
  	}
  
  	/**
  	 * Returns as a reference any previous <code>switch</code> labels
  	 * (<code>case</code> or <code>default</code>) or the offset of the brace
  	 * that scopes the switch statement. Sets <code>fIndent</code> to
  	 * <code>prefCaseIndent</code> upon a match.
  	 *
  	 * @return the reference offset for a <code>switch</code> label
  	 */
  	private int matchCaseAlignment() {
  		while (true) {
  			nextToken();
  			switch (fToken) {
  			// invalid cases: another case label or an LBRACE must come before a
  			// case
  			// -> bail out with the current position
  			case Symbols.TokenLPAREN:
  			case Symbols.TokenLBRACKET:
  			case Symbols.TokenEOF:
  				return fPosition;
  			case Symbols.TokenLBRACE:
  				// opening brace of switch statement
  				fIndent = fPrefs.prefCaseIndent;
  				return fPosition;
  			case Symbols.TokenCASE:
  			case Symbols.TokenDEFAULT:
  				// align with previous label
  				fIndent = 0;
  				return fPosition;
  
  			// scopes: skip them
  			case Symbols.TokenRPAREN:
  			case Symbols.TokenRBRACKET:
  			case Symbols.TokenRBRACE:
  			case Symbols.TokenGREATERTHAN:
  				skipScope();
  				break;
  
  			default:
  				// keep searching
  				continue;
  
  			}
  		}
  	}
  
  	/**
  	 * Returns the reference position for a list element. The algorithm tries to
  	 * match any previous indentation on the same list. If there is none, the
  	 * reference position returned is determined depending on the type of list:
  	 * The indentation will either match the list scope introducer (e.g. for
  	 * method declarations), so called deep indents, or simply increase the
  	 * indentation by a number of standard indents. See also
  	 * {@link #handleScopeIntroduction(int)}.
  	 *
  	 * @return the reference position for a list item: either a previous list
  	 *         item that has its own indentation, or the list introduction
  	 *         start.
  	 */
  	private int skipToPreviousListItemOrListStart() {
  		int startLine = fLine;
  		int startPosition = fPosition;
  		while (true) {
  			nextToken();
  
  			// if any line item comes with its own indentation, adapt to it
  			if (fLine < startLine) {
  				try {
  					int lineOffset = fDocument.getLineOffset(startLine);
  					int bound = Math.min(fDocument.getLength(), startPosition + 1);
  					fAlign = fScanner.findNonWhitespaceForwardInAnyPartition(lineOffset, bound);
  				} catch (BadLocationException e) {
  					// ignore and return just the position
  				}
  				return startPosition;
  			}
  
  			switch (fToken) {
  			// scopes: skip them
  			case Symbols.TokenRPAREN:
  			case Symbols.TokenRBRACKET:
  			case Symbols.TokenRBRACE:
  			case Symbols.TokenGREATERTHAN:
  				skipScope();
  				break;
  
  			// scope introduction: special treat who special is
  			case Symbols.TokenLPAREN:
  			case Symbols.TokenLBRACE:
  			case Symbols.TokenLBRACKET:
  				return handleScopeIntroduction(startPosition + 1);
  
  			case Symbols.TokenSEMICOLON:
  				return fPosition;
  			case Symbols.TokenQUESTIONMARK:
  				if (fPrefs.prefTernaryDeepAlign) {
  					setFirstElementAlignment(fPosition - 1, fPosition + 1);
  					return fPosition;
  				} else {
  					fIndent = fPrefs.prefTernaryIndent;
  					return fPosition;
  				}
  			case Symbols.TokenEOF:
  				return 0;
  
  			}
  		}
  	}
  
  	/**
  	 * Skips a scope and positions the cursor (<code>fPosition</code>) on the
  	 * token that opens the scope. Returns <code>true</code> if a matching peer
  	 * could be found, <code>false</code> otherwise. The current token when
  	 * calling must be one out of <code>Symbols.TokenRPAREN</code>,
  	 * <code>Symbols.TokenRBRACE</code>, and <code>Symbols.TokenRBRACKET</code>.
  	 *
  	 * @return <code>true</code> if a matching peer was found,
  	 *         <code>false</code> otherwise
  	 */
  	private boolean skipScope() {
  		switch (fToken) {
  		case Symbols.TokenRPAREN:
  			return skipScope(Symbols.TokenLPAREN, Symbols.TokenRPAREN);
  		case Symbols.TokenRBRACKET:
  			return skipScope(Symbols.TokenLBRACKET, Symbols.TokenRBRACKET);
  		case Symbols.TokenRBRACE:
  			return skipScope(Symbols.TokenLBRACE, Symbols.TokenRBRACE);
  		case Symbols.TokenGREATERTHAN:
  			if (!fPrefs.prefHasGenerics)
  				return false;
  			int storedPosition = fPosition;
  			int storedToken = fToken;
  			nextToken();
  			switch (fToken) {
  			case Symbols.TokenIDENT:
  				if (!JavaHeuristicScanner.isGenericStarter(getTokenContent()))
  					break;
  				//$FALL-THROUGH$
  			case Symbols.TokenQUESTIONMARK:
  			case Symbols.TokenGREATERTHAN:
  				if (skipScope(Symbols.TokenLESSTHAN, Symbols.TokenGREATERTHAN))
  					return true;
  			}
  			// <> are harder to detect - restore the position if we fail
  			fPosition = storedPosition;
  			fToken = storedToken;
  			return false;
  
  		default:
  			Assert.isTrue(false);
  			return false;
  		}
  	}
  
  	/**
  	 * Returns the contents of the current token.
  	 *
  	 * @return the contents of the current token
  	 * 
  	 */
  	private CharSequence getTokenContent() {
  		return new DocumentCharacterIterator(fDocument, fPosition, fPreviousPos);
  	}
  
  	/**
  	 * Handles the introduction of a new scope. The current token must be one
  	 * out of <code>Symbols.TokenLPAREN</code>,
  	 * <code>Symbols.TokenLBRACE</code>, and <code>Symbols.TokenLBRACKET</code>.
  	 * Returns as the reference position either the token introducing the scope
  	 * or - if available - the first java token after that.
  	 *
  	 * <p>
  	 * Depending on the type of scope introduction, the indentation will align
  	 * (deep indenting) with the reference position (<code>fAlign</code> will be
  	 * set to the reference position) or <code>fIndent</code> will be set to the
  	 * number of indentation units.
  	 * </p>
  	 *
  	 * @param bound
  	 *            the bound for the search for the first token after the scope
  	 *            introduction.
  	 * @return the indent
  	 */
  	private int handleScopeIntroduction(int bound) {
  		switch (fToken) {
  		// scope introduction: special treat who special is
  		case Symbols.TokenLPAREN:
  			int pos = fPosition; // store
  
  			// special: method declaration deep indentation
  			if (looksLikeMethodDecl()) {
  				if (fPrefs.prefMethodDeclDeepIndent)
  					return setFirstElementAlignment(pos, bound);
  				else {
  					fIndent = fPrefs.prefMethodDeclIndent;
  					return pos;
  				}
  			} else {
  				fPosition = pos;
  				if (looksLikeMethodCall()) {
  					if (fPrefs.prefMethodCallDeepIndent)
  						return setFirstElementAlignment(pos, bound);
  					else {
  						fIndent = fPrefs.prefMethodCallIndent;
  						return pos;
  					}
  				} else if (fPrefs.prefParenthesisDeepIndent)
  					return setFirstElementAlignment(pos, bound);
  			}
  
  			// normal: return the parenthesis as reference
  			fIndent = fPrefs.prefParenthesisIndent;
  			return pos;
  
  		case Symbols.TokenLBRACE:
  			pos = fPosition; // store
  
  			// special: array initializer
  			if (looksLikeArrayInitializerIntro())
  				if (fPrefs.prefArrayDeepIndent)
  					return setFirstElementAlignment(pos, bound);
  				else
  					fIndent = fPrefs.prefArrayIndent;
  			else
  				fIndent = fPrefs.prefBlockIndent;
  
  			// normal: skip to the statement start before the scope introducer
  			// opening braces are often on differently ending indents than e.g.
  			// a method definition
  			if (looksLikeArrayInitializerIntro() && !fPrefs.prefIndentBracesForArrays
  					|| !fPrefs.prefIndentBracesForBlocks) {
  				fPosition = pos; // restore
  				return skipToStatementStart(true, true); // set to true to match
  															// the first if
  			} else {
  				return pos;
  			}
  
  		case Symbols.TokenLBRACKET:
  			pos = fPosition; // store
  
  			// special: method declaration deep indentation
  			if (fPrefs.prefArrayDimensionsDeepIndent) {
  				return setFirstElementAlignment(pos, bound);
  			}
  
  			// normal: return the bracket as reference
  			fIndent = fPrefs.prefBracketIndent;
  			return pos; // restore
  
  		default:
  			Assert.isTrue(false);
  			return -1; // dummy
  		}
  	}
  
  	/**
  	 * Sets the deep indent offset (<code>fAlign</code>) to either the offset
  	 * right after <code>scopeIntroducerOffset</code> or - if available - the
  	 * first Java token after <code>scopeIntroducerOffset</code>, but before
  	 * <code>bound</code>.
  	 *
  	 * @param scopeIntroducerOffset
  	 *            the offset of the scope introducer
  	 * @param bound
  	 *            the bound for the search for another element
  	 * @return the reference position
  	 */
  	private int setFirstElementAlignment(int scopeIntroducerOffset, int bound) {
  		int firstPossible = scopeIntroducerOffset + 1; // align with the first
  														// position after the
  														// scope intro
  		fAlign = fScanner.findNonWhitespaceForwardInAnyPartition(firstPossible, bound);
  		if (fAlign == JavaHeuristicScanner.NOT_FOUND)
  			fAlign = firstPossible;
  		return fAlign;
  	}
  
  	/**
  	 * Returns <code>true</code> if the next token received after calling
  	 * <code>nextToken</code> is either an equal sign or an array designator
  	 * ('[]').
  	 *
  	 * @return <code>true</code> if the next elements look like the start of an
  	 *         array definition
  	 */
  	private boolean looksLikeArrayInitializerIntro() {
  		nextToken();
  		if (fToken == Symbols.TokenEQUAL || skipBrackets()) {
  			return true;
  		}
  		return false;
  	}
  
  	/**
  	 * Skips over the next <code>if</code> keyword. The current token when
  	 * calling this method must be an <code>else</code> keyword. Returns
  	 * <code>true</code> if a matching <code>if</code> could be found,
  	 * <code>false</code> otherwise. The cursor (<code>fPosition</code>) is set
  	 * to the offset of the <code>if</code> token.
  	 *
  	 * @return <code>true</code> if a matching <code>if</code> token was found,
  	 *         <code>false</code> otherwise
  	 */
  	private boolean skipNextIF() {
  		Assert.isTrue(fToken == Symbols.TokenELSE);
  
  		while (true) {
  			nextToken();
  			switch (fToken) {
  			// scopes: skip them
  			case Symbols.TokenRPAREN:
  			case Symbols.TokenRBRACKET:
  			case Symbols.TokenRBRACE:
  			case Symbols.TokenGREATERTHAN:
  				skipScope();
  				break;
  
  			case Symbols.TokenIF:
  				// found it, return
  				return true;
  			case Symbols.TokenELSE:
  				// recursively skip else-if blocks
  				skipNextIF();
  				break;
  
  			// shortcut scope starts
  			case Symbols.TokenLPAREN:
  			case Symbols.TokenLBRACE:
  			case Symbols.TokenLBRACKET:
  			case Symbols.TokenEOF:
  				return false;
  			}
  		}
  	}
  
  	/**
  	 * while(condition); is ambiguous when parsed backwardly, as it is a valid
  	 * statement by its own, so we have to check whether there is a matching do.
  	 * A <code>do</code> can either be separated from the while by a block, or
  	 * by a single statement, which limits our search distance.
  	 *
  	 * @return <code>true</code> if the <code>while</code> currently in
  	 *         <code>fToken</code> has a matching <code>do</code>.
  	 */
  	private boolean hasMatchingDo() {
  		Assert.isTrue(fToken == Symbols.TokenWHILE);
  		nextToken();
  		switch (fToken) {
  		case Symbols.TokenRBRACE:
  			skipScope(); // and fall thru
  			//$FALL-THROUGH$
  		case Symbols.TokenSEMICOLON:
  			skipToStatementStart(false, false);
  			return fToken == Symbols.TokenDO;
  		}
  		return false;
  	}
  
  	/**
  	 * Skips brackets if the current token is a RBRACKET. There can be nothing
  	 * but whitespace in between, this is only to be used for <code>[]</code>
  	 * elements.
  	 *
  	 * @return <code>true</code> if a <code>[]</code> could be scanned, the
  	 *         current token is left at the LBRACKET.
  	 */
  	private boolean skipBrackets() {
  		if (fToken == Symbols.TokenRBRACKET) {
  			nextToken();
  			if (fToken == Symbols.TokenLBRACKET) {
  				return true;
  			}
  		}
  		return false;
  	}
  
  	/**
  	 * Reads the next token in backward direction from the heuristic scanner and
  	 * sets the fields <code>fToken, fPreviousPosition</code> and
  	 * <code>fPosition</code> accordingly.
  	 */
  	private void nextToken() {
  		nextToken(fPosition);
  	}
  
  	/**
  	 * Reads the next token in backward direction of <code>start</code> from the
  	 * heuristic scanner and sets the fields
  	 * <code>fToken, fPreviousPosition</code> and <code>fPosition</code>
  	 * accordingly.
  	 *
  	 * @param start
  	 *            the start offset from which to scan backwards
  	 */
  	private void nextToken(int start) {
  		fToken = fScanner.previousToken(start - 1, JavaHeuristicScanner.UNBOUND);
  		fPreviousPos = start;
  		fPosition = fScanner.getPosition() + 1;
  		try {
  			fLine = fDocument.getLineOfOffset(fPosition);
  		} catch (BadLocationException e) {
  			fLine = -1;
  		}
  	}
  
  	/**
  	 * Returns <code>true</code> if the current tokens look like a method
  	 * declaration header (i.e. only the return type and method name). The
  	 * heuristic calls <code>nextToken</code> and expects an identifier (method
  	 * name) and a type declaration (an identifier with optional brackets) which
  	 * also covers the visibility modifier of constructors; it does not
  	 * recognize package visible constructors.
  	 *
  	 * @return <code>true</code> if the current position looks like a method
  	 *         declaration header.
  	 */
  	private boolean looksLikeMethodDecl() {
  		/*
  		 * TODO This heuristic does not recognize package private constructors
  		 * since those do have neither type nor visibility keywords. One option
  		 * would be to go over the parameter list, but that might be empty as
  		 * well, or not typed in yet - hard to do without an AST...
  		 */
  
  		nextToken();
  		if (fToken == Symbols.TokenIDENT) { // method name
  			do
  				nextToken();
  			while (skipBrackets()); // optional brackets for array valued return
  									// types
  
  			return fToken == Symbols.TokenIDENT; // return type name
  
  		}
  		return false;
  	}
  
  	/**
  	 * Returns <code>true</code> if the current tokens look like an anonymous
  	 * type declaration header (i.e. a type name (potentially qualified) and a
  	 * new keyword). The heuristic calls <code>nextToken</code> and expects a
  	 * possibly qualified identifier (type name) and a new keyword
  	 * 
  	 * @return <code>true</code> if the current position looks like a anonymous
  	 *         type declaration header.
  	 */
  	private boolean looksLikeAnonymousTypeDecl() {
  
  		nextToken();
  		if (fToken == Symbols.TokenIDENT) { // type name
  			nextToken();
  			while (fToken == Symbols.TokenOTHER) { // dot of qualification
  				nextToken();
  				if (fToken != Symbols.TokenIDENT) // qualificating name
  					return false;
  				nextToken();
  			}
  			return fToken == Symbols.TokenNEW;
  		}
  		return false;
  	}
  
  	/**
  	 * Returns <code>true</code> if the current tokens look like a method call
  	 * header (i.e. an identifier as opposed to a keyword taking parenthesized
  	 * parameters such as <code>if</code>).
  	 * <p>
  	 * The heuristic calls <code>nextToken</code> and expects an identifier
  	 * (method name).
  	 *
  	 * @return <code>true</code> if the current position looks like a method
  	 *         call header.
  	 */
  	private boolean looksLikeMethodCall() {
  		// TODO [5.0] add awareness for constructor calls with generic types:
  		// new ArrayList<String>()
  		nextToken();
  		return fToken == Symbols.TokenIDENT; // method name
  	}
  
  	/**
  	 * Scans tokens for the matching opening peer. The internal cursor
  	 * (<code>fPosition</code>) is set to the offset of the opening peer if
  	 * found.
  	 *
  	 * @param openToken
  	 *            the opening peer token
  	 * @param closeToken
  	 *            the closing peer token
  	 * @return <code>true</code> if a matching token was found,
  	 *         <code>false</code> otherwise
  	 */
  	private boolean skipScope(int openToken, int closeToken) {
  
  		int depth = 1;
  
  		while (true) {
  			nextToken();
  
  			if (fToken == closeToken) {
  				depth++;
  			} else if (fToken == openToken) {
  				depth--;
  				if (depth == 0)
  					return true;
  			} else if (fToken == Symbols.TokenEOF) {
  				return false;
  			}
  		}
  	}
  }
  