cdt/cdt_6_0_x/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CIndenter.java
changeset 37 c2bce6dd59e7
child 138 e0657161c0cc
equal deleted inserted replaced
35:a36bd1ef0b40 37:c2bce6dd59e7
       
     1 /*******************************************************************************
       
     2  * Copyright (c) 2000, 2009 IBM Corporation and others.
       
     3  * All rights reserved. This program and the accompanying materials
       
     4  * are made available under the terms of the Eclipse Public License v1.0
       
     5  * which accompanies this distribution, and is available at
       
     6  * http://www.eclipse.org/legal/epl-v10.html
       
     7  *
       
     8  * Contributors:
       
     9  *     IBM Corporation - initial API and implementation
       
    10  *     Sergey Prigogin (Google)
       
    11  *     Anton Leherbauer (Wind River Systems)
       
    12  *******************************************************************************/
       
    13 package org.eclipse.cdt.internal.ui.text;
       
    14 
       
    15 import org.eclipse.core.runtime.Assert;
       
    16 import org.eclipse.core.runtime.Platform;
       
    17 import org.eclipse.core.runtime.preferences.IPreferencesService;
       
    18 import org.eclipse.jface.text.BadLocationException;
       
    19 import org.eclipse.jface.text.IDocument;
       
    20 import org.eclipse.jface.text.IRegion;
       
    21 
       
    22 import org.eclipse.cdt.core.CCorePlugin;
       
    23 import org.eclipse.cdt.core.formatter.DefaultCodeFormatterConstants;
       
    24 import org.eclipse.cdt.core.model.ICProject;
       
    25 
       
    26 import org.eclipse.cdt.internal.corext.util.CodeFormatterUtil;
       
    27 
       
    28 /**
       
    29  * Uses the {@link org.eclipse.cdt.internal.ui.text.CHeuristicScanner} to
       
    30  * get the indentation level for a certain position in a document.
       
    31  *
       
    32  * <p>
       
    33  * An instance holds some internal position in the document and is therefore
       
    34  * not thread-safe.
       
    35  * </p>
       
    36  */
       
    37 public final class CIndenter {
       
    38 	
       
    39 	/**
       
    40 	 * The CDT Core preferences.
       
    41 	 */
       
    42 	private final class CorePrefs {
       
    43 		final boolean prefUseTabs;
       
    44 		final int prefTabSize;
       
    45 		final int prefIndentationSize;
       
    46 		final boolean prefArrayDimensionsDeepIndent;
       
    47 		final int prefArrayIndent;
       
    48 		final boolean prefArrayDeepIndent;
       
    49 		final boolean prefTernaryDeepAlign;
       
    50 		final int prefTernaryIndent;
       
    51 		final int prefCaseIndent;
       
    52 		final int prefCaseBlockIndent;
       
    53 		final int prefAssignmentIndent;
       
    54 		final int prefSimpleIndent;
       
    55 		final int prefBracketIndent;
       
    56 		final boolean prefMethodDeclDeepIndent;
       
    57 		final boolean prefMethodDeclFirstParameterDeepIndent;
       
    58 		final int prefMethodDeclIndent;
       
    59 		final boolean prefMethodCallDeepIndent;
       
    60 		final boolean prefMethodCallFirstParameterDeepIndent;
       
    61 		final int prefMethodCallIndent;
       
    62 		final boolean prefParenthesisDeepIndent;
       
    63 		final int prefParenthesisIndent;
       
    64 		final int prefBlockIndent;
       
    65 		final int prefMethodBodyIndent;
       
    66 		final int prefTypeIndent;
       
    67 		final int prefAccessSpecifierIndent;
       
    68 		final int prefAccessSpecifierExtraSpaces;
       
    69 		final int prefNamespaceBodyIndent;
       
    70 		final boolean prefIndentBracesForBlocks;
       
    71 		final boolean prefIndentBracesForArrays;
       
    72 		final boolean prefIndentBracesForMethods;
       
    73 		final boolean prefIndentBracesForTypes;
       
    74 		final int prefContinuationIndent;
       
    75 		final boolean prefHasTemplates;
       
    76 		final String prefTabChar;
       
    77 		
       
    78 		private final ICProject fProject;
       
    79 
       
    80 		/**
       
    81 		 * Returns the possibly project-specific core preference defined under <code>key</code>.
       
    82 		 *
       
    83 		 * @param key the key of the preference
       
    84 		 * @return the value of the preference
       
    85 		 */
       
    86 		private String getCoreFormatterOption(String key) {
       
    87 			if (fProject == null)
       
    88 				return CCorePlugin.getOption(key);
       
    89 			return fProject.getOption(key, true);
       
    90 		}
       
    91 		
       
    92 		CorePrefs(ICProject project) {
       
    93 			fProject= project;
       
    94 			prefUseTabs= prefUseTabs();
       
    95 			prefTabSize= prefTabSize();
       
    96 			prefIndentationSize= prefIndentationSize();
       
    97 			prefArrayDimensionsDeepIndent= prefArrayDimensionsDeepIndent();
       
    98 			prefContinuationIndent= prefContinuationIndent();
       
    99 			prefBlockIndent= prefBlockIndent();
       
   100 			prefArrayIndent= prefArrayIndent();
       
   101 			prefArrayDeepIndent= prefArrayDeepIndent();
       
   102 			prefTernaryDeepAlign= prefTernaryDeepAlign();
       
   103 			prefTernaryIndent= prefTernaryIndent();
       
   104 			prefCaseIndent= prefCaseIndent();
       
   105 			prefCaseBlockIndent= prefCaseBlockIndent();
       
   106 			prefAssignmentIndent= prefAssignmentIndent();
       
   107 			prefIndentBracesForBlocks= prefIndentBracesForBlocks();
       
   108 			prefSimpleIndent= prefSimpleIndent();
       
   109 			prefBracketIndent= prefBracketIndent();
       
   110 			prefMethodDeclDeepIndent= prefMethodDeclDeepIndent();
       
   111 			prefMethodDeclFirstParameterDeepIndent= prefMethodDeclFirstParameterDeepIndent();
       
   112 			prefMethodDeclIndent= prefMethodDeclIndent();
       
   113 			prefMethodCallDeepIndent= prefMethodCallDeepIndent();
       
   114 			prefMethodCallFirstParameterDeepIndent= prefMethodCallFirstParameterDeepIndent();
       
   115 			prefMethodCallIndent= prefMethodCallIndent();
       
   116 			prefParenthesisDeepIndent= prefParenthesisDeepIndent();
       
   117 			prefParenthesisIndent= prefParenthesisIndent();
       
   118 			prefMethodBodyIndent= prefMethodBodyIndent();
       
   119 			prefTypeIndent= prefTypeIndent();
       
   120 			prefAccessSpecifierIndent= prefAccessSpecifierIndent();
       
   121 			prefAccessSpecifierExtraSpaces= prefAccessSpecifierExtraSpaces();
       
   122 			prefNamespaceBodyIndent= prefNamespaceBodyIndent();
       
   123 			prefIndentBracesForArrays= prefIndentBracesForArrays();
       
   124 			prefIndentBracesForMethods= prefIndentBracesForMethods();
       
   125 			prefIndentBracesForTypes= prefIndentBracesForTypes();
       
   126 			prefHasTemplates= hasTemplates();
       
   127 			prefTabChar= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR);
       
   128 		}
       
   129 		
       
   130 		private boolean prefUseTabs() {
       
   131 			return !CCorePlugin.SPACE.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR));
       
   132 		}
       
   133 
       
   134 		private int prefTabSize() {
       
   135 			return CodeFormatterUtil.getTabWidth(fProject);
       
   136 		}
       
   137 
       
   138 		private int prefIndentationSize() {
       
   139 			return CodeFormatterUtil.getIndentWidth(fProject);
       
   140 		}
       
   141 
       
   142 		private boolean prefArrayDimensionsDeepIndent() {
       
   143 			return true; // sensible default, no formatter setting
       
   144 		}
       
   145 
       
   146 		private int prefArrayIndent() {
       
   147 			String option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_EXPRESSIONS_IN_INITIALIZER_LIST);
       
   148 			try {
       
   149 				if (DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_BY_ONE)
       
   150 					return 1;
       
   151 			} catch (IllegalArgumentException e) {
       
   152 				// ignore and return default
       
   153 			}
       
   154 
       
   155 			return prefContinuationIndent(); // default
       
   156 		}
       
   157 
       
   158 		private boolean prefArrayDeepIndent() {
       
   159 			String option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_EXPRESSIONS_IN_INITIALIZER_LIST);
       
   160 			try {
       
   161 				return DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
       
   162 			} catch (IllegalArgumentException e) {
       
   163 				// ignore and return default
       
   164 			}
       
   165 
       
   166 			return true;
       
   167 		}
       
   168 
       
   169 		private boolean prefTernaryDeepAlign() {
       
   170 			String option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_CONDITIONAL_EXPRESSION);
       
   171 			try {
       
   172 				return DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
       
   173 			} catch (IllegalArgumentException e) {
       
   174 				// ignore and return default
       
   175 			}
       
   176 			return false;
       
   177 		}
       
   178 
       
   179 		private int prefTernaryIndent() {
       
   180 			String option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_CONDITIONAL_EXPRESSION);
       
   181 			try {
       
   182 				if (DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_BY_ONE)
       
   183 					return 1;
       
   184 				else
       
   185 					return prefContinuationIndent();
       
   186 			} catch (IllegalArgumentException e) {
       
   187 				// ignore and return default
       
   188 			}
       
   189 
       
   190 			return prefContinuationIndent();
       
   191 		}
       
   192 
       
   193 		private int prefCaseIndent() {
       
   194 			if (DefaultCodeFormatterConstants.TRUE.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_INDENT_SWITCHSTATEMENTS_COMPARE_TO_SWITCH)))
       
   195 				return 1;
       
   196 			else
       
   197 				return 0;
       
   198 		}
       
   199 
       
   200 		private int prefCaseBlockIndent() {
       
   201 			if (DefaultCodeFormatterConstants.TRUE.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_INDENT_SWITCHSTATEMENTS_COMPARE_TO_CASES)))
       
   202 				return 1;
       
   203 			else
       
   204 				return 0;
       
   205 		}
       
   206 
       
   207 		private int prefAssignmentIndent() {
       
   208 			return prefContinuationIndent();
       
   209 		}
       
   210 
       
   211 		private int prefSimpleIndent() {
       
   212 			if (prefIndentBracesForBlocks() && prefBlockIndent() == 0)
       
   213 				return 1;
       
   214 			else
       
   215 				return prefBlockIndent();
       
   216 		}
       
   217 
       
   218 		private int prefBracketIndent() {
       
   219 			return prefBlockIndent();
       
   220 		}
       
   221 
       
   222 		private boolean prefMethodDeclDeepIndent() {
       
   223 			String option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_PARAMETERS_IN_METHOD_DECLARATION);
       
   224 			try {
       
   225 				int indentStyle = DefaultCodeFormatterConstants.getIndentStyle(option);
       
   226 				return indentStyle == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
       
   227 			} catch (IllegalArgumentException e) {
       
   228 				// ignore and return default
       
   229 			}
       
   230 
       
   231 			return false;
       
   232 		}
       
   233 
       
   234 		private boolean prefMethodDeclFirstParameterDeepIndent() {
       
   235 			String option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_PARAMETERS_IN_METHOD_DECLARATION);
       
   236 			try {
       
   237 				int indentStyle = DefaultCodeFormatterConstants.getIndentStyle(option);
       
   238 				int wrappingStyle = DefaultCodeFormatterConstants.getWrappingStyle(option);
       
   239 				return indentStyle == DefaultCodeFormatterConstants.INDENT_ON_COLUMN &&
       
   240 				    	(wrappingStyle == DefaultCodeFormatterConstants.WRAP_COMPACT_FIRST_BREAK ||
       
   241 				    	wrappingStyle == DefaultCodeFormatterConstants.WRAP_ONE_PER_LINE);
       
   242 			} catch (IllegalArgumentException e) {
       
   243 				// ignore and return default
       
   244 			}
       
   245 
       
   246 			return false;
       
   247 		}
       
   248 
       
   249 		private int prefMethodDeclIndent() {
       
   250 			String option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_PARAMETERS_IN_METHOD_DECLARATION);
       
   251 			try {
       
   252 				if (DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_BY_ONE)
       
   253 					return 1;
       
   254 				else
       
   255 					return prefContinuationIndent();
       
   256 			} catch (IllegalArgumentException e) {
       
   257 				// ignore and return default
       
   258 			}
       
   259 			return 1;
       
   260 		}
       
   261 
       
   262 		private boolean prefMethodCallDeepIndent() {
       
   263 			String option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_ARGUMENTS_IN_METHOD_INVOCATION);
       
   264 			try {
       
   265 				int indentStyle = DefaultCodeFormatterConstants.getIndentStyle(option);
       
   266 				return indentStyle == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
       
   267 			} catch (IllegalArgumentException e) {
       
   268 				// ignore and return default
       
   269 			}
       
   270 			return false; // sensible default
       
   271 		}
       
   272 
       
   273 		private boolean prefMethodCallFirstParameterDeepIndent() {
       
   274 			String option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_ARGUMENTS_IN_METHOD_INVOCATION);
       
   275 			try {
       
   276 				int indentStyle = DefaultCodeFormatterConstants.getIndentStyle(option);
       
   277 				int wrappingStyle = DefaultCodeFormatterConstants.getWrappingStyle(option);
       
   278 				return indentStyle == DefaultCodeFormatterConstants.INDENT_ON_COLUMN &&
       
   279 				    	(wrappingStyle == DefaultCodeFormatterConstants.WRAP_COMPACT_FIRST_BREAK ||
       
   280 				        wrappingStyle == DefaultCodeFormatterConstants.WRAP_ONE_PER_LINE);
       
   281 			} catch (IllegalArgumentException e) {
       
   282 				// ignore and return default
       
   283 			}
       
   284 			return false; // sensible default
       
   285 		}
       
   286 
       
   287 		private int prefMethodCallIndent() {
       
   288 			String option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_ARGUMENTS_IN_METHOD_INVOCATION);
       
   289 			try {
       
   290 				if (DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_BY_ONE)
       
   291 					return 1;
       
   292 				else
       
   293 					return prefContinuationIndent();
       
   294 			} catch (IllegalArgumentException e) {
       
   295 				// ignore and return default
       
   296 			}
       
   297 
       
   298 			return 1; // sensible default
       
   299 		}
       
   300 
       
   301 		private boolean prefParenthesisDeepIndent() {
       
   302 			// don't do parenthesis deep indentation
       
   303 //			String option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_CONTINUATION_INDENTATION);
       
   304 //			try {
       
   305 //				return DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
       
   306 //			} catch (IllegalArgumentException e) {
       
   307 //				// ignore and return default
       
   308 //			}
       
   309 
       
   310 			return false;
       
   311 		}
       
   312 
       
   313 		private int prefParenthesisIndent() {
       
   314 			return prefContinuationIndent();
       
   315 		}
       
   316 
       
   317 		private int prefBlockIndent() {
       
   318 			String option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_INDENT_STATEMENTS_COMPARE_TO_BLOCK);
       
   319 			if (DefaultCodeFormatterConstants.FALSE.equals(option))
       
   320 				return 0;
       
   321 
       
   322 			return 1; // sensible default
       
   323 		}
       
   324 
       
   325 		private int prefMethodBodyIndent() {
       
   326 			if (DefaultCodeFormatterConstants.FALSE.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_INDENT_STATEMENTS_COMPARE_TO_BODY)))
       
   327 				return 0;
       
   328 
       
   329 			return 1; // sensible default
       
   330 		}
       
   331 
       
   332 		private int prefTypeIndent() {
       
   333 			String option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_INDENT_BODY_DECLARATIONS_COMPARE_TO_ACCESS_SPECIFIER);
       
   334 			if (DefaultCodeFormatterConstants.FALSE.equals(option))
       
   335 				return 0;
       
   336 
       
   337 			return 1; // sensible default
       
   338 		}
       
   339 		
       
   340 		private int prefAccessSpecifierIndent() {
       
   341 			if (DefaultCodeFormatterConstants.TRUE.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_INDENT_ACCESS_SPECIFIER_COMPARE_TO_TYPE_HEADER)))
       
   342 				return 1;
       
   343 			else
       
   344 				return 0;
       
   345 		}
       
   346 
       
   347 		private int prefAccessSpecifierExtraSpaces() {
       
   348 			// Hidden option that enables fractional indent of access specifiers.
       
   349 			IPreferencesService prefs = Platform.getPreferencesService();
       
   350 			return prefs.getInt(CCorePlugin.PLUGIN_ID, CCorePlugin.PLUGIN_ID + ".formatter.indent_access_specifier_extra_spaces", 0, null); //$NON-NLS-1$
       
   351 		}
       
   352 
       
   353 		private int prefNamespaceBodyIndent() {
       
   354 			if (DefaultCodeFormatterConstants.TRUE.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_INDENT_BODY_DECLARATIONS_COMPARE_TO_NAMESPACE_HEADER)))
       
   355 				return prefBlockIndent();
       
   356 			else
       
   357 				return 0;
       
   358 		}
       
   359 
       
   360 		private boolean prefIndentBracesForBlocks() {
       
   361 			return DefaultCodeFormatterConstants.NEXT_LINE_SHIFTED.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_BLOCK));
       
   362 		}
       
   363 
       
   364 		private boolean prefIndentBracesForArrays() {
       
   365 			return DefaultCodeFormatterConstants.NEXT_LINE_SHIFTED.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_INITIALIZER_LIST));
       
   366 		}
       
   367 
       
   368 		private boolean prefIndentBracesForMethods() {
       
   369 			return DefaultCodeFormatterConstants.NEXT_LINE_SHIFTED.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_METHOD_DECLARATION));
       
   370 		}
       
   371 
       
   372 		private boolean prefIndentBracesForTypes() {
       
   373 			return DefaultCodeFormatterConstants.NEXT_LINE_SHIFTED.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_TYPE_DECLARATION));
       
   374 		}
       
   375 
       
   376 		private int prefContinuationIndent() {
       
   377 			try {
       
   378 				return Integer.parseInt(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_CONTINUATION_INDENTATION));
       
   379 			} catch (NumberFormatException e) {
       
   380 				// ignore and return default
       
   381 			}
       
   382 
       
   383 			return 2; // sensible default
       
   384 		}
       
   385 		
       
   386 		private boolean hasTemplates() {
       
   387 			return true;
       
   388 		}
       
   389 	}
       
   390 
       
   391 	/** The document being scanned. */
       
   392 	private final IDocument fDocument;
       
   393 	/** The indentation accumulated by <code>findReferencePosition</code>. */
       
   394 	private int fIndent;
       
   395 	/** Extra spaces to add on top of fIndent */
       
   396 	private int fExtraSpaces;
       
   397 	/**
       
   398 	 * The absolute (character-counted) indentation offset for special cases
       
   399 	 * (method defs, array initializers)
       
   400 	 */
       
   401 	private int fAlign;
       
   402 	/** The stateful scan position for the indentation methods. */
       
   403 	private int fPosition;
       
   404 	/** The previous position. */
       
   405 	private int fPreviousPos;
       
   406 	/** The most recent token. */
       
   407 	private int fToken;
       
   408 	/** The line of <code>fPosition</code>. */
       
   409 	private int fLine;
       
   410 	/**
       
   411 	 * The scanner we will use to scan the document. It has to be installed
       
   412 	 * on the same document as the one we get.
       
   413 	 */
       
   414 	private final CHeuristicScanner fScanner;
       
   415 	/**
       
   416 	 * The CDT Core preferences.
       
   417 	 */
       
   418 	private final CorePrefs fPrefs;
       
   419 
       
   420 	/**
       
   421 	 * Creates a new instance.
       
   422 	 *
       
   423 	 * @param document the document to scan
       
   424 	 * @param scanner the {@link CHeuristicScanner} to be used for scanning
       
   425 	 * the document. It must be installed on the same <code>IDocument</code>.
       
   426 	 */
       
   427 	public CIndenter(IDocument document, CHeuristicScanner scanner) {
       
   428 		this(document, scanner, null);
       
   429 	}
       
   430 
       
   431 	/**
       
   432 	 * Creates a new instance.
       
   433 	 *
       
   434 	 * @param document the document to scan
       
   435 	 * @param scanner the {@link CHeuristicScanner} to be used for scanning
       
   436 	 *        the document. It must be installed on the same
       
   437 	 *        <code>IDocument</code>.
       
   438 	 * @param project the C/C++ project to get the formatter preferences from, or
       
   439 	 *        <code>null</code> to use the workspace settings
       
   440 	 */
       
   441 	public CIndenter(IDocument document, CHeuristicScanner scanner, ICProject project) {
       
   442 		Assert.isNotNull(document);
       
   443 		Assert.isNotNull(scanner);
       
   444 		fDocument= document;
       
   445 		fScanner= scanner;
       
   446 		fPrefs= new CorePrefs(project);
       
   447 	}
       
   448 
       
   449 	/**
       
   450 	 * Computes the indentation at the reference point of <code>position</code>.
       
   451 	 *
       
   452 	 * @param offset the offset in the document
       
   453 	 * @return a String which reflects the indentation at the line in which the
       
   454 	 *         reference position to <code>offset</code> resides, or <code>null</code>
       
   455 	 *         if it cannot be determined
       
   456 	 */
       
   457 	public StringBuilder getReferenceIndentation(int offset) {
       
   458 		return getReferenceIndentation(offset, false);
       
   459 	}
       
   460 
       
   461 	/**
       
   462 	 * Computes the indentation at the reference point of <code>position</code>.
       
   463 	 *
       
   464 	 * @param offset the offset in the document
       
   465 	 * @param assumeOpeningBrace <code>true</code> if an opening brace should be assumed
       
   466 	 * @return a String which reflects the indentation at the line in which the
       
   467 	 *         reference position to <code>offset</code> resides, or <code>null</code>
       
   468 	 *         if it cannot be determined
       
   469 	 */
       
   470 	private StringBuilder getReferenceIndentation(int offset, boolean assumeOpeningBrace) {
       
   471 		int unit;
       
   472 		if (assumeOpeningBrace)
       
   473 			unit= findReferencePosition(offset, Symbols.TokenLBRACE);
       
   474 		else
       
   475 			unit= findReferencePosition(offset, peekToken(offset));
       
   476 
       
   477 		// if we were unable to find anything, return null
       
   478 		if (unit == CHeuristicScanner.NOT_FOUND)
       
   479 			return null;
       
   480 
       
   481 		return getLeadingWhitespace(unit);
       
   482 	}
       
   483 
       
   484 	/**
       
   485 	 * Computes the indentation at <code>offset</code>.
       
   486 	 *
       
   487 	 * @param offset the offset in the document
       
   488 	 * @return a String which reflects the correct indentation for the line in
       
   489 	 *         which offset resides, or <code>null</code> if it cannot be
       
   490 	 *         determined
       
   491 	 */
       
   492 	public StringBuilder computeIndentation(int offset) {
       
   493 		return computeIndentation(offset, false);
       
   494 	}
       
   495 
       
   496 	/**
       
   497 	 * Computes the indentation at <code>offset</code>.
       
   498 	 *
       
   499 	 * @param offset the offset in the document
       
   500 	 * @param assumeOpeningBrace <code>true</code> if an opening brace should be assumed
       
   501 	 * @return a String which reflects the correct indentation for the line in
       
   502 	 *         which offset resides, or <code>null</code> if it cannot be
       
   503 	 *         determined
       
   504 	 */
       
   505 	public StringBuilder computeIndentation(int offset, boolean assumeOpeningBrace) {
       
   506 		StringBuilder reference= getReferenceIndentation(offset, assumeOpeningBrace);
       
   507 
       
   508 		// handle special alignment
       
   509 		if (fAlign != CHeuristicScanner.NOT_FOUND) {
       
   510 			try {
       
   511 				// a special case has been detected.
       
   512 				IRegion line= fDocument.getLineInformationOfOffset(fAlign);
       
   513 				int lineOffset= line.getOffset();
       
   514 				return createIndent(lineOffset, fAlign, false);
       
   515 			} catch (BadLocationException e) {
       
   516 				return null;
       
   517 			}
       
   518 		}
       
   519 
       
   520 		if (reference == null)
       
   521 			return null;
       
   522 
       
   523 		// Add additional indent
       
   524 		return createReusingIndent(reference, fIndent, fExtraSpaces);
       
   525 	}
       
   526 
       
   527 	/**
       
   528 	 * Computes the indentation for a continuation line at <code>offset</code>.
       
   529 	 *
       
   530 	 * @param offset the offset in the document
       
   531 	 * @return a StringBuilder which reflects the correct indentation for
       
   532 	 *         the line in  which offset resides, or <code>null</code> if it cannot be
       
   533 	 *         determined.
       
   534 	 * @throws BadLocationException
       
   535 	 */
       
   536 	public StringBuilder computeContinuationLineIndentation(int offset) throws BadLocationException {
       
   537 		StringBuilder reference= getLeadingWhitespace(offset);
       
   538 		IRegion line= fDocument.getLineInformationOfOffset(offset);
       
   539 		String string= fDocument.get(line.getOffset(), offset - line.getOffset());
       
   540 		if (string.trim().length() == 0)
       
   541 			return reference;
       
   542 		// Add additional indent
       
   543 		return createReusingIndent(reference, fPrefs.prefContinuationIndent, 0);
       
   544 	}
       
   545 	
       
   546 	/**
       
   547 	 * Computes the length of a <code>CharacterSequence</code>, counting
       
   548 	 * a tab character as the size until the next tab stop and every other
       
   549 	 * character as one.
       
   550 	 *
       
   551 	 * @param indent the string to measure
       
   552 	 * @return the visual length in characters
       
   553 	 */
       
   554 	private int computeVisualLength(CharSequence indent) {
       
   555 		final int tabSize= fPrefs.prefTabSize;
       
   556 		int length= 0;
       
   557 		for (int i= 0; i < indent.length(); i++) {
       
   558 			char ch= indent.charAt(i);
       
   559 			switch (ch) {
       
   560 			case '\t':
       
   561 				if (tabSize > 0) {
       
   562 					int reminder= length % tabSize;
       
   563 					length += tabSize - reminder;
       
   564 				}
       
   565 				break;
       
   566 			case ' ':
       
   567 				length++;
       
   568 				break;
       
   569 			}
       
   570 		}
       
   571 		return length;
       
   572 	}
       
   573 
       
   574 	/**
       
   575 	 * Strips any characters off the end of <code>reference</code> that exceed
       
   576 	 * <code>indentLength</code>.
       
   577 	 *
       
   578 	 * @param reference the string to measure
       
   579 	 * @param indentLength the maximum visual indentation length
       
   580 	 * @return the stripped <code>reference</code>
       
   581 	 */
       
   582 	private StringBuilder stripExceedingChars(StringBuilder reference, int indentLength) {
       
   583 		final int tabSize= fPrefs.prefTabSize;
       
   584 		int measured= 0;
       
   585 		int chars= reference.length();
       
   586 		int i= 0;
       
   587 		for (; measured < indentLength && i < chars; i++) {
       
   588 			char ch= reference.charAt(i);
       
   589 			switch (ch) {
       
   590 			case '\t':
       
   591 				if (tabSize > 0) {
       
   592 					int reminder= measured % tabSize;
       
   593 					measured += tabSize - reminder;
       
   594 				}
       
   595 				break;
       
   596 			case ' ':
       
   597 				measured++;
       
   598 				break;
       
   599 			}
       
   600 		}
       
   601 		int deleteFrom= measured > indentLength ? i - 1 : i;
       
   602 
       
   603 		return reference.delete(deleteFrom, chars);
       
   604 	}
       
   605 
       
   606 	/**
       
   607 	 * Returns the indentation of the line at <code>offset</code> as a
       
   608 	 * <code>StringBuilder</code>. If the offset is not valid, the empty string
       
   609 	 * is returned.
       
   610 	 *
       
   611 	 * @param offset the offset in the document
       
   612 	 * @return the indentation (leading whitespace) of the line in which
       
   613 	 * 		   <code>offset</code> is located
       
   614 	 */
       
   615 	private StringBuilder getLeadingWhitespace(int offset) {
       
   616 		StringBuilder indent= new StringBuilder();
       
   617 		try {
       
   618 			IRegion line= fDocument.getLineInformationOfOffset(offset);
       
   619 			int lineOffset= line.getOffset();
       
   620 			int nonWS= fScanner.findNonWhitespaceForwardInAnyPartition(lineOffset, lineOffset + line.getLength());
       
   621 			indent.append(fDocument.get(lineOffset, nonWS - lineOffset));
       
   622 			return indent;
       
   623 		} catch (BadLocationException e) {
       
   624 			return indent;
       
   625 		}
       
   626 	}
       
   627 
       
   628 	/**
       
   629 	 * Creates an indentation string of the length indent - start, consisting of
       
   630 	 * the content in <code>fDocument</code> in the range [start, indent),
       
   631 	 * with every character replaced by a space except for tabs, which are kept
       
   632 	 * as such.
       
   633 	 * <p>
       
   634 	 * If <code>convertSpaceRunsToTabs</code> is <code>true</code>, every
       
   635 	 * run of the number of spaces that make up a tab are replaced by a tab
       
   636 	 * character. If it is not set, no conversion takes place, but tabs in the
       
   637 	 * original range are still copied verbatim.
       
   638 	 * </p>
       
   639 	 *
       
   640 	 * @param start the start of the document region to copy the indent from
       
   641 	 * @param indent the exclusive end of the document region to copy the indent
       
   642 	 *        from
       
   643 	 * @param convertSpaceRunsToTabs whether to convert consecutive runs of
       
   644 	 *        spaces to tabs
       
   645 	 * @return the indentation corresponding to the document content specified
       
   646 	 *         by <code>start</code> and <code>indent</code>
       
   647 	 */
       
   648 	private StringBuilder createIndent(int start, final int indent, final boolean convertSpaceRunsToTabs) {
       
   649 		final boolean convertTabs= fPrefs.prefUseTabs && convertSpaceRunsToTabs;
       
   650 		final int tabLen= fPrefs.prefTabSize;
       
   651 		final StringBuilder ret= new StringBuilder();
       
   652 		try {
       
   653 			int spaces= 0;
       
   654 			while (start < indent) {
       
   655 				char ch= fDocument.getChar(start);
       
   656 				if (ch == '\t') {
       
   657 					ret.append('\t');
       
   658 					spaces= 0;
       
   659 				} else if (convertTabs) {
       
   660 					spaces++;
       
   661 					if (spaces == tabLen) {
       
   662 						ret.append('\t');
       
   663 						spaces= 0;
       
   664 					}
       
   665 				} else {
       
   666 					ret.append(' ');
       
   667 				}
       
   668 
       
   669 				start++;
       
   670 			}
       
   671 			// remainder
       
   672 			while (spaces-- > 0)
       
   673 				ret.append(' ');
       
   674 		} catch (BadLocationException e) {
       
   675 		}
       
   676 
       
   677 		return ret;
       
   678 	}
       
   679 
       
   680 	/**
       
   681 	 * Creates a string with a visual length of the given
       
   682 	 * <code>indentationSize</code>.
       
   683 	 *
       
   684 	 * @param buffer the original indent to reuse if possible
       
   685 	 * @param additional the additional indentation units to add or subtract to
       
   686 	 *        reference
       
   687 	 * @param extraSpaces additional spaces to add to indentation.
       
   688 	 * @return the modified <code>buffer</code> reflecting the indentation
       
   689 	 *         adapted to <code>additional</code>
       
   690 	 */
       
   691 	public StringBuilder createReusingIndent(StringBuilder buffer, int additional, int extraSpaces) {
       
   692 		int refLength= computeVisualLength(buffer);
       
   693 		int addLength= fPrefs.prefIndentationSize * additional + extraSpaces; // may be < 0
       
   694 		int totalLength= Math.max(0, refLength + addLength);
       
   695 
       
   696 		// copy the reference indentation for the indent up to the last tab
       
   697 		// stop within the maxCopy area
       
   698 		int minLength= Math.min(totalLength, refLength);
       
   699 		int tabSize= fPrefs.prefTabSize;
       
   700 		int maxCopyLength= tabSize > 0 ? minLength - minLength % tabSize : minLength; // maximum indent to copy
       
   701 		stripExceedingChars(buffer, maxCopyLength);
       
   702 
       
   703 		// add additional indent
       
   704 		int missing= totalLength - maxCopyLength;
       
   705 		final int tabs, spaces;
       
   706 		if (CCorePlugin.SPACE.equals(fPrefs.prefTabChar)) {
       
   707 			tabs= 0;
       
   708 			spaces= missing;
       
   709 		} else {
       
   710 			tabs= tabSize > 0 ? missing / tabSize : 0;
       
   711 			spaces= tabSize > 0 ? missing % tabSize : missing;
       
   712 		}
       
   713 		for (int i= 0; i < tabs; i++)
       
   714 			buffer.append('\t');
       
   715 		for (int i= 0; i < spaces; i++)
       
   716 			buffer.append(' ');
       
   717 		return buffer;
       
   718 	}
       
   719 
       
   720 	/**
       
   721 	 * Returns relative indent of continuation lines.
       
   722 	 * @return a number of indentation units.
       
   723 	 */
       
   724 	public int getContinuationLineIndent() {
       
   725 		return fPrefs.prefContinuationIndent;
       
   726 	}
       
   727 
       
   728 	/**
       
   729 	 * Returns the reference position regarding to indentation for <code>offset</code>,
       
   730 	 * or <code>NOT_FOUND</code>. This method calls
       
   731 	 * {@link #findReferencePosition(int, int) findReferencePosition(offset, nextChar)} where
       
   732 	 * <code>nextChar</code> is the next character after <code>offset</code>.
       
   733 	 *
       
   734 	 * @param offset the offset for which the reference is computed
       
   735 	 * @return the reference statement relative to which <code>offset</code>
       
   736 	 *         should be indented, or {@link CHeuristicScanner#NOT_FOUND}
       
   737 	 */
       
   738 	public int findReferencePosition(int offset) {
       
   739 		return findReferencePosition(offset, peekToken(offset));
       
   740 	}
       
   741 
       
   742 	/**
       
   743 	 * Peeks the next token in the document that comes after <code>offset</code>
       
   744 	 * on the same line as <code>offset</code>.
       
   745 	 *
       
   746 	 * @param offset the offset into document
       
   747 	 * @return the token symbol of the next element, or TokenEOF if there is none
       
   748 	 */
       
   749 	private int peekToken(int offset) {
       
   750 		if (offset < fDocument.getLength()) {
       
   751 			try {
       
   752 				IRegion line= fDocument.getLineInformationOfOffset(offset);
       
   753 				int lineOffset= line.getOffset();
       
   754 				int next= fScanner.nextToken(offset, lineOffset + line.getLength());
       
   755 				return next;
       
   756 			} catch (BadLocationException e) {
       
   757 			}
       
   758 		}
       
   759 		return Symbols.TokenEOF;
       
   760 	}
       
   761 
       
   762 	/**
       
   763 	 * Returns the reference position regarding to indentation for <code>position</code>,
       
   764 	 * or <code>NOT_FOUND</code>.
       
   765 	 *
       
   766 	 * <p>If <code>peekNextChar</code> is <code>true</code>, the next token after
       
   767 	 * <code>offset</code> is read and taken into account when computing the
       
   768 	 * indentation. Currently, if the next token is the first token on the line
       
   769 	 * (i.e. only preceded by whitespace), the following tokens are specially
       
   770 	 * handled:
       
   771 	 * <ul>
       
   772 	 * 	<li><code>switch</code> labels are indented relative to the switch block</li>
       
   773 	 * 	<li>opening curly braces are aligned correctly with the introducing code</li>
       
   774 	 * 	<li>closing curly braces are aligned properly with the introducing code of
       
   775 	 * 		the matching opening brace</li>
       
   776 	 * 	<li>closing parenthesis' are aligned with their opening peer</li>
       
   777 	 * 	<li>the <code>else</code> keyword is aligned with its <code>if</code>, anything
       
   778 	 * 		else is aligned normally (i.e. with the base of any introducing statements).</li>
       
   779 	 *  <li>if there is no token on the same line after <code>offset</code>, the indentation
       
   780 	 * 		is the same as for an <code>else</code> keyword</li>
       
   781 	 * </ul>
       
   782 	 *
       
   783 	 * @param offset the offset for which the reference is computed
       
   784 	 * @param nextToken the next token to assume in the document
       
   785 	 * @return the reference statement relative to which <code>offset</code>
       
   786 	 *         should be indented, or {@link CHeuristicScanner#NOT_FOUND}
       
   787 	 */
       
   788 	public int findReferencePosition(int offset, int nextToken) {
       
   789 		boolean danglingElse= false;
       
   790 		boolean cancelIndent= false; // If set to true, fIndent is ignored.
       
   791 		int extraIndent= 0; // Can be either positive or negative.
       
   792 		boolean matchBrace= false;
       
   793 		boolean matchParen= false;
       
   794 		boolean matchCase= false;
       
   795 		boolean matchAccessSpecifier= false;
       
   796 
       
   797 		// Account for un-indentation characters already typed in, but after position.
       
   798 		// If they are on a line by themselves, the indentation gets adjusted accordingly.
       
   799 		//
       
   800 		// Also account for a dangling else.
       
   801 		if (offset < fDocument.getLength()) {
       
   802 			try {
       
   803 				IRegion line= fDocument.getLineInformationOfOffset(offset);
       
   804 				int lineOffset= line.getOffset();
       
   805 				int prevPos= Math.max(offset - 1, 0);
       
   806 				boolean isFirstTokenOnLine= fDocument.get(lineOffset, prevPos + 1 - lineOffset).trim().length() == 0;
       
   807 				int prevToken= fScanner.previousToken(prevPos, CHeuristicScanner.UNBOUND);
       
   808 				boolean bracelessBlockStart= fScanner.isBracelessBlockStart(prevPos, CHeuristicScanner.UNBOUND);
       
   809 
       
   810 				switch (nextToken) {
       
   811 				case Symbols.TokenELSE:
       
   812 					danglingElse= true;
       
   813 					break;
       
   814 					
       
   815 				case Symbols.TokenCASE:
       
   816 				case Symbols.TokenDEFAULT:
       
   817 					if (isFirstTokenOnLine)
       
   818 						matchCase= true;
       
   819 					break;
       
   820 					
       
   821 				case Symbols.TokenPUBLIC:
       
   822 				case Symbols.TokenPROTECTED:
       
   823 				case Symbols.TokenPRIVATE:
       
   824 					if (isFirstTokenOnLine)
       
   825 						matchAccessSpecifier= true;
       
   826 					break;
       
   827 					
       
   828 				case Symbols.TokenLBRACE: // for opening-brace-on-new-line style
       
   829 					if (bracelessBlockStart) {
       
   830 						extraIndent= fPrefs.prefIndentBracesForBlocks ? 0 : -1;
       
   831 					} else if (prevToken == Symbols.TokenCOLON && !fPrefs.prefIndentBracesForBlocks) {
       
   832 						extraIndent= -1;
       
   833 					} else if ((prevToken == Symbols.TokenEQUAL || prevToken == Symbols.TokenRBRACKET) &&
       
   834 							!fPrefs.prefIndentBracesForArrays) {
       
   835 						cancelIndent= true;
       
   836 					} else if (prevToken == Symbols.TokenRPAREN && fPrefs.prefIndentBracesForMethods) {
       
   837 						extraIndent= 1;
       
   838 					} else if (prevToken == Symbols.TokenIDENT && fPrefs.prefIndentBracesForTypes) {
       
   839 						extraIndent= 1;
       
   840 					}
       
   841 					break;
       
   842 					
       
   843 				case Symbols.TokenRBRACE: // closing braces get unindented
       
   844 					if (isFirstTokenOnLine || prevToken != Symbols.TokenLBRACE)
       
   845 						matchBrace= true;
       
   846 					break;
       
   847 					
       
   848 				case Symbols.TokenRPAREN:
       
   849 					if (isFirstTokenOnLine)
       
   850 						matchParen= true;
       
   851 					break;
       
   852 				}
       
   853 			} catch (BadLocationException e) {
       
   854 			}
       
   855 		} else {
       
   856 			// don't assume an else could come if we are at the end of file
       
   857 			danglingElse= false;
       
   858 		}
       
   859 
       
   860 		int ref= findReferencePosition(offset, danglingElse, matchBrace, matchParen, matchCase,
       
   861 				matchAccessSpecifier);
       
   862 		if (cancelIndent) {
       
   863 			fIndent = 0;
       
   864 		} else if (extraIndent > 0) {
       
   865 			fAlign= CHeuristicScanner.NOT_FOUND;
       
   866 			fIndent += extraIndent;
       
   867 		} else {
       
   868 			fIndent += extraIndent;
       
   869 		}
       
   870 		return ref;
       
   871 	}
       
   872 
       
   873 	/**
       
   874 	 * Returns the reference position regarding to indentation for <code>position</code>,
       
   875 	 * or <code>NOT_FOUND</code>.<code>fIndent</code> will contain the
       
   876 	 * relative indentation (in indentation units, not characters) after the
       
   877 	 * call. If there is a special alignment (e.g. for a method declaration
       
   878 	 * where parameters should be aligned), <code>fAlign</code> will contain
       
   879 	 * the absolute position of the alignment reference in <code>fDocument</code>,
       
   880 	 * otherwise <code>fAlign</code> is set to <code>CHeuristicScanner.NOT_FOUND</code>.
       
   881 	 *
       
   882 	 * @param offset the offset for which the reference is computed
       
   883 	 * @param danglingElse whether a dangling else should be assumed at <code>position</code>
       
   884 	 * @param matchBrace whether the position of the matching brace should be
       
   885 	 *            returned instead of doing code analysis
       
   886 	 * @param matchParen whether the position of the matching parenthesis
       
   887 	 *            should be returned instead of doing code analysis
       
   888 	 * @param matchCase whether the position of a switch statement reference
       
   889 	 *            should be returned (either an earlier case statement or the
       
   890 	 *            switch block brace)
       
   891 	 * @param matchAccessSpecifier whether the position of a class body reference
       
   892 	 *            should be returned (either an earlier public/protected/private
       
   893 	 *            or the class body brace)
       
   894 	 * @return the reference statement relative to which <code>position</code>
       
   895 	 *         should be indented, or {@link CHeuristicScanner#NOT_FOUND}
       
   896 	 */
       
   897 	public int findReferencePosition(int offset, boolean danglingElse, boolean matchBrace, boolean matchParen,
       
   898 			boolean matchCase, boolean matchAccessSpecifier) {
       
   899 		fIndent= 0; // the indentation modification
       
   900 		fAlign= CHeuristicScanner.NOT_FOUND;
       
   901 		fPosition= offset;
       
   902 
       
   903 		// forward cases
       
   904 		// An unindentation happens sometimes if the next token is special, namely on braces, parens and case
       
   905 		// labels align braces, but handle the case where we align with the method declaration start instead
       
   906 		// of the opening brace.
       
   907 		if (matchBrace) {
       
   908 			if (skipScope(Symbols.TokenLBRACE, Symbols.TokenRBRACE)) {
       
   909 				try {
       
   910 					// Align with the opening brace that is on a line by its own
       
   911 					int lineOffset= fDocument.getLineOffset(fLine);
       
   912 					if (lineOffset <= fPosition && fDocument.get(lineOffset, fPosition - lineOffset).trim().length() == 0)
       
   913 						return fPosition;
       
   914 				} catch (BadLocationException e) {
       
   915 					// Concurrent modification - walk default path
       
   916 				}
       
   917 				// If the opening brace is not on the start of the line, skip to the start
       
   918 				int pos= skipToStatementStart(true, true);
       
   919 				fIndent= 0; // indent is aligned with reference position
       
   920 				return pos;
       
   921 			} else {
       
   922 				// If we can't find the matching brace, the heuristic is to unindent
       
   923 				// by one against the normal position
       
   924 				int pos= findReferencePosition(offset, danglingElse, false, matchParen, matchCase,
       
   925 						matchAccessSpecifier);
       
   926 				fIndent--;
       
   927 				return pos;
       
   928 			}
       
   929 		}
       
   930 
       
   931 		// Align parentheses
       
   932 		if (matchParen) {
       
   933 			if (skipScope(Symbols.TokenLPAREN, Symbols.TokenRPAREN)) {
       
   934 				return fPosition;
       
   935 			} else {
       
   936 				// if we can't find the matching paren, the heuristic is to unindent
       
   937 				// by one against the normal position
       
   938 				int pos= findReferencePosition(offset, danglingElse, matchBrace, false, matchCase,
       
   939 						matchAccessSpecifier);
       
   940 				fIndent--;
       
   941 				return pos;
       
   942 			}
       
   943 		}
       
   944 
       
   945 		// The only reliable way to get case labels aligned (due to many different styles of using braces in
       
   946 		// a block) is to go for another case statement, or the scope opening brace.
       
   947 		if (matchCase) {
       
   948 			return matchCaseAlignment();
       
   949 		}
       
   950 
       
   951 		// the only reliable way to get access specifiers aligned (due to many different styles of using
       
   952 		// braces in a block) is to go for another access specifier, or the scope opening brace.
       
   953 		if (matchAccessSpecifier) {
       
   954 			return matchAccessSpecifierAlignment();
       
   955 		}
       
   956 		
       
   957 		nextToken();
       
   958 		// Skip access specifiers
       
   959 		while (fToken == Symbols.TokenCOLON && isAccessSpecifier()) {
       
   960 			nextToken();
       
   961 		}
       
   962 
       
   963 		int line= fLine;
       
   964 		switch (fToken) {
       
   965 		case Symbols.TokenGREATERTHAN:
       
   966 		case Symbols.TokenRBRACE:
       
   967 			// skip the block and fall through
       
   968 			// if we can't complete the scope, reset the scan position
       
   969 			int pos= fPosition;
       
   970 			if (!skipScope())
       
   971 				fPosition= pos;
       
   972 			return skipToStatementStart(danglingElse, false);
       
   973 		case Symbols.TokenSEMICOLON:
       
   974 			// this is the 90% case: after a statement block
       
   975 			// the end of the previous statement / block previous.end
       
   976 			// search to the end of the statement / block before the previous;
       
   977 			// the token just after that is previous.start
       
   978 			return skipToStatementStart(danglingElse, false);
       
   979 
       
   980 		// scope introduction: special treat who special is
       
   981 		case Symbols.TokenLPAREN:
       
   982 		case Symbols.TokenLBRACE:
       
   983 		case Symbols.TokenLBRACKET:
       
   984 			return handleScopeIntroduction(Math.min(offset + 1, fDocument.getLength()), true);
       
   985 
       
   986 		case Symbols.TokenEOF:
       
   987 			// trap when hitting start of document
       
   988 			return CHeuristicScanner.NOT_FOUND;
       
   989 
       
   990 		case Symbols.TokenEQUAL:
       
   991 			// indent assignments
       
   992 			fIndent= fPrefs.prefAssignmentIndent;
       
   993 			return fPosition;
       
   994 
       
   995 		case Symbols.TokenCOLON:
       
   996 			pos= fPosition;
       
   997 			if (looksLikeCaseStatement()) {
       
   998 				fIndent= fPrefs.prefCaseBlockIndent;
       
   999 				return pos;
       
  1000 			}
       
  1001 			fPosition= pos;
       
  1002 			if (looksLikeTypeInheritanceDecl()) {
       
  1003 				fIndent= fPrefs.prefBlockIndent;
       
  1004 				return pos;
       
  1005 			}
       
  1006 			fPosition= pos;
       
  1007 			if (looksLikeConstructorInitializer()) {
       
  1008 				fIndent= fPrefs.prefBlockIndent;
       
  1009 				return pos;
       
  1010 			}
       
  1011 			fPosition= pos;
       
  1012 			if (isConditional()) {
       
  1013 				fPosition= offset;
       
  1014 				fLine= line;
       
  1015 				return skipToPreviousListItemOrListStart();
       
  1016 			}
       
  1017 			fPosition= pos;
       
  1018 			return skipToStatementStart(danglingElse, false);
       
  1019 
       
  1020 		case Symbols.TokenQUESTIONMARK:
       
  1021 			if (fPrefs.prefTernaryDeepAlign) {
       
  1022 				setFirstElementAlignment(fPosition, offset + 1);
       
  1023 			} else {
       
  1024 				fIndent= fPrefs.prefTernaryIndent;
       
  1025 			}
       
  1026 			return fPosition;
       
  1027 
       
  1028 		// indentation for blockless introducers:
       
  1029 		case Symbols.TokenDO:
       
  1030 		case Symbols.TokenWHILE:
       
  1031 		case Symbols.TokenELSE:
       
  1032 			fIndent= fPrefs.prefSimpleIndent;
       
  1033 			return fPosition;
       
  1034 
       
  1035 		case Symbols.TokenTRY:
       
  1036 			return skipToStatementStart(danglingElse, false);
       
  1037 
       
  1038 		case Symbols.TokenRPAREN:
       
  1039 			if (skipScope(Symbols.TokenLPAREN, Symbols.TokenRPAREN)) {
       
  1040 				int scope= fPosition;
       
  1041 				nextToken();
       
  1042 				if (fToken == Symbols.TokenIF || fToken == Symbols.TokenWHILE || fToken == Symbols.TokenFOR) {
       
  1043 					fIndent= fPrefs.prefSimpleIndent;
       
  1044 					return fPosition;
       
  1045 				}
       
  1046 				if (fToken == Symbols.TokenSWITCH) {
       
  1047 					return fPosition;
       
  1048 				}
       
  1049 				fPosition= scope;
       
  1050 				if (looksLikeMethodDecl()) {
       
  1051 					return skipToStatementStart(danglingElse, false);
       
  1052 				}
       
  1053 				if (fToken == Symbols.TokenCATCH) {
       
  1054 					return skipToStatementStart(danglingElse, false);
       
  1055 				}
       
  1056 				fPosition= scope;
       
  1057 				if (looksLikeAnonymousTypeDecl()) {
       
  1058 					return skipToStatementStart(danglingElse, false);
       
  1059 				}
       
  1060 			}
       
  1061 			// restore
       
  1062 			fPosition= offset;
       
  1063 			fLine= line;
       
  1064 			// else: fall through to default
       
  1065 			return skipToPreviousListItemOrListStart();
       
  1066 
       
  1067 		case Symbols.TokenCOMMA:
       
  1068 			// inside a list of some type
       
  1069 			// easy if there is already a list item before with its own indentation - we just align
       
  1070 			// if not: take the start of the list ( LPAREN, LBRACE, LBRACKET ) and either align or
       
  1071 			// indent by list-indent
       
  1072 			return skipToPreviousListItemOrListStart();
       
  1073 		default:
       
  1074 			// inside whatever we don't know about: similar to the list case:
       
  1075 			// if we are inside a continued expression, then either align with a previous line that
       
  1076 			// has indentation or indent from the expression start line (either a scope introducer
       
  1077 			// or the start of the expression).
       
  1078 			return skipToPreviousListItemOrListStart();
       
  1079 		}
       
  1080 	}
       
  1081 
       
  1082 	/**
       
  1083 	 * Test whether an identifier encountered during scanning is part of
       
  1084 	 * a type declaration, by scanning backward and ignoring any identifiers, commas,
       
  1085 	 * and colons until we hit <code>class</code>, <code>struct</code>, <code>union</code>,
       
  1086 	 * or <code>enum</code>.  If any braces, semicolons, or parentheses are encountered,
       
  1087 	 * this is not a type declaration.
       
  1088 	 * @return the reference offset of the start of the statement
       
  1089 	 */
       
  1090 	private int matchTypeDeclaration() {
       
  1091 		while (true) {
       
  1092 			nextToken();
       
  1093 			if (fToken == Symbols.TokenIDENT
       
  1094 					|| fToken == Symbols.TokenCOMMA
       
  1095 					|| fToken == Symbols.TokenCOLON
       
  1096 					|| fToken == Symbols.TokenPUBLIC
       
  1097 					|| fToken == Symbols.TokenPROTECTED
       
  1098 					|| fToken == Symbols.TokenPRIVATE) {
       
  1099 				continue;
       
  1100 			}
       
  1101 			if (fToken == Symbols.TokenCLASS
       
  1102 					|| fToken == Symbols.TokenSTRUCT
       
  1103 					|| fToken == Symbols.TokenUNION
       
  1104 					|| fToken == Symbols.TokenENUM) {
       
  1105 				// inside a type declaration?  Only so if not preceded by '(' or ',' as in
       
  1106 				// a parameter list.  To be safe, only accept ';' or EOF
       
  1107 				int pos= fPosition;
       
  1108 				nextToken();
       
  1109 				if (fToken == Symbols.TokenSEMICOLON || fToken == Symbols.TokenEOF) {
       
  1110 					return pos;
       
  1111 				} else {
       
  1112 					return CHeuristicScanner.NOT_FOUND;
       
  1113 				}
       
  1114 			} else {
       
  1115 				return CHeuristicScanner.NOT_FOUND;
       
  1116 			}
       
  1117 		}
       
  1118 	}
       
  1119 
       
  1120 	/**
       
  1121 	 * Test whether the colon at the current position marks a case statement
       
  1122 	 * 
       
  1123 	 * @return <code>true</code> if this looks like a case statement
       
  1124 	 */
       
  1125 	private boolean looksLikeCaseStatement() {
       
  1126 		nextToken();
       
  1127 		switch (fToken) {
       
  1128 		case Symbols.TokenCASE:
       
  1129 			// char literal got skipped
       
  1130 			return true;
       
  1131 		case Symbols.TokenIDENT:
       
  1132 			nextToken();
       
  1133 			while (skipQualifiers()) {
       
  1134 				nextToken();
       
  1135 			}
       
  1136 			if (fToken == Symbols.TokenCASE) {
       
  1137 				return true;
       
  1138 			}
       
  1139 			break;
       
  1140 		case Symbols.TokenOTHER:
       
  1141 			nextToken();
       
  1142 			if (fToken == Symbols.TokenCASE) {
       
  1143 				return true;
       
  1144 			}
       
  1145 			break;
       
  1146 		case Symbols.TokenDEFAULT:
       
  1147 			return true;
       
  1148 		}
       
  1149 		return false;
       
  1150 	}
       
  1151 
       
  1152 	/**
       
  1153 	 * Test whether the colon at the current position marks a type inheritance decl.
       
  1154 	 * 
       
  1155 	 * @return <code>true</code> if this looks like a type inheritance decl.
       
  1156 	 */
       
  1157 	private boolean looksLikeTypeInheritanceDecl() {
       
  1158 		nextToken();
       
  1159 		switch (fToken) {
       
  1160 		case Symbols.TokenIDENT:
       
  1161 			nextToken();
       
  1162 			while (skipQualifiers()) {
       
  1163 				nextToken();
       
  1164 			}
       
  1165 			switch (fToken) {
       
  1166 			case Symbols.TokenCLASS:
       
  1167 			case Symbols.TokenSTRUCT:
       
  1168 			case Symbols.TokenUNION:
       
  1169 				return true;
       
  1170 			}
       
  1171 			break;
       
  1172 		}
       
  1173 		return false;
       
  1174 	}
       
  1175 
       
  1176 	/**
       
  1177 	 * Test whether the colon at the current position marks a constructor initializer list.
       
  1178 	 * 
       
  1179 	 * @return <code>true</code> if this looks like a constructor initializer list.
       
  1180 	 */
       
  1181 	private boolean looksLikeConstructorInitializer() {
       
  1182 		nextToken();
       
  1183 		if (fToken != Symbols.TokenRPAREN) {
       
  1184 			return false;
       
  1185 		}
       
  1186 		if (!skipScope()) {
       
  1187 			return false;
       
  1188 		}
       
  1189 		nextToken();
       
  1190 		if (fToken == Symbols.TokenTHROW) {
       
  1191 			nextToken();
       
  1192 			if (fToken != Symbols.TokenRPAREN) {
       
  1193 				return false;
       
  1194 			}
       
  1195 			if (!skipScope()) {
       
  1196 				return false;
       
  1197 			}
       
  1198 			nextToken();
       
  1199 		}
       
  1200 		if (fToken != Symbols.TokenIDENT) {
       
  1201 			return false;
       
  1202 		}
       
  1203 		nextToken();
       
  1204 		switch (fToken) {
       
  1205 		case Symbols.TokenCOLON:
       
  1206 			nextToken();
       
  1207 			switch (fToken) {
       
  1208 			case Symbols.TokenCOLON:  // A::A() :
       
  1209 			case Symbols.TokenPUBLIC:  // public: A() :
       
  1210 			case Symbols.TokenPROTECTED:
       
  1211 			case Symbols.TokenPRIVATE:
       
  1212 				return true;
       
  1213 			}
       
  1214 			return false;
       
  1215 			
       
  1216 		case Symbols.TokenLBRACE:  // class A { A() :
       
  1217 		case Symbols.TokenRBRACE:
       
  1218 		case Symbols.TokenSEMICOLON:
       
  1219 			return true;
       
  1220 		}
       
  1221 		return false;
       
  1222 	}
       
  1223 
       
  1224 	/**
       
  1225 	 * Test whether the colon at the current position marks an access specifier.
       
  1226 	 * 
       
  1227 	 * @return <code>true</code> if current position marks an access specifier
       
  1228 	 */
       
  1229 	private boolean isAccessSpecifier() {
       
  1230 		int pos= fPosition;
       
  1231 		int token = fToken;
       
  1232 		nextToken();
       
  1233 		switch (fToken) {
       
  1234 		case Symbols.TokenPUBLIC:
       
  1235 		case Symbols.TokenPROTECTED:
       
  1236 		case Symbols.TokenPRIVATE:
       
  1237 			return true;
       
  1238 		}
       
  1239 		fToken = token;
       
  1240 		fPosition= pos;
       
  1241 		return false;
       
  1242 	}
       
  1243 
       
  1244 	/**
       
  1245 	 * Skips to the start of a statement that ends at the current position.
       
  1246 	 *
       
  1247 	 * @param danglingElse whether to indent aligned with the last <code>if</code>
       
  1248 	 * @param isInBlock whether the current position is inside a block, which limits the search scope to
       
  1249 	 *   the next scope introducer
       
  1250 	 * @return the reference offset of the start of the statement
       
  1251 	 */
       
  1252 	private int skipToStatementStart(boolean danglingElse, boolean isInBlock) {
       
  1253 		final int NOTHING= 0;
       
  1254 		final int READ_PARENS= 1;
       
  1255 		final int READ_IDENT= 2;
       
  1256 		int mayBeMethodBody= NOTHING;
       
  1257 		boolean isTypeBody= false;
       
  1258 		int startLine = fLine;
       
  1259 		while (true) {
       
  1260 			int prevToken= fToken;
       
  1261 			nextToken();
       
  1262 
       
  1263 			if (isInBlock) {
       
  1264 				switch (fToken) {
       
  1265 				// exit on all block introducers
       
  1266 				case Symbols.TokenIF:
       
  1267 				case Symbols.TokenELSE:
       
  1268 				case Symbols.TokenCATCH:
       
  1269 				case Symbols.TokenDO:
       
  1270 				case Symbols.TokenWHILE:
       
  1271 				case Symbols.TokenFOR:
       
  1272 				case Symbols.TokenTRY:
       
  1273 					return fPosition;
       
  1274 
       
  1275 				case Symbols.TokenCLASS:
       
  1276 				case Symbols.TokenENUM:
       
  1277 				case Symbols.TokenSTRUCT:
       
  1278 				case Symbols.TokenUNION:
       
  1279 					isTypeBody= true;
       
  1280 					break;
       
  1281 
       
  1282 				case Symbols.TokenSWITCH:
       
  1283 					fIndent= fPrefs.prefCaseIndent;
       
  1284 					return fPosition;
       
  1285 				}
       
  1286 			}
       
  1287 
       
  1288 			if (fToken == Symbols.TokenSEMICOLON && fLine == startLine) {
       
  1289 				// Skip semicolons on the same line. Otherwise we may never reach beginning of a 'for'
       
  1290 				// statement.
       
  1291 				continue;
       
  1292 			}
       
  1293 
       
  1294 			switch (fToken) {
       
  1295 			// scope introduction through: LPAREN, LBRACE, LBRACKET
       
  1296 			// search stop on SEMICOLON, RBRACE, COLON, EOF
       
  1297 			// -> the next token is the start of the statement (i.e. previousPos when backward scanning)
       
  1298 			case Symbols.TokenLPAREN:
       
  1299 				if (peekToken() == Symbols.TokenFOR) {
       
  1300 					nextToken();  // Consume 'for'
       
  1301 					fIndent = fPrefs.prefContinuationIndent;
       
  1302 					return fPosition;
       
  1303 				}
       
  1304 				break;
       
  1305 
       
  1306 			case Symbols.TokenLBRACE:
       
  1307 			case Symbols.TokenSEMICOLON:
       
  1308 			case Symbols.TokenEOF:
       
  1309 				if (isInBlock)
       
  1310 					fIndent= getBlockIndent(mayBeMethodBody == READ_IDENT, isTypeBody);
       
  1311 				// else: fIndent set by previous calls
       
  1312 				return fPreviousPos;
       
  1313 
       
  1314 			case Symbols.TokenCOLON:
       
  1315 				int pos= fPreviousPos;
       
  1316 				if (!isConditional())
       
  1317 					return pos;
       
  1318 				break;
       
  1319 
       
  1320 			case Symbols.TokenRBRACE:
       
  1321 				// RBRACE is a little tricky: it can be the end of an array definition, but
       
  1322 				// usually it is the end of a previous block
       
  1323 				pos= fPreviousPos; // store state
       
  1324 				if (skipScope()) {
       
  1325 					if (looksLikeArrayInitializerIntro()) {
       
  1326 						continue; // it's an array
       
  1327 					}
       
  1328 					if (prevToken == Symbols.TokenSEMICOLON) {
       
  1329 						// end of type def
       
  1330 						continue;
       
  1331 					}
       
  1332 				}
       
  1333 				if (isInBlock)
       
  1334 					fIndent= getBlockIndent(mayBeMethodBody == READ_IDENT, isTypeBody);
       
  1335 				return pos; // it's not - do as with all the above
       
  1336 
       
  1337 			// scopes: skip them
       
  1338 			case Symbols.TokenRPAREN:
       
  1339 				if (isInBlock)
       
  1340 					mayBeMethodBody= READ_PARENS;
       
  1341 				// fall thru
       
  1342 				pos= fPreviousPos;
       
  1343 				if (skipScope())
       
  1344 					break;
       
  1345 				else
       
  1346 					return pos;
       
  1347 			case Symbols.TokenRBRACKET:
       
  1348 				pos= fPreviousPos;
       
  1349 				if (skipScope())
       
  1350 					break;
       
  1351 				else
       
  1352 					return pos;
       
  1353 
       
  1354 			// IF / ELSE: align the position after the conditional block with the if
       
  1355 			// so we are ready for an else, except if danglingElse is false
       
  1356 			// in order for this to work, we must skip an else to its if
       
  1357 			case Symbols.TokenIF:
       
  1358 				if (danglingElse)
       
  1359 					return fPosition;
       
  1360 				else
       
  1361 					break;
       
  1362 			case Symbols.TokenELSE:
       
  1363 				// skip behind the next if, as we have that one covered
       
  1364 				pos= fPosition;
       
  1365 				if (skipNextIF())
       
  1366 					break;
       
  1367 				else
       
  1368 					return pos;
       
  1369 
       
  1370 			case Symbols.TokenDO:
       
  1371 				// align the WHILE position with its do
       
  1372 				return fPosition;
       
  1373 
       
  1374 			case Symbols.TokenWHILE:
       
  1375 				// this one is tricky: while can be the start of a while loop
       
  1376 				// or the end of a do - while
       
  1377 				pos= fPosition;
       
  1378 				if (hasMatchingDo()) {
       
  1379 					// continue searching from the DO on
       
  1380 					break;
       
  1381 				} else {
       
  1382 					// continue searching from the WHILE on
       
  1383 					fPosition= pos;
       
  1384 					break;
       
  1385 				}
       
  1386 			case Symbols.TokenIDENT:
       
  1387 				if (mayBeMethodBody == READ_PARENS)
       
  1388 					mayBeMethodBody= READ_IDENT;
       
  1389 				break;
       
  1390 
       
  1391 			default:
       
  1392 				// keep searching
       
  1393 			}
       
  1394 		}
       
  1395 	}
       
  1396 
       
  1397 	private int getBlockIndent(boolean isMethodBody, boolean isTypeBody) {
       
  1398 		if (isTypeBody) {
       
  1399 			return fPrefs.prefTypeIndent + fPrefs.prefAccessSpecifierIndent;
       
  1400 		} else if (isMethodBody) {
       
  1401 			return fPrefs.prefMethodBodyIndent + (fPrefs.prefIndentBracesForMethods ? 1 : 0);
       
  1402 		} else {
       
  1403 			return fIndent;
       
  1404 		}
       
  1405 	}
       
  1406 
       
  1407 	/**
       
  1408 	 * Returns <code>true</code> if the colon at the current position is part of a conditional
       
  1409 	 * (ternary) expression, <code>false</code> otherwise.
       
  1410 	 *
       
  1411 	 * @return <code>true</code> if the colon at the current position is part of a conditional
       
  1412 	 */
       
  1413 	private boolean isConditional() {
       
  1414 		while (true) {
       
  1415 			int previous= fToken;
       
  1416 			nextToken();
       
  1417 			switch (fToken) {
       
  1418 			// search for case labels, which consist of (possibly qualified) identifiers or numbers
       
  1419 			case Symbols.TokenIDENT:
       
  1420 				if (previous == Symbols.TokenIDENT) {
       
  1421 					return false;
       
  1422 				}
       
  1423 				// fall thru
       
  1424 				continue;
       
  1425 			case Symbols.TokenDOUBLECOLON:
       
  1426 			case Symbols.TokenOTHER:
       
  1427 				continue;
       
  1428 				
       
  1429 			case Symbols.TokenQUESTIONMARK:
       
  1430 				return true;
       
  1431 				
       
  1432 			case Symbols.TokenSEMICOLON:
       
  1433 			case Symbols.TokenLBRACE:
       
  1434 			case Symbols.TokenRBRACE:
       
  1435 			case Symbols.TokenCASE:
       
  1436 			case Symbols.TokenDEFAULT:
       
  1437 			case Symbols.TokenPUBLIC:
       
  1438 			case Symbols.TokenPROTECTED:
       
  1439 			case Symbols.TokenPRIVATE:
       
  1440 			case Symbols.TokenCLASS:
       
  1441 			case Symbols.TokenSTRUCT:
       
  1442 			case Symbols.TokenUNION:
       
  1443 				return false;
       
  1444 
       
  1445 			default:
       
  1446 				return true;
       
  1447 			}
       
  1448 		}
       
  1449 	}
       
  1450 
       
  1451 	/**
       
  1452 	 * Returns as a reference any previous <code>switch</code> labels (<code>case</code>
       
  1453 	 * or <code>default</code>) or the offset of the brace that scopes the switch
       
  1454 	 * statement. Sets <code>fIndent</code> to <code>prefCaseIndent</code> upon
       
  1455 	 * a match.
       
  1456 	 *
       
  1457 	 * @return the reference offset for a <code>switch</code> label
       
  1458 	 */
       
  1459 	private int matchCaseAlignment() {
       
  1460 		while (true) {
       
  1461 			nextToken();
       
  1462 			switch (fToken) {
       
  1463 			// invalid cases: another case label or an LBRACE must come before a case
       
  1464 			// -> bail out with the current position
       
  1465 			case Symbols.TokenLPAREN:
       
  1466 			case Symbols.TokenLBRACKET:
       
  1467 			case Symbols.TokenEOF:
       
  1468 				return fPosition;
       
  1469 				
       
  1470 			case Symbols.TokenSWITCH:
       
  1471 				// start of switch statement
       
  1472 				fIndent= fPrefs.prefCaseIndent;
       
  1473 				return fPosition;
       
  1474 				
       
  1475 			case Symbols.TokenCASE:
       
  1476 			case Symbols.TokenDEFAULT:
       
  1477 				// align with previous label
       
  1478 				fIndent= 0;
       
  1479 				return fPosition;
       
  1480 
       
  1481 			// scopes: skip them
       
  1482 			case Symbols.TokenRPAREN:
       
  1483 			case Symbols.TokenRBRACKET:
       
  1484 			case Symbols.TokenRBRACE:
       
  1485 				skipScope();
       
  1486 				break;
       
  1487 
       
  1488 			default:
       
  1489 				// keep searching
       
  1490 				continue;
       
  1491 			}
       
  1492 		}
       
  1493 	}
       
  1494 
       
  1495 	/**
       
  1496 	 * Returns as a reference any previous access specifiers (<code>public</code>,
       
  1497 	 * <code>protected</code> or <code>default</code>) or the offset of the brace that
       
  1498 	 * scopes the class body.
       
  1499 	 * Sets <code>fIndent</code> to <code>prefAccessSpecifierIndent</code> upon
       
  1500 	 * a match.
       
  1501 	 *
       
  1502 	 * @return the reference offset for an access specifier (public/protected/private)
       
  1503 	 */
       
  1504 	private int matchAccessSpecifierAlignment() {
       
  1505 		while (true) {
       
  1506 			nextToken();
       
  1507 			switch (fToken) {
       
  1508 			// invalid cases: another access specifier or an LBRACE must come before an access specifier
       
  1509 			// -> bail out with the current position
       
  1510 			case Symbols.TokenLPAREN:
       
  1511 			case Symbols.TokenLBRACKET:
       
  1512 			case Symbols.TokenEOF:
       
  1513 				return fPosition;
       
  1514 				
       
  1515 			case Symbols.TokenLBRACE:
       
  1516 				// opening brace of class body
       
  1517 				int pos= fPosition;
       
  1518 				int typeDeclPos= matchTypeDeclaration();
       
  1519 				fIndent= fPrefs.prefAccessSpecifierIndent;
       
  1520 				fExtraSpaces = fPrefs.prefAccessSpecifierExtraSpaces;
       
  1521 				if (typeDeclPos != CHeuristicScanner.NOT_FOUND) {
       
  1522 					return typeDeclPos;
       
  1523 				}
       
  1524 				return pos;
       
  1525 			case Symbols.TokenPUBLIC:
       
  1526 			case Symbols.TokenPROTECTED:
       
  1527 			case Symbols.TokenPRIVATE:
       
  1528 				// align with previous access specifier
       
  1529 				fIndent= 0;
       
  1530 				return fPosition;
       
  1531 
       
  1532 			// scopes: skip them
       
  1533 			case Symbols.TokenRPAREN:
       
  1534 			case Symbols.TokenRBRACKET:
       
  1535 			case Symbols.TokenRBRACE:
       
  1536 				skipScope();
       
  1537 				break;
       
  1538 
       
  1539 			default:
       
  1540 				// keep searching
       
  1541 				continue;
       
  1542 			}
       
  1543 		}
       
  1544 	}
       
  1545 
       
  1546 	/**
       
  1547 	 * Returns the reference position for a list element. The algorithm
       
  1548 	 * tries to match any previous indentation on the same list. If there is none,
       
  1549 	 * the reference position returned is determined depending on the type of list:
       
  1550 	 * The indentation will either match the list scope introducer (e.g. for
       
  1551 	 * method declarations), so called deep indents, or simply increase the
       
  1552 	 * indentation by a number of standard indents. See also {@link #handleScopeIntroduction(int, boolean)}.
       
  1553 	 * @return the reference position for a list item: either a previous list item
       
  1554 	 * that has its own indentation, or the list introduction start.
       
  1555 	 */
       
  1556 	private int skipToPreviousListItemOrListStart() {
       
  1557 		int startLine= fLine;
       
  1558 		int startPosition= fPosition;
       
  1559 		boolean seenEqual = fToken == Symbols.TokenEQUAL;
       
  1560 		boolean seenShiftLeft = fToken == Symbols.TokenSHIFTLEFT;
       
  1561 		boolean seenRightParen = fToken == Symbols.TokenRPAREN;
       
  1562 		while (true) {
       
  1563 			nextToken();
       
  1564 
       
  1565 			// If any line item comes with its own indentation, adapt to it
       
  1566 			if (fLine < startLine) {
       
  1567 				try {
       
  1568 					int lineOffset= fDocument.getLineOffset(startLine);
       
  1569 					int bound= Math.min(fDocument.getLength(), startPosition + 1);
       
  1570 					if ((fToken == Symbols.TokenSEMICOLON || fToken == Symbols.TokenRBRACE ||
       
  1571 							fToken == Symbols.TokenLBRACE && !looksLikeArrayInitializerIntro()) &&
       
  1572 							(seenEqual || seenShiftLeft || seenRightParen)) {
       
  1573 						fIndent = fPrefs.prefContinuationIndent;
       
  1574 					} else {
       
  1575 						fAlign= fScanner.findNonWhitespaceForwardInAnyPartition(lineOffset, bound);
       
  1576 						// If the reference line starts with a colon, skip the colon.  
       
  1577 						if (peekToken(fAlign) == Symbols.TokenCOLON) {
       
  1578 							fAlign= fScanner.findNonWhitespaceForwardInAnyPartition(fAlign + 1, bound);
       
  1579 						}
       
  1580 					}
       
  1581 				} catch (BadLocationException e) {
       
  1582 					// Ignore and return just the position
       
  1583 				}
       
  1584 				return startPosition;
       
  1585 			}
       
  1586 
       
  1587 			switch (fToken) {
       
  1588 			// scopes: skip them
       
  1589 			case Symbols.TokenRPAREN:
       
  1590 				seenRightParen = true;
       
  1591 				//$FALL-THROUGH$
       
  1592 			case Symbols.TokenRBRACKET:
       
  1593 			case Symbols.TokenRBRACE:
       
  1594 				skipScope();
       
  1595 				break;
       
  1596 
       
  1597 			// scope introduction: special treat who special is
       
  1598 			case Symbols.TokenLPAREN:
       
  1599 			case Symbols.TokenLBRACE:
       
  1600 			case Symbols.TokenLBRACKET:
       
  1601 				return handleScopeIntroduction(startPosition + 1, false);
       
  1602 
       
  1603 			case Symbols.TokenSEMICOLON:
       
  1604 				return fPosition;
       
  1605 
       
  1606 			case Symbols.TokenQUESTIONMARK:
       
  1607 				if (fPrefs.prefTernaryDeepAlign) {
       
  1608 					setFirstElementAlignment(fPosition - 1, fPosition + 1);
       
  1609 				} else {
       
  1610 					fIndent= fPrefs.prefTernaryIndent;
       
  1611 				}
       
  1612 				return fPosition;
       
  1613 
       
  1614 			case Symbols.TokenEQUAL:
       
  1615 				seenEqual = true;
       
  1616 				break;
       
  1617 
       
  1618 			case Symbols.TokenSHIFTLEFT:
       
  1619 				seenShiftLeft = true;
       
  1620 				break;
       
  1621 
       
  1622 			case Symbols.TokenEOF:
       
  1623 				if (seenEqual || seenShiftLeft || seenRightParen) {
       
  1624 					fIndent = fPrefs.prefContinuationIndent;
       
  1625 				}
       
  1626 				return 0;
       
  1627 			}
       
  1628 		}
       
  1629 	}
       
  1630 
       
  1631 	/**
       
  1632 	 * Skips a scope and positions the cursor (<code>fPosition</code>) on the
       
  1633 	 * token that opens the scope. Returns <code>true</code> if a matching peer
       
  1634 	 * could be found, <code>false</code> otherwise. The current token when calling
       
  1635 	 * must be one out of <code>Symbols.TokenRPAREN</code>, <code>Symbols.TokenRBRACE</code>,
       
  1636 	 * and <code>Symbols.TokenRBRACKET</code>.
       
  1637 	 *
       
  1638 	 * @return <code>true</code> if a matching peer was found, <code>false</code> otherwise
       
  1639 	 */
       
  1640 	private boolean skipScope() {
       
  1641 		switch (fToken) {
       
  1642 		case Symbols.TokenRPAREN:
       
  1643 			return skipScope(Symbols.TokenLPAREN, Symbols.TokenRPAREN);
       
  1644 		case Symbols.TokenRBRACKET:
       
  1645 			return skipScope(Symbols.TokenLBRACKET, Symbols.TokenRBRACKET);
       
  1646 		case Symbols.TokenRBRACE:
       
  1647 			return skipScope(Symbols.TokenLBRACE, Symbols.TokenRBRACE);
       
  1648 		case Symbols.TokenGREATERTHAN:
       
  1649 			if (!fPrefs.prefHasTemplates)
       
  1650 				return false;
       
  1651 			int storedPosition= fPosition;
       
  1652 			int storedToken= fToken;
       
  1653 			nextToken();
       
  1654 			switch (fToken) {
       
  1655 				case Symbols.TokenIDENT:
       
  1656 					if (skipScope(Symbols.TokenLESSTHAN, Symbols.TokenGREATERTHAN))
       
  1657 						return true;
       
  1658 					break;
       
  1659 				case Symbols.TokenQUESTIONMARK:
       
  1660 				case Symbols.TokenGREATERTHAN:
       
  1661 					if (skipScope(Symbols.TokenLESSTHAN, Symbols.TokenGREATERTHAN))
       
  1662 						return true;
       
  1663 					break;
       
  1664 			}
       
  1665 			// <> are harder to detect - restore the position if we fail
       
  1666 			fPosition= storedPosition;
       
  1667 			fToken= storedToken;
       
  1668 			return false;
       
  1669 
       
  1670 		default:
       
  1671 			 // programming error
       
  1672 			Assert.isTrue(false);
       
  1673 			return false;
       
  1674 		}
       
  1675 	}
       
  1676 
       
  1677 	/**
       
  1678 	 * Returns the contents of the current token.
       
  1679 	 *
       
  1680 	 * @return the contents of the current token
       
  1681 	 */
       
  1682 	private CharSequence getTokenContent() {
       
  1683 		return new DocumentCharacterIterator(fDocument, fPosition, fPreviousPos);
       
  1684 	}
       
  1685 
       
  1686 	/**
       
  1687 	 * Handles the introduction of a new scope. The current token must be one out
       
  1688 	 * of <code>Symbols.TokenLPAREN</code>, <code>Symbols.TokenLBRACE</code>,
       
  1689 	 * and <code>Symbols.TokenLBRACKET</code>. Returns as the reference position
       
  1690 	 * either the token introducing the scope or - if available - the first
       
  1691 	 * token after that.
       
  1692 	 *
       
  1693 	 * <p>Depending on the type of scope introduction, the indentation will align
       
  1694 	 * (deep indenting) with the reference position (<code>fAlign</code> will be
       
  1695 	 * set to the reference position) or <code>fIndent</code> will be set to
       
  1696 	 * the number of indentation units.
       
  1697 	 * </p>
       
  1698 	 *
       
  1699 	 * @param bound the bound for the search for the first token after the scope
       
  1700 	 * introduction.
       
  1701 	 * @param firstToken <code>true</code> if we are dealing with the first token after
       
  1702 	 * the opening parenthesis.
       
  1703 	 * @return the indent
       
  1704 	 */
       
  1705 	private int handleScopeIntroduction(int bound, boolean firstToken) {
       
  1706 		int pos= fPosition; // store
       
  1707 
       
  1708 		switch (fToken) {
       
  1709 		// scope introduction: special treat who special is
       
  1710 		case Symbols.TokenLPAREN:
       
  1711 			// special: method declaration deep indentation
       
  1712 			if (looksLikeMethodDecl()) {
       
  1713 				if (firstToken ? fPrefs.prefMethodDeclFirstParameterDeepIndent : fPrefs.prefMethodDeclDeepIndent) {
       
  1714 					return setFirstElementAlignment(pos, bound);
       
  1715 				} else {
       
  1716 					fIndent= fPrefs.prefMethodDeclIndent;
       
  1717 					return pos;
       
  1718 				}
       
  1719 			} else {
       
  1720 				fPosition= pos;
       
  1721 				if (looksLikeMethodCall()) {
       
  1722 					if (firstToken ? fPrefs.prefMethodCallFirstParameterDeepIndent : fPrefs.prefMethodCallDeepIndent) {
       
  1723 						return setFirstElementAlignment(pos, bound);
       
  1724 					} else {
       
  1725 						fIndent= fPrefs.prefMethodCallIndent;
       
  1726 						return pos;
       
  1727 					}
       
  1728 				} else if (fPrefs.prefParenthesisDeepIndent) {
       
  1729 					return setFirstElementAlignment(pos, bound);
       
  1730 				}
       
  1731 			}
       
  1732 
       
  1733 			// normal: return the parenthesis as reference
       
  1734 			fIndent= fPrefs.prefParenthesisIndent;
       
  1735 			return pos;
       
  1736 
       
  1737 		case Symbols.TokenLBRACE:
       
  1738 			final boolean looksLikeArrayInitializerIntro= looksLikeArrayInitializerIntro();
       
  1739 			// special: array initializer
       
  1740 			if (looksLikeArrayInitializerIntro) {
       
  1741 				if (fPrefs.prefArrayDeepIndent)
       
  1742 					return setFirstElementAlignment(pos, bound);
       
  1743 				else
       
  1744 					fIndent= fPrefs.prefArrayIndent;
       
  1745 			} else if (isNamespace()) {
       
  1746 				fIndent= fPrefs.prefNamespaceBodyIndent;
       
  1747 			} else {
       
  1748 				int typeDeclPos = matchTypeDeclaration();
       
  1749 				if (typeDeclPos == CHeuristicScanner.NOT_FOUND) {
       
  1750 					fIndent= fPrefs.prefBlockIndent;
       
  1751 				} else {
       
  1752 					fIndent= fPrefs.prefAccessSpecifierIndent + fPrefs.prefTypeIndent;
       
  1753 				}
       
  1754 			}
       
  1755 
       
  1756 			// normal: skip to the statement start before the scope introducer
       
  1757 			// opening braces are often on differently ending indents than e.g. a method definition
       
  1758 			if (!looksLikeArrayInitializerIntro) {
       
  1759 				fPosition= pos; // restore
       
  1760 				return skipToStatementStart(true, true); // set to true to match the first if
       
  1761 			} else {
       
  1762 				return pos;
       
  1763 			}
       
  1764 
       
  1765 		case Symbols.TokenLBRACKET:
       
  1766 			// special: method declaration deep indentation
       
  1767 			if (fPrefs.prefArrayDimensionsDeepIndent) {
       
  1768 				return setFirstElementAlignment(pos, bound);
       
  1769 			}
       
  1770 
       
  1771 			// normal: return the bracket as reference
       
  1772 			fIndent= fPrefs.prefBracketIndent;
       
  1773 			return pos; // restore
       
  1774 
       
  1775 		default:
       
  1776 			// programming error
       
  1777 			Assert.isTrue(false);
       
  1778 			return -1; // dummy
       
  1779 		}
       
  1780 	}
       
  1781 
       
  1782 	/**
       
  1783 	 * Sets the deep indent offset (<code>fAlign</code>) to either the offset
       
  1784 	 * right after <code>scopeIntroducerOffset</code> or - if available - the
       
  1785 	 * first C token after <code>scopeIntroducerOffset</code>, but before
       
  1786 	 * <code>bound</code>.
       
  1787 	 *
       
  1788 	 * @param scopeIntroducerOffset the offset of the scope introducer
       
  1789 	 * @param bound the bound for the search for another element
       
  1790 	 * @return the reference position
       
  1791 	 */
       
  1792 	private int setFirstElementAlignment(int scopeIntroducerOffset, int bound) {
       
  1793 		int firstPossible= scopeIntroducerOffset + 1; // align with the first position after the scope intro
       
  1794 		fAlign= fScanner.findNonWhitespaceForwardInAnyPartition(firstPossible, bound);
       
  1795 		if (fAlign == CHeuristicScanner.NOT_FOUND) {
       
  1796 			fAlign= firstPossible;
       
  1797 		} else {
       
  1798 			try {
       
  1799 				IRegion lineRegion = fDocument.getLineInformationOfOffset(scopeIntroducerOffset);
       
  1800 				if (fAlign > lineRegion.getOffset() + lineRegion.getLength()) {
       
  1801 					fAlign= firstPossible; 
       
  1802 				}
       
  1803 			} catch (BadLocationException e) {
       
  1804 				// Ignore.
       
  1805 			}
       
  1806 		}
       
  1807 		return fAlign;
       
  1808 	}
       
  1809 
       
  1810 	/**
       
  1811 	 * Returns <code>true</code> if the next token received after calling
       
  1812 	 * <code>nextToken</code> is either an equal sign, an opening brace,
       
  1813 	 * a comma or an array designator ('[]').
       
  1814 	 *
       
  1815 	 * @return <code>true</code> if the next elements look like the start of an array definition
       
  1816 	 */
       
  1817 	private boolean looksLikeArrayInitializerIntro() {
       
  1818 		int pos= fPosition;
       
  1819 		nextToken();
       
  1820 		switch (fToken) {
       
  1821 		case Symbols.TokenEQUAL:
       
  1822 			return true;
       
  1823 		case Symbols.TokenRBRACKET:
       
  1824 			return skipBrackets();
       
  1825 		case Symbols.TokenLBRACE:
       
  1826 			if (looksLikeArrayInitializerIntro()) {
       
  1827 				fPosition= pos;
       
  1828 				return true;
       
  1829 			}
       
  1830 			return false;
       
  1831 		case Symbols.TokenCOMMA:
       
  1832 			fPosition= pos;
       
  1833 			return true;
       
  1834 		}
       
  1835 		return false;
       
  1836 	}
       
  1837 
       
  1838 	/**
       
  1839 	 * Returns <code>true</code> if the the current token is "namespace", or the current token
       
  1840 	 * is an identifier and the previous token is "namespace".
       
  1841 	 *
       
  1842 	 * @return <code>true</code> if the next elements look like the start of a namespace declaration.
       
  1843 	 */
       
  1844 	private boolean isNamespace() {
       
  1845 		int pos = fPosition;
       
  1846 		if (fToken == Symbols.TokenNAMESPACE) {
       
  1847 			return true;		// Anonymous namespace
       
  1848 		} else if (fToken == Symbols.TokenIDENT) {
       
  1849 			nextToken();		// Get previous token
       
  1850 			if (fToken == Symbols.TokenNAMESPACE) {
       
  1851 				return true;	// Named namespace
       
  1852 			}
       
  1853 		}
       
  1854 		fPosition = pos;
       
  1855 		return false;
       
  1856 	}
       
  1857 
       
  1858 	/**
       
  1859 	 * Skips over the next <code>if</code> keyword. The current token when calling
       
  1860 	 * this method must be an <code>else</code> keyword. Returns <code>true</code>
       
  1861 	 * if a matching <code>if</code> could be found, <code>false</code> otherwise.
       
  1862 	 * The cursor (<code>fPosition</code>) is set to the offset of the <code>if</code>
       
  1863 	 * token.
       
  1864 	 *
       
  1865 	 * @return <code>true</code> if a matching <code>if</code> token was found, <code>false</code> otherwise
       
  1866 	 */
       
  1867 	private boolean skipNextIF() {
       
  1868 		Assert.isTrue(fToken == Symbols.TokenELSE);
       
  1869 
       
  1870 		while (true) {
       
  1871 			nextToken();
       
  1872 			switch (fToken) {
       
  1873 			// scopes: skip them
       
  1874 			case Symbols.TokenRPAREN:
       
  1875 			case Symbols.TokenRBRACKET:
       
  1876 			case Symbols.TokenRBRACE:
       
  1877 				skipScope();
       
  1878 				break;
       
  1879 
       
  1880 			case Symbols.TokenIF:
       
  1881 				// found it, return
       
  1882 				return true;
       
  1883 			case Symbols.TokenELSE:
       
  1884 				// recursively skip else-if blocks
       
  1885 				skipNextIF();
       
  1886 				break;
       
  1887 
       
  1888 			// shortcut scope starts
       
  1889 			case Symbols.TokenLPAREN:
       
  1890 			case Symbols.TokenLBRACE:
       
  1891 			case Symbols.TokenLBRACKET:
       
  1892 			case Symbols.TokenEOF:
       
  1893 				return false;
       
  1894 			}
       
  1895 		}
       
  1896 	}
       
  1897 
       
  1898 	/**
       
  1899 	 * while(condition); is ambiguous when parsed backwardly, as it is a valid
       
  1900 	 * statement by its own, so we have to check whether there is a matching
       
  1901 	 * do. A <code>do</code> can either be separated from the while by a
       
  1902 	 * block, or by a single statement, which limits our search distance.
       
  1903 	 *
       
  1904 	 * @return <code>true</code> if the <code>while</code> currently in
       
  1905 	 *         <code>fToken</code> has a matching <code>do</code>.
       
  1906 	 */
       
  1907 	private boolean hasMatchingDo() {
       
  1908 		Assert.isTrue(fToken == Symbols.TokenWHILE);
       
  1909 		nextToken();
       
  1910 		switch (fToken) {
       
  1911 		case Symbols.TokenRBRACE:
       
  1912 			skipScope(); // and fall thru
       
  1913 			skipToStatementStart(false, false);
       
  1914 			return fToken == Symbols.TokenDO;
       
  1915 
       
  1916 		case Symbols.TokenSEMICOLON:
       
  1917 			skipToStatementStart(false, false);
       
  1918 			return fToken == Symbols.TokenDO;
       
  1919 		}
       
  1920 		return false;
       
  1921 	}
       
  1922 
       
  1923 	/**
       
  1924 	 * Skips pointer operators if the current token is a pointer operator.
       
  1925 	 *
       
  1926 	 * @return <code>true</code> if a <code>*</code> or <code>&amp;</code> could be scanned, the
       
  1927 	 *         current token is left at the operator.
       
  1928 	 */
       
  1929 	private boolean skipPointerOperators() {
       
  1930 		if (fToken == Symbols.TokenOTHER) {
       
  1931 			CharSequence token= getTokenContent();
       
  1932 			if (token.length() == 1 && token.charAt(0) == '*' || token.charAt(0) == '&') {
       
  1933 				return true;
       
  1934 			}
       
  1935 		} else if (fToken == Symbols.TokenCONST) {
       
  1936 			return true;
       
  1937 		}
       
  1938 		return false;
       
  1939 	}
       
  1940 
       
  1941 	/**
       
  1942 	 * Skips brackets if the current token is a RBRACKET. There can be nothing
       
  1943 	 * but whitespace in between, this is only to be used for <code>[]</code> elements.
       
  1944 	 *
       
  1945 	 * @return <code>true</code> if a <code>[]</code> could be scanned, the
       
  1946 	 *         current token is left at the LBRACKET.
       
  1947 	 */
       
  1948 	private boolean skipBrackets() {
       
  1949 		if (fToken == Symbols.TokenRBRACKET) {
       
  1950 			nextToken();
       
  1951 			if (fToken == Symbols.TokenLBRACKET) {
       
  1952 				return true;
       
  1953 			}
       
  1954 		}
       
  1955 		return false;
       
  1956 	}
       
  1957 
       
  1958 	/**
       
  1959 	 * Skips scope qualifiers of identifiers.
       
  1960 	 * 
       
  1961 	 * @return <code>true</code> if a qualifier was encountered, the last token
       
  1962 	 *         will be an IDENT.
       
  1963 	 */
       
  1964 	private boolean skipQualifiers() {
       
  1965 		if (fToken == Symbols.TokenDOUBLECOLON) {
       
  1966 			nextToken();
       
  1967 			if (fToken == Symbols.TokenIDENT) {
       
  1968 				return true;
       
  1969 			}
       
  1970 		}
       
  1971 		return false;
       
  1972 	}
       
  1973 
       
  1974 	/**
       
  1975 	 * Reads the next token in backward direction from the heuristic scanner
       
  1976 	 * and sets the fields <code>fToken, fPreviousPosition</code> and <code>fPosition</code>
       
  1977 	 * accordingly.
       
  1978 	 */
       
  1979 	private void nextToken() {
       
  1980 		nextToken(fPosition);
       
  1981 	}
       
  1982 
       
  1983 	/**
       
  1984 	 * Reads the next token in backward direction of <code>start</code> from
       
  1985 	 * the heuristic scanner and sets the fields <code>fToken, fPreviousPosition</code>
       
  1986 	 * and <code>fPosition</code> accordingly.
       
  1987 	 *
       
  1988 	 * @param start the start offset from which to scan backwards
       
  1989 	 */
       
  1990 	private void nextToken(int start) {
       
  1991 		fToken= fScanner.previousToken(start - 1, CHeuristicScanner.UNBOUND);
       
  1992 		fPreviousPos= start;
       
  1993 		fPosition= fScanner.getPosition() + 1;
       
  1994 		try {
       
  1995 			fLine= fDocument.getLineOfOffset(fPosition);
       
  1996 		} catch (BadLocationException e) {
       
  1997 			fLine= -1;
       
  1998 		}
       
  1999 	}
       
  2000 
       
  2001 	/**
       
  2002 	 * Reads the next token in backward direction from the heuristic scanner
       
  2003 	 * and returns that token without changing the current position.
       
  2004 	 */
       
  2005 	private int peekToken() {
       
  2006 		return fScanner.previousToken(fPosition - 1, CHeuristicScanner.UNBOUND);
       
  2007 	}
       
  2008 
       
  2009 	/**
       
  2010 	 * Returns <code>true</code> if the current tokens look like a method
       
  2011 	 * declaration header (i.e. only the return type and method name). The
       
  2012 	 * heuristic calls <code>nextToken</code> and expects an identifier
       
  2013 	 * (method name) and an optional return type declaration.
       
  2014 	 *
       
  2015 	 * @return <code>true</code> if the current position looks like a method
       
  2016 	 *         declaration header.
       
  2017 	 */
       
  2018 	private boolean looksLikeMethodDecl() {
       
  2019 		nextToken();
       
  2020 		switch (fToken) {
       
  2021 		case Symbols.TokenIDENT: // method name
       
  2022 			int pos= fPosition;
       
  2023 			nextToken();
       
  2024 			// check destructor tilde
       
  2025 			if (fToken == Symbols.TokenTILDE) {
       
  2026 				return true;
       
  2027 			}
       
  2028 			if (skipQualifiers()) {
       
  2029 				return true;
       
  2030 			}
       
  2031 			// optional brackets for array valued return types
       
  2032 			while (skipBrackets()) {
       
  2033 				nextToken();
       
  2034 			}
       
  2035 			while (skipPointerOperators()) {
       
  2036 				nextToken();
       
  2037 			}
       
  2038 			switch (fToken) {
       
  2039 			case Symbols.TokenIDENT:
       
  2040 				return true;
       
  2041 			case Symbols.TokenSEMICOLON:
       
  2042 			case Symbols.TokenRBRACE:
       
  2043 				fPosition= pos;
       
  2044 				return true;
       
  2045 			case Symbols.TokenLBRACE:
       
  2046 				if (fScanner.looksLikeCompositeTypeDefinitionBackward(fPosition, CHeuristicScanner.UNBOUND)) {
       
  2047 					fPosition= pos;
       
  2048 					return true;
       
  2049 				}
       
  2050 				break;
       
  2051 			case Symbols.TokenCOLON:
       
  2052 				nextToken();
       
  2053 				switch (fToken) {
       
  2054 				case Symbols.TokenPUBLIC:
       
  2055 				case Symbols.TokenPROTECTED:
       
  2056 				case Symbols.TokenPRIVATE:
       
  2057 					fPosition= pos;
       
  2058 					return true;
       
  2059 				case Symbols.TokenRPAREN:
       
  2060 					// constructor initializer
       
  2061 					if (skipScope()) {
       
  2062 						nextToken();
       
  2063 						// optional throw
       
  2064 						if (fToken == Symbols.TokenTHROW) {
       
  2065 							nextToken();
       
  2066 							if (fToken != Symbols.TokenRPAREN || !skipScope()) {
       
  2067 								return false;
       
  2068 							}
       
  2069 						}
       
  2070 						return looksLikeMethodDecl();
       
  2071 					}
       
  2072 					break;
       
  2073 				}
       
  2074 			}
       
  2075 			break;
       
  2076 		case Symbols.TokenARROW:
       
  2077 		case Symbols.TokenCOMMA:
       
  2078 		case Symbols.TokenEQUAL:
       
  2079 		case Symbols.TokenGREATERTHAN:
       
  2080 		case Symbols.TokenLESSTHAN:
       
  2081 		case Symbols.TokenMINUS:
       
  2082 		case Symbols.TokenSHIFTRIGHT:
       
  2083 		case Symbols.TokenSHIFTLEFT:
       
  2084 		case Symbols.TokenDELETE:
       
  2085 		case Symbols.TokenNEW:
       
  2086 			nextToken();
       
  2087 			return fToken == Symbols.TokenOPERATOR;
       
  2088 		case Symbols.TokenOTHER:
       
  2089 			if (getTokenContent().length() == 1) {
       
  2090 				nextToken();
       
  2091 				if (fToken == Symbols.TokenOPERATOR)
       
  2092 					return true;
       
  2093 			}
       
  2094 			if (getTokenContent().length() == 1) {
       
  2095 				nextToken();
       
  2096 				if (fToken == Symbols.TokenOPERATOR)
       
  2097 					return true;
       
  2098 			}
       
  2099 		}
       
  2100 		return false;
       
  2101 	}
       
  2102 
       
  2103 	/**
       
  2104 	 * Returns <code>true</code> if the current tokens look like an anonymous type declaration
       
  2105 	 * header (i.e. a type name (potentially qualified) and a new keyword). The heuristic calls
       
  2106 	 * <code>nextToken</code> and expects a possibly qualified identifier (type name) and a new
       
  2107 	 * keyword
       
  2108 	 * 
       
  2109 	 * @return <code>true</code> if the current position looks like a anonymous type declaration
       
  2110 	 *         header.
       
  2111 	 */
       
  2112 	private boolean looksLikeAnonymousTypeDecl() {
       
  2113 		nextToken();
       
  2114 		if (fToken == Symbols.TokenIDENT) { // type name
       
  2115 			nextToken();
       
  2116 			while (fToken == Symbols.TokenOTHER) { // dot of qualification
       
  2117 				nextToken();
       
  2118 				if (fToken != Symbols.TokenIDENT) // qualificating name
       
  2119 					return false;
       
  2120 				nextToken();
       
  2121 			}
       
  2122 			return fToken == Symbols.TokenNEW;
       
  2123 		}
       
  2124 		return false;
       
  2125 	}
       
  2126 
       
  2127 	/**
       
  2128 	 * Returns <code>true</code> if the current tokens look like a method
       
  2129 	 * call header (i.e. an identifier as opposed to a keyword taking parenthesized
       
  2130 	 * parameters such as <code>if</code>).
       
  2131 	 * <p>The heuristic calls <code>nextToken</code> and expects an identifier
       
  2132 	 * (method name).
       
  2133 	 *
       
  2134 	 * @return <code>true</code> if the current position looks like a method call
       
  2135 	 *         header.
       
  2136 	 */
       
  2137 	private boolean looksLikeMethodCall() {
       
  2138 		// TODO add awareness for constructor calls with templates: new complex<float>()
       
  2139 		nextToken();
       
  2140 		return fToken == Symbols.TokenIDENT; // method name
       
  2141 	}
       
  2142 
       
  2143 	/**
       
  2144 	 * Scans tokens for the matching opening peer. The internal cursor
       
  2145 	 * (<code>fPosition</code>) is set to the offset of the opening peer if found.
       
  2146 	 *
       
  2147 	 * @param openToken the opening peer token
       
  2148 	 * @param closeToken the closing peer token
       
  2149 	 * @return <code>true</code> if a matching token was found, <code>false</code>
       
  2150 	 *         otherwise
       
  2151 	 */
       
  2152 	private boolean skipScope(int openToken, int closeToken) {
       
  2153 		int depth= 1;
       
  2154 
       
  2155 		while (true) {
       
  2156 			nextToken();
       
  2157 
       
  2158 			if (fToken == closeToken) {
       
  2159 				depth++;
       
  2160 			} else if (fToken == openToken) {
       
  2161 				depth--;
       
  2162 				if (depth == 0)
       
  2163 					return true;
       
  2164 			} else if (fToken == Symbols.TokenEOF) {
       
  2165 				return false;
       
  2166 			}
       
  2167 		}
       
  2168 	}
       
  2169 }