From: khartlage Date: Thu, 20 Mar 2003 20:38:51 +0000 (+0000) Subject: php formatter based on the JDT java formatter (very early version) X-Git-Url: http://secure.phpeclipse.com php formatter based on the JDT java formatter (very early version) --- diff --git a/net.sourceforge.phpeclipse/plugin.xml b/net.sourceforge.phpeclipse/plugin.xml index ddafca2..0dd2df1 100644 --- a/net.sourceforge.phpeclipse/plugin.xml +++ b/net.sourceforge.phpeclipse/plugin.xml @@ -548,6 +548,12 @@ Temporarily replaced until errors can be ironed out... class="net.sourceforge.phpdt.internal.ui.preferences.TemplatePreferencePage" id="net.sourceforge.phpeclipse.preference.TemplatePreferencePage"> + + sourceString, + * and returns a string containing the formatted version. + * + * @param string the string to format + * @param indentationLevel the initial indentation level, used + * to shift left/right the entire source fragment. An initial indentation + * level of zero has no effect. + * @param positions an array of positions to map. These are + * character-based source positions inside the original source, + * for which corresponding positions in the formatted source will + * be computed (so as to relocate elements associated with the original + * source). It updates the positions array with updated positions. + * If set to null, then no positions are mapped. + * @param lineSeparator the line separator to use in formatted source, + * if set to null, then the platform default one will be used. + * @return the formatted output string. + */ + String format(String string, int indentationLevel, int[] positions, String lineSeparator); +} diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/core/ToolFactory.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/core/ToolFactory.java new file mode 100644 index 0000000..249dbec --- /dev/null +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/core/ToolFactory.java @@ -0,0 +1,164 @@ +/******************************************************************************* + * Copyright (c) 2002 International Business Machines Corp. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * IBM Corporation - added #createScanner allowing to make comment check stricter + ******************************************************************************/ +package net.sourceforge.phpdt.core; + +import java.util.Map; + +import net.sourceforge.phpeclipse.PHPCore; + +import org.eclipse.core.runtime.Plugin; +import org.phpeclipse.phpdt.internal.formatter.CodeFormatter; + +/** + * Factory for creating various compiler tools, such as scanners, parsers and compilers. + *

+ * This class provides static methods only; it is not intended to be instantiated or subclassed by clients. + *

+ * + * @since 2.0 + */ +public class ToolFactory { + + /** + * Create an instance of a code formatter. A code formatter implementation can be contributed via the + * extension point "org.phpeclipse.phpdt.core.codeFormatter". If unable to find a registered extension, the factory + * will default to using the default code formatter. + * + * @return an instance of a code formatter + * @see ICodeFormatter + * @see ToolFactory#createDefaultCodeFormatter(Map) + */ + public static ICodeFormatter createCodeFormatter(){ + + Plugin jdtCorePlugin = PHPCore.getPlugin(); + if (jdtCorePlugin == null) return null; + +// IExtensionPoint extension = jdtCorePlugin.getDescriptor().getExtensionPoint(JavaModelManager.FORMATTER_EXTPOINT_ID); +// if (extension != null) { +// IExtension[] extensions = extension.getExtensions(); +// for(int i = 0; i < extensions.length; i++){ +// IConfigurationElement [] configElements = extensions[i].getConfigurationElements(); +// for(int j = 0; j < configElements.length; j++){ +// try { +// Object execExt = configElements[j].createExecutableExtension("class"); //$NON-NLS-1$ +// if (execExt instanceof ICodeFormatter){ +// // use first contribution found +// return (ICodeFormatter)execExt; +// } +// } catch(CoreException e){ +// } +// } +// } +// } + // no proper contribution found, use default formatter + return createDefaultCodeFormatter(null); + } + + /** + * Create an instance of the built-in code formatter. A code formatter implementation can be contributed via the + * extension point "org.phpeclipse.phpdt.core.codeFormatter". If unable to find a registered extension, the factory will + * default to using the default code formatter. + * + * @param options - the options map to use for formatting with the default code formatter. Recognized options + * are documented on JavaCore#getDefaultOptions(). If set to null, then use + * the current settings from JavaCore#getOptions. + * @return an instance of the built-in code formatter + * @see ICodeFormatter + * @see ToolFactory#createCodeFormatter() + * @see JavaCore#getOptions() + */ + public static ICodeFormatter createDefaultCodeFormatter(Map options){ + + if (options == null) options = PHPCore.getOptions(); + return new CodeFormatter(options); + } + + /** + * Create a scanner, indicating the level of detail requested for tokenizing. The scanner can then be + * used to tokenize some source in a Java aware way. + * Here is a typical scanning loop: + * + * + *
+	 *   IScanner scanner = ToolFactory.createScanner(false, false, false, false);
+	 *   scanner.setSource("int i = 0;".toCharArray());
+	 *   while (true) {
+	 *     int token = scanner.getNextToken();
+	 *     if (token == ITerminalSymbols.TokenNameEOF) break;
+	 *     System.out.println(token + " : " + new String(scanner.getCurrentTokenSource()));
+	 *   }
+	 * 
+ *
+ * + *

+ * The returned scanner will tolerate unterminated line comments (missing line separator). It can be made stricter + * by using API with extra boolean parameter (strictCommentMode). + *

+ * @param tokenizeComments if set to false, comments will be silently consumed + * @param tokenizeWhiteSpace if set to false, white spaces will be silently consumed, + * @param assertKeyword if set to false, occurrences of 'assert' will be reported as identifiers + * (ITerminalSymbols#TokenNameIdentifier), whereas if set to true, it + * would report assert keywords (ITerminalSymbols#TokenNameassert). Java 1.4 has introduced + * a new 'assert' keyword. + * @param recordLineSeparator if set to true, the scanner will record positions of encountered line + * separator ends. In case of multi-character line separators, the last character position is considered. These positions + * can then be extracted using IScanner#getLineEnds. Only non-unicode escape sequences are + * considered as valid line separators. + * @return a scanner + * @see ToolFactory#createScanner(boolean,boolean,boolean,boolean, boolean) + * @see org.phpeclipse.phpdt.core.compiler.IScanner + */ +// public static IScanner createScanner(boolean tokenizeComments, boolean tokenizeWhiteSpace, boolean assertMode, boolean recordLineSeparator){ +// return createScanner(tokenizeComments, tokenizeWhiteSpace, assertMode, recordLineSeparator, false); +// } + + /** + * Create a scanner, indicating the level of detail requested for tokenizing. The scanner can then be + * used to tokenize some source in a Java aware way. + * Here is a typical scanning loop: + * + * + *

+	 *   IScanner scanner = ToolFactory.createScanner(false, false, false, false);
+	 *   scanner.setSource("int i = 0;".toCharArray());
+	 *   while (true) {
+	 *     int token = scanner.getNextToken();
+	 *     if (token == ITerminalSymbols.TokenNameEOF) break;
+	 *     System.out.println(token + " : " + new String(scanner.getCurrentTokenSource()));
+	 *   }
+	 * 
+ * + * + * @param tokenizeComments if set to false, comments will be silently consumed + * @param tokenizeWhiteSpace if set to false, white spaces will be silently consumed, + * @param assertMode if set to false, occurrences of 'assert' will be reported as identifiers + * (ITerminalSymbols#TokenNameIdentifier), whereas if set to true, it + * would report assert keywords (ITerminalSymbols#TokenNameassert). Java 1.4 has introduced + * a new 'assert' keyword. + * @param recordLineSeparator if set to true, the scanner will record positions of encountered line + * separator ends. In case of multi-character line separators, the last character position is considered. These positions + * can then be extracted using IScanner#getLineEnds. Only non-unicode escape sequences are + * considered as valid line separators. + * @param strictCommentMode if set to true, line comments with no trailing line separator will be + * treated as invalid tokens. + * @return a scanner + * + * @see org.phpeclipse.phpdt.core.compiler.IScanner + * @since 2.1 + */ +// public static IScanner createScanner(boolean tokenizeComments, boolean tokenizeWhiteSpace, boolean assertMode, boolean recordLineSeparator, boolean strictCommentMode){ +// +// PublicScanner scanner = new PublicScanner(tokenizeComments, tokenizeWhiteSpace, false/*nls*/, assertMode, strictCommentMode /*strict comment*/, null/*taskTags*/, null/*taskPriorities*/); +// scanner.recordLineSeparator = recordLineSeparator; +// return scanner; +// } +} diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/core/compiler/CharOperation.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/core/compiler/CharOperation.java new file mode 100644 index 0000000..ae0ed10 --- /dev/null +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/core/compiler/CharOperation.java @@ -0,0 +1,2528 @@ +/******************************************************************************* + * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v0.5 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v05.html + * + * Contributors: + * IBM Corporation - initial API and implementation + ******************************************************************************/ +package net.sourceforge.phpdt.core.compiler; + +/** + * This class is a collection of helper methods to manipulate char arrays. + * + * @since 2.1 + */ +public final class CharOperation { + + /** + * Constant for an empty char array + */ + public static final char[] NO_CHAR = new char[0]; + + /** + * Constant for an empty char array with two dimensions. + */ + public static final char[][] NO_CHAR_CHAR = new char[0][]; + + /** + * Answers a new array with appending the suffix character at the end of the array. + *
+ *
+ * For example:
+ *
    + *
  1. +	 *    array = { 'a', 'b' }
    +	 *    suffix = 'c'
    +	 *    => result = { 'a', 'b' , 'c' }
    +	 * 
    + *
  2. + *
  3. +	 *    array = null
    +	 *    suffix = 'c'
    +	 *    => result = { 'c' }
    +	 * 
  4. + *
+ * + * @param array the array that is concanated with the suffix character + * @param suffix the suffix character + * @return the new array + */ + public static final char[] append(char[] array, char suffix) { + if (array == null) + return new char[] { suffix }; + int length = array.length; + System.arraycopy(array, 0, array = new char[length + 1], 0, length); + array[length] = suffix; + return array; + } + /** + * Append the given subarray to append to the target array starting at the given index in the target array. + * The start of the subarray is inclusive, the end is exclusive. + * Answers a new target array if it needs to grow, otherwise answers the same target array. + *
+ * For example:
+ *
    + *
  1. +	 *    target = { 'a', 'b', -1 }
    +	 *    index = 0
    +	 *    array = { 'c', 'd' }
    +	 *    start = 0
    +	 *    end = 1
    +	 *    => result = { 'a', 'b' , 'c' }
    +	 * 
    + *
  2. + *
  3. +	 *    target = { 'a', 'b' }
    +	 *    index = 0
    +	 *    array = { 'c', 'd' }
    +	 *    start = 0
    +	 *    end = 1
    +	 *    => result = new { 'a', 'b' , 'c', -1 }
    +	 * 
  4. + *
  5. +	 *    target = { 'a', 'b', 'c' }
    +	 *    index = 1
    +	 *    array = { 'c', 'd', 'e', 'f' }
    +	 *    start = 1
    +	 *    end = 4
    +	 *    => result = new { 'a', 'd' , 'e', 'f', -1, -1 }
    +	 * 
  6. + *
+ */ + public static final char[] append(char[] target, int index, char[] array, int start, int end) { + int targetLength = target.length; + int subLength = end-start; + int newTargetLength = subLength+index; + if (newTargetLength > targetLength) { + System.arraycopy(target, 0, target = new char[newTargetLength*2], 0, index); + } + System.arraycopy(array, start, target, index, subLength); + return target; + } + + /** + * Answers the concatenation of the two arrays. It answers null if the two arrays are null. + * If the first array is null, then the second array is returned. + * If the second array is null, then the first array is returned. + *
+ *
+ * For example: + *
    + *
  1. +	 *    first = null
    +	 *    second = null
    +	 *    => result = null
    +	 * 
    + *
  2. + *
  3. +	 *    first = { { ' a' } }
    +	 *    second = null
    +	 *    => result = { { ' a' } }
    +	 * 
    + *
  4. + *
  5. +	 *    first = null
    +	 *    second = { { ' a' } }
    +	 *    => result = { { ' a' } }
    +	 * 
    + *
  6. + *
  7. +	 *    first = { { ' b' } }
    +	 *    second = { { ' a' } }
    +	 *    => result = { { ' b' }, { ' a' } }
    +	 * 
    + *
  8. + *
+ * + * @param first the first array to concatenate + * @param second the second array to concatenate + * @return the concatenation of the two arrays, or null if the two arrays are null. + */ + public static final char[][] arrayConcat(char[][] first, char[][] second) { + if (first == null) + return second; + if (second == null) + return first; + + int length1 = first.length; + int length2 = second.length; + char[][] result = new char[length1 + length2][]; + System.arraycopy(first, 0, result, 0, length1); + System.arraycopy(second, 0, result, length1, length2); + return result; + } + + /** + * Answers a new array adding the second array at the end of first array. + * It answers null if the first and second are null. + * If the first array is null, then a new array char[][] is created with second. + * If the second array is null, then the first array is returned. + *
+ *
+ * For example: + *
    + *
  1. +	 *    first = null
    +	 *    second = { 'a' }
    +	 *    => result = { { ' a' } }
    +	 * 
    + *
  2. +	 *    first = { { ' a' } }
    +	 *    second = null
    +	 *    => result = { { ' a' } }
    +	 * 
    + *
  3. + *
  4. +	 *    first = { { ' a' } }
    +	 *    second = { ' b' }
    +	 *    => result = { { ' a' } , { ' b' } }
    +	 * 
    + *
  5. + *
+ * + * @param first the first array to concatenate + * @param second the array to add at the end of the first array + * @return a new array adding the second array at the end of first array, or null if the two arrays are null. + */ + public static final char[][] arrayConcat(char[][] first, char[] second) { + if (second == null) + return first; + if (first == null) + return new char[][] { second }; + + int length = first.length; + char[][] result = new char[length + 1][]; + System.arraycopy(first, 0, result, 0, length); + result[length] = second; + return result; + } + + /** + * Answers the concatenation of the two arrays. It answers null if the two arrays are null. + * If the first array is null, then the second array is returned. + * If the second array is null, then the first array is returned. + *
+ *
+ * For example: + *
    + *
  1. +	 *    first = null
    +	 *    second = { 'a' }
    +	 *    => result = { ' a' }
    +	 * 
    + *
  2. + *
  3. +	 *    first = { ' a' }
    +	 *    second = null
    +	 *    => result = { ' a' }
    +	 * 
    + *
  4. + *
  5. +	 *    first = { ' a' }
    +	 *    second = { ' b' }
    +	 *    => result = { ' a' , ' b' }
    +	 * 
    + *
  6. + *
+ * + * @param first the first array to concatenate + * @param second the second array to concatenate + * @return the concatenation of the two arrays, or null if the two arrays are null. + */ + public static final char[] concat(char[] first, char[] second) { + if (first == null) + return second; + if (second == null) + return first; + + int length1 = first.length; + int length2 = second.length; + char[] result = new char[length1 + length2]; + System.arraycopy(first, 0, result, 0, length1); + System.arraycopy(second, 0, result, length1, length2); + return result; + } + + /** + * Answers the concatenation of the three arrays. It answers null if the three arrays are null. + * If first is null, it answers the concatenation of second and third. + * If second is null, it answers the concatenation of first and third. + * If third is null, it answers the concatenation of first and second. + *
+ *
+ * For example: + *
    + *
  1. +	 *    first = null
    +	 *    second = { 'a' }
    +	 *    third = { 'b' }
    +	 *    => result = { ' a', 'b' }
    +	 * 
    + *
  2. + *
  3. +	 *    first = { 'a' }
    +	 *    second = null
    +	 *    third = { 'b' }
    +	 *    => result = { ' a', 'b' }
    +	 * 
    + *
  4. + *
  5. +	 *    first = { 'a' }
    +	 *    second = { 'b' }
    +	 *    third = null
    +	 *    => result = { ' a', 'b' }
    +	 * 
    + *
  6. + *
  7. +	 *    first = null
    +	 *    second = null
    +	 *    third = null
    +	 *    => result = null
    +	 * 
    + *
  8. + *
  9. +	 *    first = { 'a' }
    +	 *    second = { 'b' }
    +	 *    third = { 'c' }
    +	 *    => result = { 'a', 'b', 'c' }
    +	 * 
    + *
  10. + *
+ * + * @param first the first array to concatenate + * @param second the second array to concatenate + * @param third the third array to concatenate + * + * @return the concatenation of the three arrays, or null if the three arrays are null. + */ + public static final char[] concat( + char[] first, + char[] second, + char[] third) { + if (first == null) + return concat(second, third); + if (second == null) + return concat(first, third); + if (third == null) + return concat(first, second); + + int length1 = first.length; + int length2 = second.length; + int length3 = third.length; + char[] result = new char[length1 + length2 + length3]; + System.arraycopy(first, 0, result, 0, length1); + System.arraycopy(second, 0, result, length1, length2); + System.arraycopy(third, 0, result, length1 + length2, length3); + return result; + } + + /** + * Answers the concatenation of the two arrays inserting the separator character between the two arrays. + * It answers null if the two arrays are null. + * If the first array is null, then the second array is returned. + * If the second array is null, then the first array is returned. + *
+ *
+ * For example: + *
    + *
  1. +	 *    first = null
    +	 *    second = { 'a' }
    +	 *    separator = '/'
    +	 *    => result = { ' a' }
    +	 * 
    + *
  2. + *
  3. +	 *    first = { ' a' }
    +	 *    second = null
    +	 *    separator = '/'
    +	 *    => result = { ' a' }
    +	 * 
    + *
  4. + *
  5. +	 *    first = { ' a' }
    +	 *    second = { ' b' }
    +	 *    separator = '/'
    +	 *    => result = { ' a' , '/', 'b' }
    +	 * 
    + *
  6. + *
+ * + * @param first the first array to concatenate + * @param second the second array to concatenate + * @param separator the character to insert + * @return the concatenation of the two arrays inserting the separator character + * between the two arrays , or null if the two arrays are null. + */ + public static final char[] concat( + char[] first, + char[] second, + char separator) { + if (first == null) + return second; + if (second == null) + return first; + + int length1 = first.length; + if (length1 == 0) + return second; + int length2 = second.length; + if (length2 == 0) + return first; + + char[] result = new char[length1 + length2 + 1]; + System.arraycopy(first, 0, result, 0, length1); + result[length1] = separator; + System.arraycopy(second, 0, result, length1 + 1, length2); + return result; + } + + /** + * Answers the concatenation of the three arrays inserting the sep1 character between the + * two arrays and sep2 between the last two. + * It answers null if the three arrays are null. + * If the first array is null, then it answers the concatenation of second and third inserting + * the sep2 character between them. + * If the second array is null, then the first array is returned. + *
+ *
+ * For example: + *
    + *
  1. +	 *    first = null
    +	 *    second = { 'a' }
    +	 *    separator = '/'
    +	 *    => result = { ' a' }
    +	 * 
    + *
  2. + *
  3. +	 *    first = { ' a' }
    +	 *    second = null
    +	 *    separator = '/'
    +	 *    => result = { ' a' }
    +	 * 
    + *
  4. + *
  5. +	 *    first = { ' a' }
    +	 *    second = { ' b' }
    +	 *    separator = '/'
    +	 *    => result = { ' a' , '/', 'b' }
    +	 * 
    + *
  6. + *
+ * + * @param first the first array to concatenate + * @param second the second array to concatenate + * @param separator the character to insert + * @return the concatenation of the two arrays inserting the separator character + * between the two arrays , or null if the two arrays are null. + */ + public static final char[] concat( + char[] first, + char sep1, + char[] second, + char sep2, + char[] third) { + if (first == null) + return concat(second, third, sep2); + if (second == null) + return concat(first, third, sep1); + if (third == null) + return concat(first, second, sep1); + + int length1 = first.length; + int length2 = second.length; + int length3 = third.length; + char[] result = new char[length1 + length2 + length3 + 2]; + System.arraycopy(first, 0, result, 0, length1); + result[length1] = sep1; + System.arraycopy(second, 0, result, length1 + 1, length2); + result[length1 + length2 + 1] = sep2; + System.arraycopy(third, 0, result, length1 + length2 + 2, length3); + return result; + } + + /** + * Answers a new array with prepending the prefix character and appending the suffix + * character at the end of the array. If array is null, it answers a new array containing the + * prefix and the suffix characters. + *
+ *
+ * For example:
+ *
    + *
  1. +	 *    prefix = 'a'
    +	 *    array = { 'b' }
    +	 *    suffix = 'c'
    +	 *    => result = { 'a', 'b' , 'c' }
    +	 * 
    + *
  2. + *
  3. +	 *    prefix = 'a'
    +	 *    array = null
    +	 *    suffix = 'c'
    +	 *    => result = { 'a', 'c' }
    +	 * 
  4. + *
+ * + * @param prefix the prefix character + * @param array the array that is concanated with the prefix and suffix characters + * @param suffix the suffix character + * @return the new array + */ + public static final char[] concat(char prefix, char[] array, char suffix) { + if (array == null) + return new char[] { prefix, suffix }; + + int length = array.length; + char[] result = new char[length + 2]; + result[0] = prefix; + System.arraycopy(array, 0, result, 1, length); + result[length + 1] = suffix; + return result; + } + + /** + * Answers the concatenation of the given array parts using the given separator between each + * part and appending the given name at the end. + *
+ *
+ * For example:
+ *
    + *
  1. +	 *    name = { 'c' }
    +	 *    array = { { 'a' }, { 'b' } }
    +	 *    separator = '.'
    +	 *    => result = { 'a', '.', 'b' , '.', 'c' }
    +	 * 
    + *
  2. + *
  3. +	 *    name = null
    +	 *    array = { { 'a' }, { 'b' } }
    +	 *    separator = '.'
    +	 *    => result = { 'a', '.', 'b' }
    +	 * 
  4. + *
  5. +	 *    name = { ' c' }
    +	 *    array = null
    +	 *    separator = '.'
    +	 *    => result = { 'c' }
    +	 * 
  6. + *
+ * + * @param name the given name + * @param array the given array + * @param separator the given separator + * @return the concatenation of the given array parts using the given separator between each + * part and appending the given name at the end + */ + public static final char[] concatWith( + char[] name, + char[][] array, + char separator) { + int nameLength = name == null ? 0 : name.length; + if (nameLength == 0) + return concatWith(array, separator); + + int length = array == null ? 0 : array.length; + if (length == 0) + return name; + + int size = nameLength; + int index = length; + while (--index >= 0) + if (array[index].length > 0) + size += array[index].length + 1; + char[] result = new char[size]; + index = size; + for (int i = length - 1; i >= 0; i--) { + int subLength = array[i].length; + if (subLength > 0) { + index -= subLength; + System.arraycopy(array[i], 0, result, index, subLength); + result[--index] = separator; + } + } + System.arraycopy(name, 0, result, 0, nameLength); + return result; + } + + /** + * Answers the concatenation of the given array parts using the given separator between each + * part and appending the given name at the end. + *
+ *
+ * For example:
+ *
    + *
  1. +	 *    name = { 'c' }
    +	 *    array = { { 'a' }, { 'b' } }
    +	 *    separator = '.'
    +	 *    => result = { 'a', '.', 'b' , '.', 'c' }
    +	 * 
    + *
  2. + *
  3. +	 *    name = null
    +	 *    array = { { 'a' }, { 'b' } }
    +	 *    separator = '.'
    +	 *    => result = { 'a', '.', 'b' }
    +	 * 
  4. + *
  5. +	 *    name = { ' c' }
    +	 *    array = null
    +	 *    separator = '.'
    +	 *    => result = { 'c' }
    +	 * 
  6. + *
+ * + * @param array the given array + * @param name the given name + * @param separator the given separator + * @return the concatenation of the given array parts using the given separator between each + * part and appending the given name at the end + */ + public static final char[] concatWith( + char[][] array, + char[] name, + char separator) { + int nameLength = name == null ? 0 : name.length; + if (nameLength == 0) + return concatWith(array, separator); + + int length = array == null ? 0 : array.length; + if (length == 0) + return name; + + int size = nameLength; + int index = length; + while (--index >= 0) + if (array[index].length > 0) + size += array[index].length + 1; + char[] result = new char[size]; + index = 0; + for (int i = 0; i < length; i++) { + int subLength = array[i].length; + if (subLength > 0) { + System.arraycopy(array[i], 0, result, index, subLength); + index += subLength; + result[index++] = separator; + } + } + System.arraycopy(name, 0, result, index, nameLength); + return result; + } + + /** + * Answers the concatenation of the given array parts using the given separator between each part. + *
+ *
+ * For example:
+ *
    + *
  1. +	 *    array = { { 'a' }, { 'b' } }
    +	 *    separator = '.'
    +	 *    => result = { 'a', '.', 'b' }
    +	 * 
    + *
  2. + *
  3. +	 *    array = null
    +	 *    separator = '.'
    +	 *    => result = { }
    +	 * 
  4. + *
+ * + * @param array the given array + * @param separator the given separator + * @return the concatenation of the given array parts using the given separator between each part + */ + public static final char[] concatWith(char[][] array, char separator) { + int length = array == null ? 0 : array.length; + if (length == 0) + return CharOperation.NO_CHAR; + + int size = length - 1; + int index = length; + while (--index >= 0) { + if (array[index].length == 0) + size--; + else + size += array[index].length; + } + if (size <= 0) + return CharOperation.NO_CHAR; + char[] result = new char[size]; + index = length; + while (--index >= 0) { + length = array[index].length; + if (length > 0) { + System.arraycopy( + array[index], + 0, + result, + (size -= length), + length); + if (--size >= 0) + result[size] = separator; + } + } + return result; + } + + /** + * Answers true if the array contains an occurrence of character, false otherwise. + * + *
+ *
+ * For example: + *
    + *
  1. +	 *    character = 'c'
    +	 *    array = { { ' a' }, { ' b' } }
    +	 *    result => false
    +	 * 
    + *
  2. + *
  3. +	 *    character = 'a'
    +	 *    array = { { ' a' }, { ' b' } }
    +	 *    result => true
    +	 * 
    + *
  4. + *
+ * + * @param character the character to search + * @param array the array in which the search is done + * @exception NullPointerException if array is null. + * + * @return true if the array contains an occurrence of character, false otherwise. + */ + public static final boolean contains(char character, char[][] array) { + for (int i = array.length; --i >= 0;) { + char[] subarray = array[i]; + for (int j = subarray.length; --j >= 0;) + if (subarray[j] == character) + return true; + } + return false; + } + + /** + * Answers true if the array contains an occurrence of character, false otherwise. + * + *
+ *
+ * For example: + *
    + *
  1. +	 *    character = 'c'
    +	 *    array = { ' b'  }
    +	 *    result => false
    +	 * 
    + *
  2. + *
  3. +	 *    character = 'a'
    +	 *    array = { ' a' , ' b' }
    +	 *    result => true
    +	 * 
    + *
  4. + *
+ * + * @param character the character to search + * @param array the array in which the search is done + * @exception NullPointerException if array is null. + * + * @return true if the array contains an occurrence of character, false otherwise. + */ + public static final boolean contains(char character, char[] array) { + for (int i = array.length; --i >= 0;) + if (array[i] == character) + return true; + return false; + } + + /** + * Answers a deep copy of the toCopy array. + * + * @param toCopy the array to copy + * @return a deep copy of the toCopy array. + */ + public static final char[][] deepCopy(char[][] toCopy) { + int toCopyLength = toCopy.length; + char[][] result = new char[toCopyLength][]; + for (int i = 0; i < toCopyLength; i++) { + char[] toElement = toCopy[i]; + int toElementLength = toElement.length; + char[] resultElement = new char[toElementLength]; + System.arraycopy(toElement, 0, resultElement, 0, toElementLength); + result[i] = resultElement; + } + return result; + } + + /** + * Return true if array ends with the sequence of characters contained in toBeFound, + * otherwise false. + *
+ *
+ * For example: + *
    + *
  1. +	 *    array = { 'a', 'b', 'c', 'd' }
    +	 *    toBeFound = { 'b', 'c' }
    +	 *    result => false
    +	 * 
    + *
  2. + *
  3. +	 *    array = { 'a', 'b', 'c' }
    +	 *    toBeFound = { 'b', 'c' }
    +	 *    result => true
    +	 * 
    + *
  4. + *
+ * + * @param array the array to check + * @param toBeFound the array to find + * @exception NullPointerException if array is null or toBeFound is null + * @return true if array ends with the sequence of characters contained in toBeFound, + * otherwise false. + */ + public static final boolean endsWith(char[] array, char[] toBeFound) { + int i = toBeFound.length; + int j = array.length - i; + + if (j < 0) + return false; + while (--i >= 0) + if (toBeFound[i] != array[i + j]) + return false; + return true; + } + + /** + * Answers true if the two arrays are identical character by character, otherwise false. + * The equality is case sensitive. + *
+ *
+ * For example: + *
    + *
  1. +	 *    first = null
    +	 *    second = null
    +	 *    result => true
    +	 * 
    + *
  2. + *
  3. +	 *    first = { { } }
    +	 *    second = null
    +	 *    result => false
    +	 * 
    + *
  4. + *
  5. +	 *    first = { { 'a' } }
    +	 *    second = { { 'a' } }
    +	 *    result => true
    +	 * 
    + *
  6. + *
  7. +	 *    first = { { 'A' } }
    +	 *    second = { { 'a' } }
    +	 *    result => false
    +	 * 
    + *
  8. + *
+ * @param first the first array + * @param second the second array + * @return true if the two arrays are identical character by character, otherwise false + */ + public static final boolean equals(char[][] first, char[][] second) { + if (first == second) + return true; + if (first == null || second == null) + return false; + if (first.length != second.length) + return false; + + for (int i = first.length; --i >= 0;) + if (!equals(first[i], second[i])) + return false; + return true; + } + + /** + * If isCaseSensite is true, answers true if the two arrays are identical character + * by character, otherwise false. + * If it is false, answers true if the two arrays are identical character by + * character without checking the case, otherwise false. + *
+ *
+ * For example: + *
    + *
  1. +	 *    first = null
    +	 *    second = null
    +	 *    isCaseSensitive = true
    +	 *    result => true
    +	 * 
    + *
  2. + *
  3. +	 *    first = { { } }
    +	 *    second = null
    +	 *    isCaseSensitive = true
    +	 *    result => false
    +	 * 
    + *
  4. + *
  5. +	 *    first = { { 'A' } }
    +	 *    second = { { 'a' } }
    +	 *    isCaseSensitive = true
    +	 *    result => false
    +	 * 
    + *
  6. + *
  7. +	 *    first = { { 'A' } }
    +	 *    second = { { 'a' } }
    +	 *    isCaseSensitive = false
    +	 *    result => true
    +	 * 
    + *
  8. + *
+ * + * @param first the first array + * @param second the second array + * @param isCaseSensitive check whether or not the equality should be case sensitive + * @return true if the two arrays are identical character by character according to the value + * of isCaseSensitive, otherwise false + */ + public static final boolean equals( + char[][] first, + char[][] second, + boolean isCaseSensitive) { + + if (isCaseSensitive) { + return equals(first, second); + } + if (first == second) + return true; + if (first == null || second == null) + return false; + if (first.length != second.length) + return false; + + for (int i = first.length; --i >= 0;) + if (!equals(first[i], second[i], false)) + return false; + return true; + } + + /** + * Answers true if the two arrays are identical character by character, otherwise false. + * The equality is case sensitive. + *
+ *
+ * For example: + *
    + *
  1. +	 *    first = null
    +	 *    second = null
    +	 *    result => true
    +	 * 
    + *
  2. + *
  3. +	 *    first = { }
    +	 *    second = null
    +	 *    result => false
    +	 * 
    + *
  4. + *
  5. +	 *    first = { 'a' }
    +	 *    second = { 'a' }
    +	 *    result => true
    +	 * 
    + *
  6. + *
  7. +	 *    first = { 'a' }
    +	 *    second = { 'A' }
    +	 *    result => false
    +	 * 
    + *
  8. + *
+ * @param first the first array + * @param second the second array + * @return true if the two arrays are identical character by character, otherwise false + */ + public static final boolean equals(char[] first, char[] second) { + if (first == second) + return true; + if (first == null || second == null) + return false; + if (first.length != second.length) + return false; + + for (int i = first.length; --i >= 0;) + if (first[i] != second[i]) + return false; + return true; + } + + /** + * If isCaseSensite is true, answers true if the two arrays are identical character + * by character, otherwise false. + * If it is false, answers true if the two arrays are identical character by + * character without checking the case, otherwise false. + *
+ *
+ * For example: + *
    + *
  1. +	 *    first = null
    +	 *    second = null
    +	 *    isCaseSensitive = true
    +	 *    result => true
    +	 * 
    + *
  2. + *
  3. +	 *    first = { }
    +	 *    second = null
    +	 *    isCaseSensitive = true
    +	 *    result => false
    +	 * 
    + *
  4. + *
  5. +	 *    first = { 'A' }
    +	 *    second = { 'a' }
    +	 *    isCaseSensitive = true
    +	 *    result => false
    +	 * 
    + *
  6. + *
  7. +	 *    first = { 'A' }
    +	 *    second = { 'a' }
    +	 *    isCaseSensitive = false
    +	 *    result => true
    +	 * 
    + *
  8. + *
+ * + * @param first the first array + * @param second the second array + * @param isCaseSensitive check whether or not the equality should be case sensitive + * @return true if the two arrays are identical character by character according to the value + * of isCaseSensitive, otherwise false + */ + public static final boolean equals( + char[] first, + char[] second, + boolean isCaseSensitive) { + + if (isCaseSensitive) { + return equals(first, second); + } + if (first == second) + return true; + if (first == null || second == null) + return false; + if (first.length != second.length) + return false; + + for (int i = first.length; --i >= 0;) + if (Character.toLowerCase(first[i]) + != Character.toLowerCase(second[i])) + return false; + return true; + } + /** + * If isCaseSensite is true, the equality is case sensitive, otherwise it is case insensitive. + * + * Answers true if the name contains the fragment at the starting index startIndex, otherwise false. + *
+ *
+ * For example: + *
    + *
  1. +	 *    fragment = { 'b', 'c' , 'd' }
    +	 *    name = { 'a', 'b', 'c' , 'd' }
    +	 *    startIndex = 1
    +	 *    isCaseSensitive = true
    +	 *    result => true
    +	 * 
    + *
  2. + *
  3. +	 *    fragment = { 'b', 'c' , 'd' }
    +	 *    name = { 'a', 'b', 'C' , 'd' }
    +	 *    startIndex = 1
    +	 *    isCaseSensitive = true
    +	 *    result => false
    +	 * 
    + *
  4. + *
  5. +	 *    fragment = { 'b', 'c' , 'd' }
    +	 *    name = { 'a', 'b', 'C' , 'd' }
    +	 *    startIndex = 0
    +	 *    isCaseSensitive = false
    +	 *    result => false
    +	 * 
    + *
  6. + *
  7. +	 *    fragment = { 'b', 'c' , 'd' }
    +	 *    name = { 'a', 'b'}
    +	 *    startIndex = 0
    +	 *    isCaseSensitive = true
    +	 *    result => false
    +	 * 
    + *
  8. + *
+ * + * @param fragment the fragment to check + * @param second the array to check + * @param startIndex the starting index + * @param isCaseSensitive check whether or not the equality should be case sensitive + * @return true if the name contains the fragment at the starting index startIndex according to the + * value of isCaseSensitive, otherwise false. + * @exception NullPointerException if fragment or name is null. + */ + public static final boolean fragmentEquals( + char[] fragment, + char[] name, + int startIndex, + boolean isCaseSensitive) { + + int max = fragment.length; + if (name.length < max + startIndex) + return false; + if (isCaseSensitive) { + for (int i = max; + --i >= 0; + ) // assumes the prefix is not larger than the name + if (fragment[i] != name[i + startIndex]) + return false; + return true; + } + for (int i = max; + --i >= 0; + ) // assumes the prefix is not larger than the name + if (Character.toLowerCase(fragment[i]) + != Character.toLowerCase(name[i + startIndex])) + return false; + return true; + } + + /** + * Answers a hashcode for the array + * + * @param array the array for which a hashcode is required + * @return the hashcode + * @exception NullPointerException if array is null + */ + public static final int hashCode(char[] array) { + int hash = 0; + int offset = 0; + int length = array.length; + if (length < 16) { + for (int i = length; i > 0; i--) + hash = (hash * 37) + array[offset++]; + } else { + // only sample some characters + int skip = length / 8; + for (int i = length; i > 0; i -= skip, offset += skip) + hash = (hash * 39) + array[offset]; + } + return hash & 0x7FFFFFFF; + } + /** + * Answers true if c is a whitespace according to the JLS (\u000a, \u000c, \u000d, \u0009), otherwise false. + *
+ *
+ * For example: + *
    + *
  1. +	 *    c = ' '
    +	 *    result => true
    +	 * 
    + *
  2. + *
  3. +	 *    c = '\u3000'
    +	 *    result => false
    +	 * 
    + *
  4. + *
+ * + * @param c the character to check + * @return true if c is a whitespace according to the JLS, otherwise false. + */ + public static boolean isWhitespace(char c) { + switch (c) { + case 10 : /* \ u000a: LINE FEED */ + case 12 : /* \ u000c: FORM FEED */ + case 13 : /* \ u000d: CARRIAGE RETURN */ + case 32 : /* \ u0020: SPACE */ + case 9 : /* \ u0009: HORIZONTAL TABULATION */ + return true; + default : + return false; + } + } + + /** + * Answers the first index in the array for which the corresponding character is + * equal to toBeFound. Answers -1 if no occurrence of this character is found. + *
+ *
+ * For example: + *
    + *
  1. +	 *    toBeFound = 'c'
    +	 *    array = { ' a', 'b', 'c', 'd' }
    +	 *    result => 2
    +	 * 
    + *
  2. + *
  3. +	 *    toBeFound = 'e'
    +	 *    array = { ' a', 'b', 'c', 'd' }
    +	 *    result => -1
    +	 * 
    + *
  4. + *
+ * + * @param toBeFound the character to search + * @param array the array to be searched + * @return the first index in the array for which the corresponding character is + * equal to toBeFound, -1 otherwise + * @exception NullPointerException if array is null + */ + public static final int indexOf(char toBeFound, char[] array) { + for (int i = 0; i < array.length; i++) + if (toBeFound == array[i]) + return i; + return -1; + } + + /** + * Answers the first index in the array for which the corresponding character is + * equal to toBeFound starting the search at index start. + * Answers -1 if no occurrence of this character is found. + *
+ *
+ * For example: + *
    + *
  1. +	 *    toBeFound = 'c'
    +	 *    array = { ' a', 'b', 'c', 'd' }
    +	 *    start = 2
    +	 *    result => 2
    +	 * 
    + *
  2. + *
  3. +	 *    toBeFound = 'c'
    +	 *    array = { ' a', 'b', 'c', 'd' }
    +	 *    start = 3
    +	 *    result => -1
    +	 * 
    + *
  4. + *
  5. +	 *    toBeFound = 'e'
    +	 *    array = { ' a', 'b', 'c', 'd' }
    +	 *    start = 1
    +	 *    result => -1
    +	 * 
    + *
  6. + *
+ * + * @param toBeFound the character to search + * @param array the array to be searched + * @param start the starting index + * @return the first index in the array for which the corresponding character is + * equal to toBeFound, -1 otherwise + * @exception NullPointerException if array is null + * @exception ArrayIndexOutOfBoundsException if start is lower than 0 + */ + public static final int indexOf(char toBeFound, char[] array, int start) { + for (int i = start; i < array.length; i++) + if (toBeFound == array[i]) + return i; + return -1; + } + + /** + * Answers the last index in the array for which the corresponding character is + * equal to toBeFound starting from the end of the array. + * Answers -1 if no occurrence of this character is found. + *
+ *
+ * For example: + *
    + *
  1. +	 *    toBeFound = 'c'
    +	 *    array = { ' a', 'b', 'c', 'd' , 'c', 'e' }
    +	 *    result => 4
    +	 * 
    + *
  2. + *
  3. +	 *    toBeFound = 'e'
    +	 *    array = { ' a', 'b', 'c', 'd' }
    +	 *    result => -1
    +	 * 
    + *
  4. + *
+ * + * @param toBeFound the character to search + * @param array the array to be searched + * @return the last index in the array for which the corresponding character is + * equal to toBeFound starting from the end of the array, -1 otherwise + * @exception NullPointerException if array is null + */ + public static final int lastIndexOf(char toBeFound, char[] array) { + for (int i = array.length; --i >= 0;) + if (toBeFound == array[i]) + return i; + return -1; + } + + /** + * Answers the last index in the array for which the corresponding character is + * equal to toBeFound stopping at the index startIndex. + * Answers -1 if no occurrence of this character is found. + *
+ *
+ * For example: + *
    + *
  1. +	 *    toBeFound = 'c'
    +	 *    array = { ' a', 'b', 'c', 'd' }
    +	 *    startIndex = 2
    +	 *    result => 2
    +	 * 
    + *
  2. + *
  3. +	 *    toBeFound = 'c'
    +	 *    array = { ' a', 'b', 'c', 'd', 'e' }
    +	 *    startIndex = 3
    +	 *    result => -1
    +	 * 
    + *
  4. + *
  5. +	 *    toBeFound = 'e'
    +	 *    array = { ' a', 'b', 'c', 'd' }
    +	 *    startIndex = 0
    +	 *    result => -1
    +	 * 
    + *
  6. + *
+ * + * @param toBeFound the character to search + * @param array the array to be searched + * @param startIndex the stopping index + * @return the last index in the array for which the corresponding character is + * equal to toBeFound stopping at the index startIndex, -1 otherwise + * @exception NullPointerException if array is null + * @exception ArrayIndexOutOfBoundsException if startIndex is lower than 0 + */ + public static final int lastIndexOf( + char toBeFound, + char[] array, + int startIndex) { + for (int i = array.length; --i >= startIndex;) + if (toBeFound == array[i]) + return i; + return -1; + } + + /** + * Answers the last index in the array for which the corresponding character is + * equal to toBeFound starting from endIndex to startIndex. + * Answers -1 if no occurrence of this character is found. + *
+ *
+ * For example: + *
    + *
  1. +	 *    toBeFound = 'c'
    +	 *    array = { ' a', 'b', 'c', 'd' }
    +	 *    startIndex = 2
    +	 *    endIndex = 2
    +	 *    result => 2
    +	 * 
    + *
  2. + *
  3. +	 *    toBeFound = 'c'
    +	 *    array = { ' a', 'b', 'c', 'd', 'e' }
    +	 *    startIndex = 3
    +	 *    endIndex = 4
    +	 *    result => -1
    +	 * 
    + *
  4. + *
  5. +	 *    toBeFound = 'e'
    +	 *    array = { ' a', 'b', 'c', 'd' }
    +	 *    startIndex = 0
    +	 *    endIndex = 3
    +	 *    result => -1
    +	 * 
    + *
  6. + *
+ * + * @param toBeFound the character to search + * @param array the array to be searched + * @param startIndex the stopping index + * @param endIndex the starting index + * @return the last index in the array for which the corresponding character is + * equal to toBeFound starting from endIndex to startIndex, -1 otherwise + * @exception NullPointerException if array is null + * @exception ArrayIndexOutOfBoundsException if endIndex is greater or equals to array length or starting is lower than 0 + */ + public static final int lastIndexOf( + char toBeFound, + char[] array, + int startIndex, + int endIndex) { + for (int i = endIndex; --i >= startIndex;) + if (toBeFound == array[i]) + return i; + return -1; + } + + /** + * Answers the last portion of a name given a separator. + *
+ *
+ * For example, + *
+	 * 	lastSegment("java.lang.Object".toCharArray(),'.') --> Object
+	 * 
+ * + * @param array the array + * @param separator the given separator + * @return the last portion of a name given a separator + * @exception NullPointerException if array is null + */ + final static public char[] lastSegment(char[] array, char separator) { + int pos = lastIndexOf(separator, array); + if (pos < 0) + return array; + return subarray(array, pos + 1, array.length); + } + + /** + * Answers true if the pattern matches the given name, false otherwise. This char[] pattern matching + * accepts wild-cards '*' and '?'. + * + * When not case sensitive, the pattern is assumed to already be lowercased, the + * name will be lowercased character per character as comparing. + * If name is null, the answer is false. + * If pattern is null, the answer is true if name is not null. + *
+ *
+ * For example: + *
    + *
  1. +	 *    pattern = { '?', 'b', '*' }
    +	 *    name = { 'a', 'b', 'c' , 'd' }
    +	 *    isCaseSensitive = true
    +	 *    result => true
    +	 * 
    + *
  2. + *
  3. +	 *    pattern = { '?', 'b', '?' }
    +	 *    name = { 'a', 'b', 'c' , 'd' }
    +	 *    isCaseSensitive = true
    +	 *    result => false
    +	 * 
    + *
  4. + *
  5. +	 *    pattern = { 'b', '*' }
    +	 *    name = { 'a', 'b', 'c' , 'd' }
    +	 *    isCaseSensitive = true
    +	 *    result => false
    +	 * 
    + *
  6. + *
+ * + * @param pattern the given pattern + * @param name the given name + * @param isCaseSensitive flag to know whether or not the matching should be case sensitive + * @return true if the pattern matches the given name, false otherwise + */ + public static final boolean match( + char[] pattern, + char[] name, + boolean isCaseSensitive) { + + if (name == null) + return false; // null name cannot match + if (pattern == null) + return true; // null pattern is equivalent to '*' + + return match( + pattern, + 0, + pattern.length, + name, + 0, + name.length, + isCaseSensitive); + } + + /** + * Answers true if the a sub-pattern matches the subpart of the given name, false otherwise. + * char[] pattern matching, accepting wild-cards '*' and '?'. Can match only subset of name/pattern. + * end positions are non-inclusive. + * The subpattern is defined by the patternStart and pattternEnd positions. + * When not case sensitive, the pattern is assumed to already be lowercased, the + * name will be lowercased character per character as comparing. + *
+ *
+ * For example: + *
    + *
  1. +	 *    pattern = { '?', 'b', '*' }
    +	 *    patternStart = 1
    +	 *    patternEnd = 3
    +	 *    name = { 'a', 'b', 'c' , 'd' }
    +	 *    nameStart = 1
    +	 *    nameEnd = 4
    +	 *    isCaseSensitive = true
    +	 *    result => true
    +	 * 
    + *
  2. + *
  3. +	 *    pattern = { '?', 'b', '*' }
    +	 *    patternStart = 1
    +	 *    patternEnd = 2
    +	 *    name = { 'a', 'b', 'c' , 'd' }
    +	 *    nameStart = 1
    +	 *    nameEnd = 2
    +	 *    isCaseSensitive = true
    +	 *    result => false
    +	 * 
    + *
  4. + *
+ * + * @param pattern the given pattern + * @param patternStart the given pattern start + * @param patternEnd the given pattern end + * @param name the given name + * @param nameStart the given name start + * @param nameEnd the given name end + * @param isCaseSensitive flag to know if the matching should be case sensitive + * @return true if the a sub-pattern matches the subpart of the given name, false otherwise + */ + public static final boolean match( + char[] pattern, + int patternStart, + int patternEnd, + char[] name, + int nameStart, + int nameEnd, + boolean isCaseSensitive) { + + if (name == null) + return false; // null name cannot match + if (pattern == null) + return true; // null pattern is equivalent to '*' + int iPattern = patternStart; + int iName = nameStart; + + if (patternEnd < 0) + patternEnd = pattern.length; + if (nameEnd < 0) + nameEnd = name.length; + + /* check first segment */ + char patternChar = 0; + while ((iPattern < patternEnd) + && (patternChar = pattern[iPattern]) != '*') { + if (iName == nameEnd) + return false; + if (patternChar + != (isCaseSensitive + ? name[iName] + : Character.toLowerCase(name[iName])) + && patternChar != '?') { + return false; + } + iName++; + iPattern++; + } + /* check sequence of star+segment */ + int segmentStart; + if (patternChar == '*') { + segmentStart = ++iPattern; // skip star + } else { + segmentStart = 0; // force iName check + } + int prefixStart = iName; + checkSegment : while (iName < nameEnd) { + if (iPattern == patternEnd) { + iPattern = segmentStart; // mismatch - restart current segment + iName = ++prefixStart; + continue checkSegment; + } + /* segment is ending */ + if ((patternChar = pattern[iPattern]) == '*') { + segmentStart = ++iPattern; // skip start + if (segmentStart == patternEnd) { + return true; + } + prefixStart = iName; + continue checkSegment; + } + /* check current name character */ + if ((isCaseSensitive ? name[iName] : Character.toLowerCase(name[iName])) + != patternChar + && patternChar != '?') { + iPattern = segmentStart; // mismatch - restart current segment + iName = ++prefixStart; + continue checkSegment; + } + iName++; + iPattern++; + } + + return (segmentStart == patternEnd) + || (iName == nameEnd && iPattern == patternEnd) + || (iPattern == patternEnd - 1 && pattern[iPattern] == '*'); + } + + /** + * Answers true if the pattern matches the filepath using the pathSepatator, false otherwise. + * + * Path char[] pattern matching, accepting wild-cards '**', '*' and '?' (using Ant directory tasks + * conventions, also see "http://jakarta.apache.org/ant/manual/dirtasks.html#defaultexcludes"). + * Path pattern matching is enhancing regular pattern matching in supporting extra rule where '**' represent + * any folder combination. + * Special rules: + * - foo\ is equivalent to foo\** + * - *.php is equivalent to **\*.php + * When not case sensitive, the pattern is assumed to already be lowercased, the + * name will be lowercased character per character as comparing. + * + * @param pattern the given pattern + * @param filepath the given path + * @param isCaseSensitive to find out whether or not the matching should be case sensitive + * @param pathSeparator the given path separator + * @return true if the pattern matches the filepath using the pathSepatator, false otherwise + */ + public static final boolean pathMatch( + char[] pattern, + char[] filepath, + boolean isCaseSensitive, + char pathSeparator) { + + if (filepath == null) + return false; // null name cannot match + if (pattern == null) + return true; // null pattern is equivalent to '*' + + // special case: pattern foo is equivalent to **\foo (not absolute) + boolean freeLeadingDoubleStar; + + // offsets inside pattern + int pSegmentStart, pLength = pattern.length; + + if (freeLeadingDoubleStar = pattern[0] != pathSeparator){ + pSegmentStart = 0; + } else { + pSegmentStart = 1; + } + int pSegmentEnd = CharOperation.indexOf(pathSeparator, pattern, pSegmentStart+1); + if (pSegmentEnd < 0) pSegmentEnd = pLength; + + // special case: pattern foo\ is equivalent to foo\** + boolean freeTrailingDoubleStar = pattern[pLength - 1] == pathSeparator; + + // offsets inside filepath + int fSegmentStart, fLength = filepath.length; + if (filepath[0] != pathSeparator){ + fSegmentStart = 0; + } else { + fSegmentStart = 1; + } + if (fSegmentStart != pSegmentStart) { + return false; // both must start with a separator or none. + } + int fSegmentEnd = CharOperation.indexOf(pathSeparator, filepath, fSegmentStart+1); + if (fSegmentEnd < 0) fSegmentEnd = fLength; + + // first segments + while (pSegmentStart < pLength + && !freeLeadingDoubleStar + && !(pSegmentEnd == pLength && freeTrailingDoubleStar + || (pSegmentEnd == pSegmentStart + 2 + && pattern[pSegmentStart] == '*' + && pattern[pSegmentStart + 1] == '*'))) { + + if (fSegmentStart >= fLength) + return false; + if (!CharOperation + .match( + pattern, + pSegmentStart, + pSegmentEnd, + filepath, + fSegmentStart, + fSegmentEnd, + isCaseSensitive)) { + return false; + } + + // jump to next segment + pSegmentEnd = + CharOperation.indexOf( + pathSeparator, + pattern, + pSegmentStart = pSegmentEnd + 1); + // skip separator + if (pSegmentEnd < 0) + pSegmentEnd = pLength; + + fSegmentEnd = + CharOperation.indexOf( + pathSeparator, + filepath, + fSegmentStart = fSegmentEnd + 1); + // skip separator + if (fSegmentEnd < 0) fSegmentEnd = fLength; + } + + /* check sequence of doubleStar+segment */ + int pSegmentRestart; + if ((pSegmentStart >= pLength && freeTrailingDoubleStar) + || (pSegmentEnd == pSegmentStart + 2 + && pattern[pSegmentStart] == '*' + && pattern[pSegmentStart + 1] == '*')) { + pSegmentEnd = + CharOperation.indexOf( + pathSeparator, + pattern, + pSegmentStart = pSegmentEnd + 1); + // skip separator + if (pSegmentEnd < 0) pSegmentEnd = pLength; + pSegmentRestart = pSegmentStart; + } else { + pSegmentRestart = 0; // force fSegmentStart check + } + int fSegmentRestart = fSegmentStart; + checkSegment : while (fSegmentStart < fLength) { + + if (pSegmentStart >= pLength) { + if (freeTrailingDoubleStar) return true; + // mismatch - restart current path segment + pSegmentEnd = + CharOperation.indexOf(pathSeparator, pattern, pSegmentStart = pSegmentRestart); + if (pSegmentEnd < 0) pSegmentEnd = pLength; + + fSegmentRestart = + CharOperation.indexOf(pathSeparator, filepath, fSegmentRestart + 1); + // skip separator + if (fSegmentRestart < 0) { + fSegmentRestart = fLength; + } else { + fSegmentRestart++; + } + fSegmentEnd = + CharOperation.indexOf(pathSeparator, filepath, fSegmentStart = fSegmentRestart); + if (fSegmentEnd < 0) fSegmentEnd = fLength; + continue checkSegment; + } + + /* path segment is ending */ + if (pSegmentEnd == pSegmentStart + 2 + && pattern[pSegmentStart] == '*' + && pattern[pSegmentStart + 1] == '*') { + pSegmentEnd = + CharOperation.indexOf(pathSeparator, pattern, pSegmentStart = pSegmentEnd + 1); + // skip separator + if (pSegmentEnd < 0) pSegmentEnd = pLength; + pSegmentRestart = pSegmentStart; + fSegmentRestart = fSegmentStart; + if (pSegmentStart >= pLength) return true; + continue checkSegment; + } + /* chech current path segment */ + if (!CharOperation.match( + pattern, + pSegmentStart, + pSegmentEnd, + filepath, + fSegmentStart, + fSegmentEnd, + isCaseSensitive)) { + // mismatch - restart current path segment + pSegmentEnd = + CharOperation.indexOf(pathSeparator, pattern, pSegmentStart = pSegmentRestart); + if (pSegmentEnd < 0) pSegmentEnd = pLength; + + fSegmentRestart = + CharOperation.indexOf(pathSeparator, filepath, fSegmentRestart + 1); + // skip separator + if (fSegmentRestart < 0) { + fSegmentRestart = fLength; + } else { + fSegmentRestart++; + } + fSegmentEnd = + CharOperation.indexOf(pathSeparator, filepath, fSegmentStart = fSegmentRestart); + if (fSegmentEnd < 0) fSegmentEnd = fLength; + continue checkSegment; + } + // jump to next segment + pSegmentEnd = + CharOperation.indexOf( + pathSeparator, + pattern, + pSegmentStart = pSegmentEnd + 1); + // skip separator + if (pSegmentEnd < 0) + pSegmentEnd = pLength; + + fSegmentEnd = + CharOperation.indexOf( + pathSeparator, + filepath, + fSegmentStart = fSegmentEnd + 1); + // skip separator + if (fSegmentEnd < 0) + fSegmentEnd = fLength; + } + + return (pSegmentRestart >= pSegmentEnd) + || (fSegmentStart >= fLength && pSegmentStart >= pLength) + || (pSegmentStart == pLength - 2 + && pattern[pSegmentStart] == '*' + && pattern[pSegmentStart + 1] == '*') + || (pSegmentStart == pLength && freeTrailingDoubleStar); + } + + /** + * Answers the number of occurrences of the given character in the given array, 0 if any. + * + *
+ *
+ * For example: + *
    + *
  1. +	 *    toBeFound = 'b'
    +	 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 *    result => 3
    +	 * 
    + *
  2. + *
  3. +	 *    toBeFound = 'c'
    +	 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 *    result => 0
    +	 * 
    + *
  4. + *
+ * + * @param toBeFound the given character + * @param array the given array + * @return the number of occurrences of the given character in the given array, 0 if any + * @exception NullPointerException if array is null + */ + public static final int occurencesOf(char toBeFound, char[] array) { + int count = 0; + for (int i = 0; i < array.length; i++) + if (toBeFound == array[i]) + count++; + return count; + } + + /** + * Answers the number of occurrences of the given character in the given array starting + * at the given index, 0 if any. + * + *
+ *
+ * For example: + *
    + *
  1. +	 *    toBeFound = 'b'
    +	 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 *    start = 2
    +	 *    result => 2
    +	 * 
    + *
  2. + *
  3. +	 *    toBeFound = 'c'
    +	 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 *    start = 0
    +	 *    result => 0
    +	 * 
    + *
  4. + *
+ * + * @param toBeFound the given character + * @param array the given array + * @return the number of occurrences of the given character in the given array, 0 if any + * @exception NullPointerException if array is null + * @exception ArrayIndexOutOfBoundsException if start is lower than 0 + */ + public static final int occurencesOf( + char toBeFound, + char[] array, + int start) { + int count = 0; + for (int i = start; i < array.length; i++) + if (toBeFound == array[i]) + count++; + return count; + } + + /** + * Answers true if the given name starts with the given prefix, false otherwise. + * The comparison is case sensitive. + *
+ *
+ * For example: + *
    + *
  1. +	 *    prefix = { 'a' , 'b' }
    +	 *    name = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 *    result => true
    +	 * 
    + *
  2. + *
  3. +	 *    prefix = { 'a' , 'c' }
    +	 *    name = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 *    result => false
    +	 * 
    + *
  4. + *
+ * + * @param prefix the given prefix + * @param name the given name + * @return true if the given name starts with the given prefix, false otherwise + * @exception NullPointerException if the given name is null or if the given prefix is null + */ + public static final boolean prefixEquals(char[] prefix, char[] name) { + + int max = prefix.length; + if (name.length < max) + return false; + for (int i = max; + --i >= 0; + ) // assumes the prefix is not larger than the name + if (prefix[i] != name[i]) + return false; + return true; + } + + /** + * Answers true if the given name starts with the given prefix, false otherwise. + * isCaseSensitive is used to find out whether or not the comparison should be case sensitive. + *
+ *
+ * For example: + *
    + *
  1. +	 *    prefix = { 'a' , 'B' }
    +	 *    name = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 *    isCaseSensitive = false
    +	 *    result => true
    +	 * 
    + *
  2. + *
  3. +	 *    prefix = { 'a' , 'B' }
    +	 *    name = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 *    isCaseSensitive = true
    +	 *    result => false
    +	 * 
    + *
  4. + *
+ * + * @param prefix the given prefix + * @param name the given name + * @param isCaseSensitive to find out whether or not the comparison should be case sensitive + * @return true if the given name starts with the given prefix, false otherwise + * @exception NullPointerException if the given name is null or if the given prefix is null + */ + public static final boolean prefixEquals( + char[] prefix, + char[] name, + boolean isCaseSensitive) { + + int max = prefix.length; + if (name.length < max) + return false; + if (isCaseSensitive) { + for (int i = max; + --i >= 0; + ) // assumes the prefix is not larger than the name + if (prefix[i] != name[i]) + return false; + return true; + } + + for (int i = max; + --i >= 0; + ) // assumes the prefix is not larger than the name + if (Character.toLowerCase(prefix[i]) + != Character.toLowerCase(name[i])) + return false; + return true; + } + + /** + * Replace all occurrence of the character to be replaced with the remplacement character in the + * given array. + *
+ *
+ * For example: + *
    + *
  1. +	 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 *    toBeReplaced = 'b'
    +	 *    replacementChar = 'a'
    +	 *    result => No returned value, but array is now equals to { 'a' , 'a', 'a', 'a', 'a', 'a' }
    +	 * 
    + *
  2. + *
  3. +	 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 *    toBeReplaced = 'c'
    +	 *    replacementChar = 'a'
    +	 *    result => No returned value, but array is now equals to { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 * 
    + *
  4. + *
+ * + * @param array the given array + * @param toBeReplaced the character to be replaced + * @param replacementChar the replacement character + * @exception NullPointerException if the given array is null + */ + public static final void replace( + char[] array, + char toBeReplaced, + char replacementChar) { + if (toBeReplaced != replacementChar) { + for (int i = 0, max = array.length; i < max; i++) { + if (array[i] == toBeReplaced) + array[i] = replacementChar; + } + } + } + + /** + * Answers a new array of characters with substitutions. No side-effect is operated on the original + * array, in case no substitution happened, then the result is the same as the + * original one. + *
+ *
+ * For example: + *
    + *
  1. +	 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 *    toBeReplaced = { 'b' }
    +	 *    replacementChar = { 'a', 'a' }
    +	 *    result => { 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a' }
    +	 * 
    + *
  2. + *
  3. +	 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 *    toBeReplaced = { 'c' }
    +	 *    replacementChar = { 'a' }
    +	 *    result => { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 * 
    + *
  4. + *
+ * + * @param the given array + * @param toBeReplaced characters to be replaced + * @param the replacement characters + * @return a new array of characters with substitutions or the given array if none + * @exception NullPointerException if the given array is null + */ + public static final char[] replace( + char[] array, + char[] toBeReplaced, + char[] replacementChars) { + + int max = array.length; + int replacedLength = toBeReplaced.length; + int replacementLength = replacementChars.length; + + int[] starts = new int[5]; + int occurrenceCount = 0; + + if (!equals(toBeReplaced, replacementChars)) { + + next : for (int i = 0; i < max; i++) { + int j = 0; + while (j < replacedLength) { + if (i + j == max) + continue next; + if (array[i + j] != toBeReplaced[j++]) + continue next; + } + if (occurrenceCount == starts.length) { + System.arraycopy( + starts, + 0, + starts = new int[occurrenceCount * 2], + 0, + occurrenceCount); + } + starts[occurrenceCount++] = i; + } + } + if (occurrenceCount == 0) + return array; + char[] result = + new char[max + + occurrenceCount * (replacementLength - replacedLength)]; + int inStart = 0, outStart = 0; + for (int i = 0; i < occurrenceCount; i++) { + int offset = starts[i] - inStart; + System.arraycopy(array, inStart, result, outStart, offset); + inStart += offset; + outStart += offset; + System.arraycopy( + replacementChars, + 0, + result, + outStart, + replacementLength); + inStart += replacedLength; + outStart += replacementLength; + } + System.arraycopy(array, inStart, result, outStart, max - inStart); + return result; + } + + /** + * Return a new array which is the split of the given array using the given divider and triming each subarray to remove + * whitespaces equals to ' '. + *
+ *
+ * For example: + *
    + *
  1. +	 *    divider = 'b'
    +	 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 *    result => { { 'a' }, {  }, { 'a' }, { 'a' } }
    +	 * 
    + *
  2. + *
  3. +	 *    divider = 'c'
    +	 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 *    result => { { 'a', 'b', 'b', 'a', 'b', 'a' } }
    +	 * 
    + *
  4. + *
  5. +	 *    divider = 'b'
    +	 *    array = { 'a' , ' ', 'b', 'b', 'a', 'b', 'a' }
    +	 *    result => { { 'a' }, {  }, { 'a' }, { 'a' } }
    +	 * 
    + *
  6. + *
  7. +	 *    divider = 'c'
    +	 *    array = { ' ', ' ', 'a' , 'b', 'b', 'a', 'b', 'a', ' ' }
    +	 *    result => { { 'a', 'b', 'b', 'a', 'b', 'a' } }
    +	 * 
    + *
  8. + *
+ * + * @param divider the given divider + * @param array the given array + * @return a new array which is the split of the given array using the given divider and triming each subarray to remove + * whitespaces equals to ' ' + */ + public static final char[][] splitAndTrimOn(char divider, char[] array) { + int length = array == null ? 0 : array.length; + if (length == 0) + return NO_CHAR_CHAR; + + int wordCount = 1; + for (int i = 0; i < length; i++) + if (array[i] == divider) + wordCount++; + char[][] split = new char[wordCount][]; + int last = 0, currentWord = 0; + for (int i = 0; i < length; i++) { + if (array[i] == divider) { + int start = last, end = i - 1; + while (start < i && array[start] == ' ') + start++; + while (end > start && array[end] == ' ') + end--; + split[currentWord] = new char[end - start + 1]; + System.arraycopy( + array, + start, + split[currentWord++], + 0, + end - start + 1); + last = i + 1; + } + } + int start = last, end = length - 1; + while (start < length && array[start] == ' ') + start++; + while (end > start && array[end] == ' ') + end--; + split[currentWord] = new char[end - start + 1]; + System.arraycopy( + array, + start, + split[currentWord++], + 0, + end - start + 1); + return split; + } + + /** + * Return a new array which is the split of the given array using the given divider. + *
+ *
+ * For example: + *
    + *
  1. +	 *    divider = 'b'
    +	 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 *    result => { { 'a' }, {  }, { 'a' }, { 'a' } }
    +	 * 
    + *
  2. + *
  3. +	 *    divider = 'c'
    +	 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 *    result => { { 'a', 'b', 'b', 'a', 'b', 'a' } }
    +	 * 
    + *
  4. + *
  5. +	 *    divider = 'c'
    +	 *    array = { ' ', ' ', 'a' , 'b', 'b', 'a', 'b', 'a', ' ' }
    +	 *    result => { { ' ', 'a', 'b', 'b', 'a', 'b', 'a', ' ' } }
    +	 * 
    + *
  6. + *
+ * + * @param divider the given divider + * @param array the given array + * @return a new array which is the split of the given array using the given divider + */ + public static final char[][] splitOn(char divider, char[] array) { + int length = array == null ? 0 : array.length; + if (length == 0) + return NO_CHAR_CHAR; + + int wordCount = 1; + for (int i = 0; i < length; i++) + if (array[i] == divider) + wordCount++; + char[][] split = new char[wordCount][]; + int last = 0, currentWord = 0; + for (int i = 0; i < length; i++) { + if (array[i] == divider) { + split[currentWord] = new char[i - last]; + System.arraycopy( + array, + last, + split[currentWord++], + 0, + i - last); + last = i + 1; + } + } + split[currentWord] = new char[length - last]; + System.arraycopy(array, last, split[currentWord], 0, length - last); + return split; + } + + /** + * Return a new array which is the split of the given array using the given divider. The given end + * is exclusive and the given start is inclusive. + *
+ *
+ * For example: + *
    + *
  1. +	 *    divider = 'b'
    +	 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 *    start = 2
    +	 *    end = 5
    +	 *    result => { {  }, {  }, { 'a' } }
    +	 * 
    + *
  2. + *
+ * + * @param divider the given divider + * @param array the given array + * @param start the given starting index + * @param end the given ending index + * @return a new array which is the split of the given array using the given divider + * @exception ArrayIndexOutOfBoundsException if start is lower than 0 or end is greater than the array length + */ + public static final char[][] splitOn( + char divider, + char[] array, + int start, + int end) { + int length = array == null ? 0 : array.length; + if (length == 0 || start > end) + return NO_CHAR_CHAR; + + int wordCount = 1; + for (int i = start; i < end; i++) + if (array[i] == divider) + wordCount++; + char[][] split = new char[wordCount][]; + int last = start, currentWord = 0; + for (int i = start; i < end; i++) { + if (array[i] == divider) { + split[currentWord] = new char[i - last]; + System.arraycopy( + array, + last, + split[currentWord++], + 0, + i - last); + last = i + 1; + } + } + split[currentWord] = new char[end - last]; + System.arraycopy(array, last, split[currentWord], 0, end - last); + return split; + } + + /** + * Answers true if the given array starts with the given characters, false otherwise. + * The comparison is case sensitive. + *
+ *
+ * For example: + *
    + *
  1. +	 *    toBeFound = { 'a' , 'b' }
    +	 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 *    result => true
    +	 * 
    + *
  2. + *
  3. +	 *    toBeFound = { 'a' , 'c' }
    +	 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
    +	 *    result => false
    +	 * 
    + *
  4. + *
+ * + * @param array the given array + * @param toBeFound the given character to search + * @return true if the given array starts with the given characters, false otherwise + * @exception NullPointerException if the given array is null or if the given characters array to be found is null + */ + public static final boolean startsWith(char[] array, char[] toBeFound) { + int i = toBeFound.length; + if (i > array.length) + return false; + while (--i >= 0) + if (toBeFound[i] != array[i]) + return false; + return true; + } + + /** + * Answers a new array which is a copy of the given array starting at the given start and + * ending at the given end. The given start is inclusive and the given end is exclusive. + * Answers null if start is greater than end, if start is lower than 0 or if end is greater + * than the length of the given array. If end equals -1, it is converted to the array length. + *
+ *
+ * For example: + *
    + *
  1. +	 *    array = { { 'a' } , { 'b' } }
    +	 *    start = 0
    +	 *    end = 1
    +	 *    result => { { 'a' } }
    +	 * 
    + *
  2. + *
  3. +	 *    array = { { 'a' } , { 'b' } }
    +	 *    start = 0
    +	 *    end = -1
    +	 *    result => { { 'a' }, { 'b' } }
    +	 * 
    + *
  4. + *
+ * + * @param array the given array + * @param start the given starting index + * @param end the given ending index + * @return a new array which is a copy of the given array starting at the given start and + * ending at the given end + * @exception NullPointerException if the given array is null + */ + public static final char[][] subarray(char[][] array, int start, int end) { + if (end == -1) + end = array.length; + if (start > end) + return null; + if (start < 0) + return null; + if (end > array.length) + return null; + + char[][] result = new char[end - start][]; + System.arraycopy(array, start, result, 0, end - start); + return result; + } + + /** + * Answers a new array which is a copy of the given array starting at the given start and + * ending at the given end. The given start is inclusive and the given end is exclusive. + * Answers null if start is greater than end, if start is lower than 0 or if end is greater + * than the length of the given array. If end equals -1, it is converted to the array length. + *
+ *
+ * For example: + *
    + *
  1. +	 *    array = { 'a' , 'b' }
    +	 *    start = 0
    +	 *    end = 1
    +	 *    result => { 'a' }
    +	 * 
    + *
  2. + *
  3. +	 *    array = { 'a', 'b' }
    +	 *    start = 0
    +	 *    end = -1
    +	 *    result => { 'a' , 'b' }
    +	 * 
    + *
  4. + *
+ * + * @param array the given array + * @param start the given starting index + * @param end the given ending index + * @return a new array which is a copy of the given array starting at the given start and + * ending at the given end + * @exception NullPointerException if the given array is null + */ + public static final char[] subarray(char[] array, int start, int end) { + if (end == -1) + end = array.length; + if (start > end) + return null; + if (start < 0) + return null; + if (end > array.length) + return null; + + char[] result = new char[end - start]; + System.arraycopy(array, start, result, 0, end - start); + return result; + } + /** + * Answers the result of a char[] conversion to lowercase. Answers null if the given chars array is null. + *
+ * NOTE: if no conversion was necessary, then answers back the argument one. + *
+ *
+ * For example: + *
    + *
  1. +	 *    chars = { 'a' , 'b' }
    +	 *    result => { 'a' , 'b' }
    +	 * 
    + *
  2. + *
  3. +	 *    array = { 'A', 'b' }
    +	 *    result => { 'a' , 'b' }
    +	 * 
    + *
  4. + *
+ * + * @param chars the chars to convert + * @return the result of a char[] conversion to lowercase + */ + final static public char[] toLowerCase(char[] chars) { + if (chars == null) + return null; + int length = chars.length; + char[] lowerChars = null; + for (int i = 0; i < length; i++) { + char c = chars[i]; + char lc = Character.toLowerCase(c); + if ((c != lc) || (lowerChars != null)) { + if (lowerChars == null) { + System.arraycopy( + chars, + 0, + lowerChars = new char[length], + 0, + i); + } + lowerChars[i] = lc; + } + } + return lowerChars == null ? chars : lowerChars; + } + + /** + * Answers a new array removing leading and trailing spaces (' '). Answers the given array if there is no + * space characters to remove. + *
+ *
+ * For example: + *
    + *
  1. +	 *    chars = { ' ', 'a' , 'b', ' ',  ' ' }
    +	 *    result => { 'a' , 'b' }
    +	 * 
    + *
  2. + *
  3. +	 *    array = { 'A', 'b' }
    +	 *    result => { 'A' , 'b' }
    +	 * 
    + *
  4. + *
+ * + * @param chars the given array + * @return a new array removing leading and trailing spaces (' ') + */ + final static public char[] trim(char[] chars) { + + if (chars == null) + return null; + + int start = 0, length = chars.length, end = length - 1; + while (start < length && chars[start] == ' ') { + start++; + } + while (end > start && chars[end] == ' ') { + end--; + } + if (start != 0 || end != length - 1) { + return subarray(chars, start, end + 1); + } + return chars; + } + + /** + * Answers a string which is the concatenation of the given array using the '.' as a separator. + *
+ *
+ * For example: + *
    + *
  1. +	 *    array = { { 'a' } , { 'b' } }
    +	 *    result => "a.b"
    +	 * 
    + *
  2. + *
  3. +	 *    array = { { ' ',  'a' } , { 'b' } }
    +	 *    result => " a.b"
    +	 * 
    + *
  4. + *
+ * + * @param chars the given array + * @return a string which is the concatenation of the given array using the '.' as a separator + */ + final static public String toString(char[][] array) { + char[] result = concatWith(array, '.'); + return new String(result); + } +} diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/core/compiler/ITerminalSymbols.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/core/compiler/ITerminalSymbols.java index c77f43c..cd6ad90 100644 --- a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/core/compiler/ITerminalSymbols.java +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/core/compiler/ITerminalSymbols.java @@ -37,7 +37,8 @@ public interface ITerminalSymbols { public final static int TokenNameWHITESPACE = 900, TokenNameCOMMENT_LINE = 901, TokenNameCOMMENT_BLOCK = 902, - TokenNameCOMMENT_PHPDOC = 903; + TokenNameCOMMENT_PHPDOC = 903, + TokenNameHTML = 904; final static int TokenNameEOF = 0; final static int TokenNameERROR = 1; diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/ConfigurableOption.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/ConfigurableOption.java new file mode 100644 index 0000000..08a9d96 --- /dev/null +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/ConfigurableOption.java @@ -0,0 +1,229 @@ +/******************************************************************************* + * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v0.5 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v05.html + * + * Contributors: + * IBM Corporation - initial API and implementation + ******************************************************************************/ +package net.sourceforge.phpdt.internal.compiler; + +/** + * Generic option description, which can be modified independently from the + * component it belongs to. + * + * @deprecated backport 1.0 internal functionality + */ + +import java.util.*; + +public class ConfigurableOption { + private String componentName; + private String optionName; + private int id; + + private String category; + private String name; + private String description; + private int currentValueIndex; + private int defaultValueIndex; + private String[] possibleValues; + + // special value for indicating that + // the is the actual value + public final static String[] NoDiscreteValue = {}; +/** + * INTERNAL USE ONLY + * + * Initialize an instance of this class according to a specific locale + * + * @param loc java.util.Locale + */ +public ConfigurableOption( + String componentName, + String optionName, + Locale loc, + int currentValueIndex) { + + this.componentName = componentName; + this.optionName = optionName; + this.currentValueIndex = currentValueIndex; + + ResourceBundle resource = null; + try { + String location = componentName.substring(0, componentName.lastIndexOf('.')); + resource = ResourceBundle.getBundle(location + ".Options", loc); //$NON-NLS-1$ + } catch (MissingResourceException e) { + category = "Missing ressources entries for" + componentName + " options"; //$NON-NLS-1$ //$NON-NLS-2$ + name = "Missing ressources entries for"+ componentName + " options"; //$NON-NLS-1$ //$NON-NLS-2$ + description = "Missing ressources entries for" + componentName + " options"; //$NON-NLS-1$ //$NON-NLS-2$ + possibleValues = new String[0]; + id = -1; + } + if (resource == null) return; + try { + id = Integer.parseInt(resource.getString(optionName + ".number")); //$NON-NLS-1$ + } catch (MissingResourceException e) { + id = -1; + } catch (NumberFormatException e) { + id = -1; + } + try { + category = resource.getString(optionName + ".category"); //$NON-NLS-1$ + } catch (MissingResourceException e) { + category = "Missing ressources entries for" + componentName + " options"; //$NON-NLS-1$ //$NON-NLS-2$ + } + try { + name = resource.getString(optionName + ".name"); //$NON-NLS-1$ + } catch (MissingResourceException e) { + name = "Missing ressources entries for"+ componentName + " options"; //$NON-NLS-1$ //$NON-NLS-2$ + } + try { + StringTokenizer tokenizer = new StringTokenizer(resource.getString(optionName + ".possibleValues"), "|"); //$NON-NLS-1$ //$NON-NLS-2$ + int numberOfValues = Integer.parseInt(tokenizer.nextToken()); + if(numberOfValues == -1){ + possibleValues = NoDiscreteValue; + } else { + possibleValues = new String[numberOfValues]; + int index = 0; + while (tokenizer.hasMoreTokens()) { + possibleValues[index] = tokenizer.nextToken(); + index++; + } + } + } catch (MissingResourceException e) { + possibleValues = new String[0]; + } catch (NoSuchElementException e) { + possibleValues = new String[0]; + } catch (NumberFormatException e) { + possibleValues = new String[0]; + } + try { + description = resource.getString(optionName + ".description"); //$NON-NLS-1$ + } catch (MissingResourceException e) { + description = "Missing ressources entries for"+ componentName + " options"; //$NON-NLS-1$ //$NON-NLS-2$ + } +} +/** + * Return a String that represents the localized category of the receiver. + * @return java.lang.String + */ +public String getCategory() { + return category; +} +/** + * Return a String that identifies the component owner (typically the qualified + * type name of the class which it corresponds to). + * + * e.g. "org.phpeclipse.phpdt.internal.compiler.api.Compiler" + * + * @return java.lang.String + */ +public String getComponentName() { + return componentName; +} +/** + * Answer the index (in possibleValues array) of the current setting for this + * particular option. + * + * In case the set of possibleValues is NoDiscreteValue, then this index is the + * actual value (e.g. max line lenght set to 80). + * + * @return int + */ +public int getCurrentValueIndex() { + return currentValueIndex; +} +/** + * Answer the index (in possibleValues array) of the default setting for this + * particular option. + * + * In case the set of possibleValues is NoDiscreteValue, then this index is the + * actual value (e.g. max line lenght set to 80). + * + * @return int + */ +public int getDefaultValueIndex() { + return defaultValueIndex; +} +/** + * Return an String that represents the localized description of the receiver. + * + * @return java.lang.String + */ +public String getDescription() { + return description; +} +/** + * Internal ID which allows the configurable component to identify this particular option. + * + * @return int + */ +public int getID() { + return id; +} +/** + * Return a String that represents the localized name of the receiver. + * @return java.lang.String + */ +public String getName() { + return name; +} +/** + * Return an array of String that represents the localized possible values of the receiver. + * @return java.lang.String[] + */ +public String[] getPossibleValues() { + return possibleValues; +} +/** + * Change the index (in possibleValues array) of the current setting for this + * particular option. + * + * In case the set of possibleValues is NoDiscreteValue, then this index is the + * actual value (e.g. max line lenght set to 80). + * + * @return int + */ +public void setValueIndex(int newIndex) { + currentValueIndex = newIndex; +} +public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append("Configurable option for "); //$NON-NLS-1$ + buffer.append(this.componentName).append("\n"); //$NON-NLS-1$ + buffer.append("- category: ").append(this.category).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$ + buffer.append("- name: ").append(this.name).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$ + /* display current value */ + buffer.append("- current value: "); //$NON-NLS-1$ + if (possibleValues == NoDiscreteValue){ + buffer.append(this.currentValueIndex); + } else { + buffer.append(this.possibleValues[this.currentValueIndex]); + } + buffer.append("\n"); //$NON-NLS-1$ + + /* display possible values */ + if (possibleValues != NoDiscreteValue){ + buffer.append("- possible values: ["); //$NON-NLS-1$ + for (int i = 0, max = possibleValues.length; i < max; i++) { + if (i != 0) + buffer.append(", "); //$NON-NLS-1$ + buffer.append(possibleValues[i]); + } + buffer.append("]\n"); //$NON-NLS-1$ + buffer.append("- curr. val. index: ").append(currentValueIndex).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$ + } + buffer.append("- description: ").append(description).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$ + return buffer.toString(); +} + /** + * Gets the optionName. + * @return Returns a String + */ + public String getOptionName() { + return optionName; + } +} diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/parser/Scanner.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/parser/Scanner.java index 04c3935..83f8e87 100644 --- a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/parser/Scanner.java +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/parser/Scanner.java @@ -719,9 +719,10 @@ public class Scanner implements IScanner, ITerminalSymbols { } public int getNextToken() throws InvalidInputException { + try { + int htmlPosition = currentPosition; while (!phpMode) { - startPosition = currentPosition; currentCharacter = source[currentPosition++]; if (currentCharacter == '<') { if (getNextChar('?')) { @@ -731,6 +732,12 @@ public class Scanner implements IScanner, ITerminalSymbols { // GenStubSettings) +// * @param imports Imports required by the stub are added to the imports structure. If imports structure is null +// * all type names are qualified. +// * @throws JavaModelException +// */ +// public static String genStub(String destTypeName, IMethod method, GenStubSettings settings, IImportsStructure imports) throws JavaModelException { +// IType declaringtype= method.getDeclaringType(); +// StringBuffer buf= new StringBuffer(); +// String methodName= method.getElementName(); +// String[] paramTypes= method.getParameterTypes(); +// String[] paramNames= method.getParameterNames(); +// String[] excTypes= method.getExceptionTypes(); +// String retTypeSig= method.getReturnType(); +// int flags= method.getFlags(); +// boolean isConstructor= method.isConstructor(); +// +// int lastParam= paramTypes.length -1; +// +// +// if (settings.createComments) { +// if (isConstructor) { +// String desc= "Constructor for " + destTypeName; //$NON-NLS-1$ +// genJavaDocStub(desc, paramNames, Signature.SIG_VOID, excTypes, buf); +// } else { +// // php doc +// if (settings.methodOverwrites) { +// boolean isDeprecated= Flags.isDeprecated(flags); +// genJavaDocSeeTag(declaringtype, methodName, paramTypes, settings.createNonJavadocComments, isDeprecated, buf); +// } else { +// // generate a default php doc comment +// String desc= "Method " + methodName; //$NON-NLS-1$ +// genJavaDocStub(desc, paramNames, retTypeSig, excTypes, buf); +// } +// } +// buf.append('\n'); +// } +// +// if (Flags.isPublic(flags) || isConstructor || (declaringtype.isInterface() && !settings.noBody)) { +// buf.append("public "); //$NON-NLS-1$ +// } else if (Flags.isProtected(flags)) { +// buf.append("protected "); //$NON-NLS-1$ +// } else if (Flags.isPrivate(flags)) { +// buf.append("private "); //$NON-NLS-1$ +// } +// if (Flags.isSynchronized(flags)) { +// buf.append("synchronized "); //$NON-NLS-1$ +// } +// if (Flags.isVolatile(flags)) { +// buf.append("volatile "); //$NON-NLS-1$ +// } +// if (Flags.isStrictfp(flags)) { +// buf.append("strictfp "); //$NON-NLS-1$ +// } +// if (Flags.isStatic(flags)) { +// buf.append("static "); //$NON-NLS-1$ +// } +// +// if (isConstructor) { +// buf.append(destTypeName); +// } else { +// String retTypeFrm; +// if (!isPrimitiveType(retTypeSig)) { +// retTypeFrm= resolveAndAdd(retTypeSig, declaringtype, imports); +// } else { +// retTypeFrm= Signature.toString(retTypeSig); +// } +// buf.append(retTypeFrm); +// buf.append(' '); +// buf.append(methodName); +// } +// buf.append('('); +// for (int i= 0; i <= lastParam; i++) { +// String paramTypeSig= paramTypes[i]; +// String paramTypeFrm; +// +// if (!isPrimitiveType(paramTypeSig)) { +// paramTypeFrm= resolveAndAdd(paramTypeSig, declaringtype, imports); +// } else { +// paramTypeFrm= Signature.toString(paramTypeSig); +// } +// buf.append(paramTypeFrm); +// buf.append(' '); +// buf.append(paramNames[i]); +// if (i < lastParam) { +// buf.append(", "); //$NON-NLS-1$ +// } +// } +// buf.append(')'); +// +// int lastExc= excTypes.length - 1; +// if (lastExc >= 0) { +// buf.append(" throws "); //$NON-NLS-1$ +// for (int i= 0; i <= lastExc; i++) { +// String excTypeSig= excTypes[i]; +// String excTypeFrm= resolveAndAdd(excTypeSig, declaringtype, imports); +// buf.append(excTypeFrm); +// if (i < lastExc) { +// buf.append(", "); //$NON-NLS-1$ +// } +// } +// } +// if (settings.noBody) { +// buf.append(";\n\n"); //$NON-NLS-1$ +// } else { +// buf.append(" {\n\t"); //$NON-NLS-1$ +// if (!settings.callSuper) { +// if (retTypeSig != null && !retTypeSig.equals(Signature.SIG_VOID)) { +// buf.append('\t'); +// if (!isPrimitiveType(retTypeSig) || Signature.getArrayCount(retTypeSig) > 0) { +// buf.append("return null;\n\t"); //$NON-NLS-1$ +// } else if (retTypeSig.equals(Signature.SIG_BOOLEAN)) { +// buf.append("return false;\n\t"); //$NON-NLS-1$ +// } else { +// buf.append("return 0;\n\t"); //$NON-NLS-1$ +// } +// } +// } else { +// buf.append('\t'); +// if (!isConstructor) { +// if (!Signature.SIG_VOID.equals(retTypeSig)) { +// buf.append("return "); //$NON-NLS-1$ +// } +// buf.append("super."); //$NON-NLS-1$ +// buf.append(methodName); +// } else { +// buf.append("super"); //$NON-NLS-1$ +// } +// buf.append('('); +// for (int i= 0; i <= lastParam; i++) { +// buf.append(paramNames[i]); +// if (i < lastParam) { +// buf.append(", "); //$NON-NLS-1$ +// } +// } +// buf.append(");\n\t"); //$NON-NLS-1$ +// } +// buf.append("}\n"); //$NON-NLS-1$ +// } +// return buf.toString(); +// } +// +// private static boolean isSet(int options, int flag) { +// return (options & flag) != 0; +// } +// +// private static boolean isPrimitiveType(String typeName) { +// char first= Signature.getElementType(typeName).charAt(0); +// return (first != Signature.C_RESOLVED && first != Signature.C_UNRESOLVED); +// } +// +// private static String resolveAndAdd(String refTypeSig, IType declaringType, IImportsStructure imports) throws JavaModelException { +// String resolvedTypeName= JavaModelUtil.getResolvedTypeName(refTypeSig, declaringType); +// if (resolvedTypeName != null) { +// StringBuffer buf= new StringBuffer(); +// if (imports != null) { +// buf.append(imports.addImport(resolvedTypeName)); +// } else { +// buf.append(resolvedTypeName); +// } +// int arrayCount= Signature.getArrayCount(refTypeSig); +// for (int i= 0; i < arrayCount; i++) { +// buf.append("[]"); //$NON-NLS-1$ +// } +// return buf.toString(); +// } +// return Signature.toString(refTypeSig); +// } +// +// /** +// * Generates a default JavaDoc comment stub for a method. +// */ +// public static void genJavaDocStub(String descr, String[] paramNames, String retTypeSig, String[] excTypeSigs, StringBuffer buf) { +// buf.append("/**\n"); //$NON-NLS-1$ +// buf.append(" * "); buf.append(descr); buf.append(".\n"); //$NON-NLS-2$ //$NON-NLS-1$ +// for (int i= 0; i < paramNames.length; i++) { +// buf.append(" * @param "); buf.append(paramNames[i]); buf.append('\n'); //$NON-NLS-1$ +// } +// if (retTypeSig != null && !retTypeSig.equals(Signature.SIG_VOID)) { +// String simpleName= Signature.getSimpleName(Signature.toString(retTypeSig)); +// buf.append(" * @return "); buf.append(simpleName); buf.append('\n'); //$NON-NLS-1$ +// } +// if (excTypeSigs != null) { +// for (int i= 0; i < excTypeSigs.length; i++) { +// String simpleName= Signature.getSimpleName(Signature.toString(excTypeSigs[i])); +// buf.append(" * @throws "); buf.append(simpleName); buf.append('\n'); //$NON-NLS-1$ +// } +// } +// buf.append(" */"); //$NON-NLS-1$ +// } +// +// /** +// * Generates a '@see' tag to the defined method. +// */ +// public static void genJavaDocSeeTag(IType declaringType, String methodName, String[] paramTypes, boolean nonJavaDocComment, boolean isDeprecated, StringBuffer buf) throws JavaModelException { +// String[] fullParamNames= new String[paramTypes.length]; +// for (int i= 0; i < paramTypes.length; i++) { +// fullParamNames[i]= JavaModelUtil.getResolvedTypeName(paramTypes[i], declaringType); +// } +// String fullTypeName= JavaModelUtil.getFullyQualifiedName(declaringType); +// +// genJavaDocSeeTag(fullTypeName, methodName, fullParamNames, nonJavaDocComment, isDeprecated, buf); +// } +// +// /** +// * Generates a '@see' tag to the defined method. +// */ +// public static void genJavaDocSeeTag(String fullyQualifiedTypeName, String methodName, String[] fullParamTypeNames, boolean nonJavaDocComment, boolean isDeprecated, StringBuffer buf) throws JavaModelException { +// // create a @see link +// buf.append("/*"); //$NON-NLS-1$ +// if (!nonJavaDocComment) { +// buf.append('*'); +// } else { +// buf.append(" (non-Javadoc)"); //$NON-NLS-1$ +// } +// buf.append("\n * @see "); //$NON-NLS-1$ +// buf.append(fullyQualifiedTypeName); +// buf.append('#'); +// buf.append(methodName); +// buf.append('('); +// for (int i= 0; i < fullParamTypeNames.length; i++) { +// if (i > 0) { +// buf.append(", "); //$NON-NLS-1$ +// } +// buf.append(fullParamTypeNames[i]); +// } +// buf.append(")\n"); //$NON-NLS-1$ +// if (isDeprecated) { +// buf.append(" * @deprecated\n"); //$NON-NLS-1$ +// } +// buf.append(" */"); //$NON-NLS-1$ +// } +// +// +// +// /** +// * Finds a method in a list of methods. +// * @return The found method or null, if nothing found +// */ +// private static IMethod findMethod(IMethod method, List allMethods) throws JavaModelException { +// String name= method.getElementName(); +// String[] paramTypes= method.getParameterTypes(); +// boolean isConstructor= method.isConstructor(); +// +// for (int i= allMethods.size() - 1; i >= 0; i--) { +// IMethod curr= (IMethod) allMethods.get(i); +// if (JavaModelUtil.isSameMethodSignature(name, paramTypes, isConstructor, curr)) { +// return curr; +// } +// } +// return null; +// } + + + /** + * Creates needed constructors for a type. + * @param type The type to create constructors for + * @param supertype The type's super type + * @param settings Options for comment generation + * @param imports Required imports are added to the import structure. Structure can be null, types are qualified then. + * @return Returns the generated stubs or null if the creation has been canceled + */ +// public static String[] evalConstructors(IType type, IType supertype, CodeGenerationSettings settings, IImportsStructure imports) throws JavaModelException { +// IMethod[] superMethods= supertype.getMethods(); +// String typeName= type.getElementName(); +// IMethod[] methods= type.getMethods(); +// GenStubSettings genStubSettings= new GenStubSettings(settings); +// genStubSettings.callSuper= true; +// ArrayList newMethods= new ArrayList(superMethods.length); +// for (int i= 0; i < superMethods.length; i++) { +// IMethod curr= superMethods[i]; +// if (curr.isConstructor() && (JavaModelUtil.isVisible(curr, type.getPackageFragment()) || Flags.isProtected(curr.getFlags()))) { +// if (JavaModelUtil.findMethod(typeName, curr.getParameterTypes(), true, methods) == null) { +// String newStub= genStub(typeName, superMethods[i], genStubSettings, imports); +// newMethods.add(newStub); +// } +// } +// } +// return (String[]) newMethods.toArray(new String[newMethods.size()]); +// } +// +// /** +// * Searches for unimplemented methods of a type. +// * @param isSubType If set, the evaluation is for a subtype of the given type. If not set, the +// * evaluation is for the type itself. +// * @param settings Options for comment generation +// * @param selectionQuery If not null will select the methods to implement. +// * @param imports Required imports are added to the import structure. Structure can be null, types are qualified then. +// * @return Returns the generated stubs or null if the creation has been canceled +// */ +// public static String[] evalUnimplementedMethods(IType type, ITypeHierarchy hierarchy, boolean isSubType, CodeGenerationSettings settings, +// IOverrideMethodQuery selectionQuery, IImportsStructure imports) throws JavaModelException { +// List allMethods= new ArrayList(); +// List toImplement= new ArrayList(); +// +// IMethod[] typeMethods= type.getMethods(); +// for (int i= 0; i < typeMethods.length; i++) { +// IMethod curr= typeMethods[i]; +// if (!curr.isConstructor() && !Flags.isStatic(curr.getFlags()) && !Flags.isPrivate(curr.getFlags())) { +// allMethods.add(curr); +// } +// } +// +// IType[] superTypes= hierarchy.getAllSuperclasses(type); +// for (int i= 0; i < superTypes.length; i++) { +// IMethod[] methods= superTypes[i].getMethods(); +// for (int k= 0; k < methods.length; k++) { +// IMethod curr= methods[k]; +// if (!curr.isConstructor() && !Flags.isStatic(curr.getFlags()) && !Flags.isPrivate(curr.getFlags())) { +// if (findMethod(curr, allMethods) == null) { +// allMethods.add(curr); +// } +// } +// } +// } +// +// // do not call super +// for (int i= 0; i < allMethods.size(); i++) { +// IMethod curr= (IMethod) allMethods.get(i); +// if ((Flags.isAbstract(curr.getFlags()) || curr.getDeclaringType().isInterface()) && (isSubType || !type.equals(curr.getDeclaringType()))) { +// // implement all abstract methods +// toImplement.add(curr); +// } +// } +// +// IType[] superInterfaces= hierarchy.getAllSuperInterfaces(type); +// for (int i= 0; i < superInterfaces.length; i++) { +// IMethod[] methods= superInterfaces[i].getMethods(); +// for (int k= 0; k < methods.length; k++) { +// IMethod curr= methods[k]; +// +// // binary interfaces can contain static initializers (variable intializations) +// // 1G4CKUS +// if (!Flags.isStatic(curr.getFlags())) { +// IMethod impl= findMethod(curr, allMethods); +// if (impl == null || ((curr.getExceptionTypes().length < impl.getExceptionTypes().length) && !Flags.isFinal(impl.getFlags()))) { +// if (impl != null) { +// allMethods.remove(impl); +// } +// // implement an interface method when it does not exist in the hierarchy +// // or when it throws less exceptions that the implemented +// toImplement.add(curr); +// allMethods.add(curr); +// } +// } +// } +// } +// IMethod[] toImplementArray= (IMethod[]) toImplement.toArray(new IMethod[toImplement.size()]); +// if (selectionQuery != null) { +// if (!isSubType) { +// allMethods.removeAll(Arrays.asList(typeMethods)); +// } +// // remove finals +// for (int i= allMethods.size() - 1; i >= 0; i--) { +// IMethod curr= (IMethod) allMethods.get(i); +// if (Flags.isFinal(curr.getFlags())) { +// allMethods.remove(i); +// } +// } +// IMethod[] choice= (IMethod[]) allMethods.toArray(new IMethod[allMethods.size()]); +// toImplementArray= selectionQuery.select(choice, toImplementArray, hierarchy); +// if (toImplementArray == null) { +// //cancel pressed +// return null; +// } +// } +// GenStubSettings genStubSettings= new GenStubSettings(settings); +// genStubSettings.methodOverwrites= true; +// String[] result= new String[toImplementArray.length]; +// for (int i= 0; i < toImplementArray.length; i++) { +// IMethod curr= toImplementArray[i]; +// IMethod overrides= JavaModelUtil.findMethodImplementationInHierarchy(hierarchy, type, curr.getElementName(), curr.getParameterTypes(), curr.isConstructor()); +// genStubSettings.callSuper= (overrides != null); +// +// IMethod desc= JavaModelUtil.findMethodDeclarationInHierarchy(hierarchy, type, curr.getElementName(), curr.getParameterTypes(), curr.isConstructor()); +// if (desc != null) { +// curr= desc; +// } +// result[i]= genStub(type.getElementName(), curr, genStubSettings, imports); +// } +// return result; +// } + + /** + * Examines a string and returns the first line delimiter found. + */ +// public static String getLineDelimiterUsed(IJavaElement elem) throws JavaModelException { +// ICompilationUnit cu= (ICompilationUnit) elem.getAncestor(IJavaElement.COMPILATION_UNIT); +// if (cu != null && cu.exists()) { +// IBuffer buf= cu.getBuffer(); +// int length= buf.getLength(); +// for (int i= 0; i < length; i++) { +// char ch= buf.getChar(i); +// if (ch == SWT.CR) { +// if (i + 1 < length) { +// if (buf.getChar(i + 1) == SWT.LF) { +// return "\r\n"; //$NON-NLS-1$ +// } +// } +// return "\r"; //$NON-NLS-1$ +// } else if (ch == SWT.LF) { +// return "\n"; //$NON-NLS-1$ +// } +// } +// } +// return System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$ +// } + + /** + * Embodies the policy which line delimiter to use when inserting into + * a document. + */ + public static String getLineDelimiterFor(IDocument doc) { + // new for: 1GF5UU0: ITPJUI:WIN2000 - "Organize Imports" in php editor inserts lines in wrong format + String lineDelim= null; + try { + lineDelim= doc.getLineDelimiter(0); + } catch (BadLocationException e) { + } + if (lineDelim == null) { + String systemDelimiter= System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$ + String[] lineDelims= doc.getLegalLineDelimiters(); + for (int i= 0; i < lineDelims.length; i++) { + if (lineDelims[i].equals(systemDelimiter)) { + lineDelim= systemDelimiter; + break; + } + } + if (lineDelim == null) { + lineDelim= lineDelims.length > 0 ? lineDelims[0] : systemDelimiter; + } + } + return lineDelim; + } + + + /** + * Evaluates the indention used by a Java element. (in tabulators) + */ +// public static int getIndentUsed(IJavaElement elem) throws JavaModelException { +// if (elem instanceof ISourceReference) { +// ICompilationUnit cu= (ICompilationUnit) elem.getAncestor(IJavaElement.COMPILATION_UNIT); +// if (cu != null) { +// IBuffer buf= cu.getBuffer(); +// int offset= ((ISourceReference)elem).getSourceRange().getOffset(); +// int i= offset; +// // find beginning of line +// while (i > 0 && !Strings.isLineDelimiterChar(buf.getChar(i - 1)) ){ +// i--; +// } +// return Strings.computeIndent(buf.getText(i, offset - i), CodeFormatterUtil.getTabWidth()); +// } +// } +// return 0; +// } + + public static String codeFormat(String sourceString, int initialIndentationLevel, String lineDelim) { + ICodeFormatter formatter= ToolFactory.createDefaultCodeFormatter(null); + return formatter.format(sourceString, initialIndentationLevel, null, lineDelim); + } + + /** + * Returns the element after the give element. + */ +// public static IJavaElement findNextSibling(IJavaElement member) throws JavaModelException { +// IJavaElement parent= member.getParent(); +// if (parent instanceof IParent) { +// IJavaElement[] elements= ((IParent)parent).getChildren(); +// for (int i= elements.length - 2; i >= 0 ; i--) { +// if (member.equals(elements[i])) { +// return elements[i+1]; +// } +// } +// } +// return null; +// } +// +// public static String getTodoTaskTag(IJavaProject project) { +// String markers= null; +// if (project == null) { +// markers= JavaCore.getOption(JavaCore.COMPILER_TASK_TAGS); +// } else { +// markers= project.getOption(JavaCore.COMPILER_TASK_TAGS, true); +// } +// +// if (markers != null && markers.length() > 0) { +// int idx= markers.indexOf(','); +// if (idx == -1) { +// return markers; +// } else { +// return markers.substring(0, idx); +// } +// } +// return null; +// } + +} diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/dialog/StatusUtil.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/dialog/StatusUtil.java new file mode 100644 index 0000000..ce895ca --- /dev/null +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/dialog/StatusUtil.java @@ -0,0 +1,75 @@ +/* + * (c) Copyright IBM Corp. 2000, 2001. + * All Rights Reserved. + */ +package net.sourceforge.phpdt.internal.ui.dialog; + +import org.eclipse.core.runtime.IStatus; + +import org.eclipse.jface.dialogs.DialogPage; + +/** + * A utility class to work with IStatus. + */ +public class StatusUtil { + + /** + * Compares two instances of IStatus. The more severe is returned: + * An error is more severe than a warning, and a warning is more severe + * than ok. If the two stati have the same severity, the second is returned. + */ + public static IStatus getMoreSevere(IStatus s1, IStatus s2) { + if (s1.getSeverity() > s2.getSeverity()) { + return s1; + } else { + return s2; + } + } + + /** + * Finds the most severe status from a array of stati. + * An error is more severe than a warning, and a warning is more severe + * than ok. + */ + public static IStatus getMostSevere(IStatus[] status) { + IStatus max= null; + for (int i= 0; i < status.length; i++) { + IStatus curr= status[i]; + if (curr.matches(IStatus.ERROR)) { + return curr; + } + if (max == null || curr.getSeverity() > max.getSeverity()) { + max= curr; + } + } + return max; + } + + /** + * Applies the status to the status line of a dialog page. + */ + public static void applyToStatusLine(DialogPage page, IStatus status) { + String message= status.getMessage(); + switch (status.getSeverity()) { + case IStatus.OK: + page.setMessage(message, DialogPage.NONE); + page.setErrorMessage(null); + break; + case IStatus.WARNING: + page.setMessage(message, DialogPage.WARNING); + page.setErrorMessage(null); + break; + case IStatus.INFO: + page.setMessage(message, DialogPage.INFORMATION); + page.setErrorMessage(null); + break; + default: + if (message.length() == 0) { + message= null; + } + page.setMessage(null); + page.setErrorMessage(message); + break; + } + } +} diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/preferences/CodeFormatterPreferencePage.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/preferences/CodeFormatterPreferencePage.java new file mode 100644 index 0000000..0e8d043 --- /dev/null +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/preferences/CodeFormatterPreferencePage.java @@ -0,0 +1,491 @@ +/* + * (c) Copyright IBM Corp. 2000, 2001. + * All Rights Reserved. + */ +package net.sourceforge.phpdt.internal.ui.preferences; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Hashtable; + +import net.sourceforge.phpdt.core.ICodeFormatter; +import net.sourceforge.phpdt.core.ToolFactory; +import net.sourceforge.phpdt.internal.ui.PHPUIMessages; +import net.sourceforge.phpdt.internal.ui.dialog.StatusInfo; +import net.sourceforge.phpdt.internal.ui.dialog.StatusUtil; +import net.sourceforge.phpdt.internal.ui.util.TabFolderLayout; +import net.sourceforge.phpeclipse.PHPCore; +import net.sourceforge.phpeclipse.PHPeclipsePlugin; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.jface.preference.PreferencePage; +import org.eclipse.jface.text.Document; +import org.eclipse.jface.text.IDocument; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.TabFolder; +import org.eclipse.swt.widgets.TabItem; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; + +/* + * The page for setting code formatter options + */ +public class CodeFormatterPreferencePage extends PreferencePage implements IWorkbenchPreferencePage { + + // Preference store keys, see PHPCore.getOptions + private static final String PREF_NEWLINE_OPENING_BRACES= PHPCore.FORMATTER_NEWLINE_OPENING_BRACE; + private static final String PREF_NEWLINE_CONTROL_STATEMENT= PHPCore.FORMATTER_NEWLINE_CONTROL; + private static final String PREF_NEWLINE_CLEAR_ALL= PHPCore.FORMATTER_CLEAR_BLANK_LINES; + private static final String PREF_NEWLINE_ELSE_IF= PHPCore.FORMATTER_NEWLINE_ELSE_IF; + private static final String PREF_NEWLINE_EMPTY_BLOCK= PHPCore.FORMATTER_NEWLINE_EMPTY_BLOCK; + private static final String PREF_LINE_SPLIT= PHPCore.FORMATTER_LINE_SPLIT; + private static final String PREF_STYLE_COMPACT_ASSIGNEMENT= PHPCore.FORMATTER_COMPACT_ASSIGNMENT; + private static final String PREF_TAB_CHAR= PHPCore.FORMATTER_TAB_CHAR; + private static final String PREF_TAB_SIZE= PHPCore.FORMATTER_TAB_SIZE; + + // values + private static final String INSERT= PHPCore.INSERT; + private static final String DO_NOT_INSERT= PHPCore.DO_NOT_INSERT; + + private static final String COMPACT= PHPCore.COMPACT; + private static final String NORMAL= PHPCore.NORMAL; + + private static final String TAB= PHPCore.TAB; + private static final String SPACE= PHPCore.SPACE; + + private static final String CLEAR_ALL= PHPCore.CLEAR_ALL; + private static final String PRESERVE_ONE= PHPCore.PRESERVE_ONE; + + + private static String[] getAllKeys() { + return new String[] { + PREF_NEWLINE_OPENING_BRACES, PREF_NEWLINE_CONTROL_STATEMENT, PREF_NEWLINE_CLEAR_ALL, + PREF_NEWLINE_ELSE_IF, PREF_NEWLINE_EMPTY_BLOCK, PREF_LINE_SPLIT, + PREF_STYLE_COMPACT_ASSIGNEMENT, PREF_TAB_CHAR, PREF_TAB_SIZE + }; + } + + /** + * Gets the currently configured tab size + * @deprecated Inline to avoid reference to preference page + */ + public static int getTabSize() { + String string= (String) PHPCore.getOptions().get(PREF_TAB_SIZE); + return getPositiveIntValue(string, 4); + } + + /** + * Gets the current compating assignement configuration + * @deprecated Inline to avoid reference to preference page + */ + public static boolean isCompactingAssignment() { + return COMPACT.equals(PHPCore.getOptions().get(PREF_STYLE_COMPACT_ASSIGNEMENT)); + } + + /** + * Gets the current compating assignement configuration + * @deprecated Inline to avoid reference to preference page + */ + public static boolean useSpaces() { + return SPACE.equals(PHPCore.getOptions().get(PREF_TAB_CHAR)); + } + + + private static int getPositiveIntValue(String string, int dflt) { + try { + int i= Integer.parseInt(string); + if (i >= 0) { + return i; + } + } catch (NumberFormatException e) { + } + return dflt; + } + + private static class ControlData { + private String fKey; + private String[] fValues; + + public ControlData(String key, String[] values) { + fKey= key; + fValues= values; + } + + public String getKey() { + return fKey; + } + + public String getValue(boolean selection) { + int index= selection ? 0 : 1; + return fValues[index]; + } + + public String getValue(int index) { + return fValues[index]; + } + + public int getSelection(String value) { + for (int i= 0; i < fValues.length; i++) { + if (value.equals(fValues[i])) { + return i; + } + } + throw new IllegalArgumentException(); + } + } + + private Hashtable fWorkingValues; + + private ArrayList fCheckBoxes; + private ArrayList fTextBoxes; + + private SelectionListener fButtonSelectionListener; + private ModifyListener fTextModifyListener; + + private String fPreviewText; + private IDocument fPreviewDocument; + + private Text fTabSizeTextBox; + // private SourceViewer fSourceViewer; + + + public CodeFormatterPreferencePage() { + setPreferenceStore(PHPeclipsePlugin.getDefault().getPreferenceStore()); + setDescription(PHPUIMessages.getString("CodeFormatterPreferencePage.description")); //$NON-NLS-1$ + + fWorkingValues= PHPCore.getOptions(); + fCheckBoxes= new ArrayList(); + fTextBoxes= new ArrayList(); + + fButtonSelectionListener= new SelectionListener() { + public void widgetDefaultSelected(SelectionEvent e) {} + + public void widgetSelected(SelectionEvent e) { + if (!e.widget.isDisposed()) { + controlChanged((Button) e.widget); + } + } + }; + + fTextModifyListener= new ModifyListener() { + public void modifyText(ModifyEvent e) { + if (!e.widget.isDisposed()) { + textChanged((Text) e.widget); + } + } + }; + + fPreviewDocument= new Document(); + fPreviewText= loadPreviewFile("CodeFormatterPreviewCode.txt"); //$NON-NLS-1$ + } + + /* + * @see IWorkbenchPreferencePage#init() + */ + public void init(IWorkbench workbench) { + } + + /* + * @see PreferencePage#createControl(Composite) + */ + public void createControl(Composite parent) { + super.createControl(parent); +// WorkbenchHelp.setHelp(getControl(), IJavaHelpContextIds.CODEFORMATTER_PREFERENCE_PAGE); + } + + /* + * @see PreferencePage#createContents(Composite) + */ + protected Control createContents(Composite parent) { + + GridLayout layout= new GridLayout(); + layout.marginHeight= 0; + layout.marginWidth= 0; + + Composite composite= new Composite(parent, SWT.NONE); + composite.setLayout(layout); + + + TabFolder folder= new TabFolder(composite, SWT.NONE); + folder.setLayout(new TabFolderLayout()); + folder.setLayoutData(new GridData(GridData.FILL_BOTH)); + + String[] insertNotInsert= new String[] { INSERT, DO_NOT_INSERT }; + + layout= new GridLayout(); + layout.numColumns= 2; + + Composite newlineComposite= new Composite(folder, SWT.NULL); + newlineComposite.setLayout(layout); + + String label= PHPUIMessages.getString("CodeFormatterPreferencePage.newline_opening_braces.label"); //$NON-NLS-1$ + addCheckBox(newlineComposite, label, PREF_NEWLINE_OPENING_BRACES, insertNotInsert); + + label= PHPUIMessages.getString("CodeFormatterPreferencePage.newline_control_statement.label"); //$NON-NLS-1$ + addCheckBox(newlineComposite, label, PREF_NEWLINE_CONTROL_STATEMENT, insertNotInsert); + + label= PHPUIMessages.getString("CodeFormatterPreferencePage.newline_clear_lines"); //$NON-NLS-1$ + addCheckBox(newlineComposite, label, PREF_NEWLINE_CLEAR_ALL, new String[] { CLEAR_ALL, PRESERVE_ONE } ); + + label= PHPUIMessages.getString("CodeFormatterPreferencePage.newline_else_if.label"); //$NON-NLS-1$ + addCheckBox(newlineComposite, label, PREF_NEWLINE_ELSE_IF, insertNotInsert); + + label= PHPUIMessages.getString("CodeFormatterPreferencePage.newline_empty_block.label"); //$NON-NLS-1$ + addCheckBox(newlineComposite, label, PREF_NEWLINE_EMPTY_BLOCK, insertNotInsert); + + layout= new GridLayout(); + layout.numColumns= 2; + + Composite lineSplittingComposite= new Composite(folder, SWT.NULL); + lineSplittingComposite.setLayout(layout); + + label= PHPUIMessages.getString("CodeFormatterPreferencePage.split_line.label"); //$NON-NLS-1$ + addTextField(lineSplittingComposite, label, PREF_LINE_SPLIT); + + layout= new GridLayout(); + layout.numColumns= 2; + + Composite styleComposite= new Composite(folder, SWT.NULL); + styleComposite.setLayout(layout); + + label= PHPUIMessages.getString("CodeFormatterPreferencePage.style_compact_assignement.label"); //$NON-NLS-1$ + addCheckBox(styleComposite, label, PREF_STYLE_COMPACT_ASSIGNEMENT, new String[] { COMPACT, NORMAL } ); + + label= PHPUIMessages.getString("CodeFormatterPreferencePage.tab_char.label"); //$NON-NLS-1$ + addCheckBox(styleComposite, label, PREF_TAB_CHAR, new String[] { TAB, SPACE } ); + + label= PHPUIMessages.getString("CodeFormatterPreferencePage.tab_size.label"); //$NON-NLS-1$ + fTabSizeTextBox= addTextField(styleComposite, label, PREF_TAB_SIZE); + + TabItem item= new TabItem(folder, SWT.NONE); + item.setText(PHPUIMessages.getString("CodeFormatterPreferencePage.tab.newline.tabtitle")); //$NON-NLS-1$ + item.setControl(newlineComposite); + + item= new TabItem(folder, SWT.NONE); + item.setText(PHPUIMessages.getString("CodeFormatterPreferencePage.tab.linesplit.tabtitle")); //$NON-NLS-1$ + item.setControl(lineSplittingComposite); + + item= new TabItem(folder, SWT.NONE); + item.setText(PHPUIMessages.getString("CodeFormatterPreferencePage.tab.style.tabtitle")); //$NON-NLS-1$ + item.setControl(styleComposite); + + // fSourceViewer= createPreview(parent); + + updatePreview(); + + return composite; + } + +// private SourceViewer createPreview(Composite parent) { +// SourceViewer previewViewer= new SourceViewer(parent, null, SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER); +// JavaTextTools tools= JavaPlugin.getDefault().getJavaTextTools(); +// previewViewer.configure(new PHPSourceViewerConfiguration(tools, null)); +// previewViewer.getTextWidget().setFont(JFaceResources.getFontRegistry().get(JFaceResources.TEXT_FONT)); +// previewViewer.getTextWidget().setTabs(getPositiveIntValue((String) fWorkingValues.get(PREF_TAB_SIZE), 0)); +// previewViewer.setEditable(false); +// previewViewer.setDocument(fPreviewDocument); +// Control control= previewViewer.getControl(); +// GridData gdata= new GridData(GridData.FILL_BOTH); +// gdata.widthHint= convertWidthInCharsToPixels(30); +// gdata.heightHint= convertHeightInCharsToPixels(5); +// control.setLayoutData(gdata); +// return previewViewer; +// } + + + private Button addCheckBox(Composite parent, String label, String key, String[] values) { + ControlData data= new ControlData(key, values); + + GridData gd= new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalSpan= 2; + + Button checkBox= new Button(parent, SWT.CHECK); + checkBox.setText(label); + checkBox.setData(data); + checkBox.setLayoutData(gd); + + String currValue= (String)fWorkingValues.get(key); + checkBox.setSelection(data.getSelection(currValue) == 0); + checkBox.addSelectionListener(fButtonSelectionListener); + + fCheckBoxes.add(checkBox); + return checkBox; + } + + private Text addTextField(Composite parent, String label, String key) { + Label labelControl= new Label(parent, SWT.NONE); + labelControl.setText(label); + labelControl.setLayoutData(new GridData()); + + Text textBox= new Text(parent, SWT.BORDER | SWT.SINGLE); + textBox.setData(key); + textBox.setLayoutData(new GridData()); + + String currValue= (String)fWorkingValues.get(key); + textBox.setText(String.valueOf(getPositiveIntValue(currValue, 1))); + textBox.setTextLimit(3); + textBox.addModifyListener(fTextModifyListener); + + GridData gd= new GridData(); + gd.widthHint= convertWidthInCharsToPixels(5); + textBox.setLayoutData(gd); + + fTextBoxes.add(textBox); + return textBox; + } + + private void controlChanged(Button button) { + ControlData data= (ControlData) button.getData(); + boolean selection= button.getSelection(); + String newValue= data.getValue(selection); + fWorkingValues.put(data.getKey(), newValue); + updatePreview(); + + if (PREF_TAB_CHAR.equals(data.getKey())) { + updateStatus(new StatusInfo()); + if (selection) { + fTabSizeTextBox.setText((String)fWorkingValues.get(PREF_TAB_SIZE)); + } + } + } + + private void textChanged(Text textControl) { + String key= (String) textControl.getData(); + String number= textControl.getText(); + IStatus status= validatePositiveNumber(number); + if (!status.matches(IStatus.ERROR)) { + fWorkingValues.put(key, number); + } +// if (PREF_TAB_SIZE.equals(key)) { +// fSourceViewer.getTextWidget().setTabs(getPositiveIntValue(number, 0)); +// } + updateStatus(status); + updatePreview(); + } + + + /* + * @see IPreferencePage#performOk() + */ + public boolean performOk() { + String[] allKeys= getAllKeys(); + // preserve other options + // store in JCore + Hashtable actualOptions= PHPCore.getOptions(); + for (int i= 0; i < allKeys.length; i++) { + String key= allKeys[i]; + String val= (String) fWorkingValues.get(key); + actualOptions.put(key, val); + } + PHPCore.setOptions(actualOptions); + PHPeclipsePlugin.getDefault().savePluginPreferences(); + return super.performOk(); + } + + /* + * @see PreferencePage#performDefaults() + */ + protected void performDefaults() { + fWorkingValues= PHPCore.getDefaultOptions(); + updateControls(); + super.performDefaults(); + } + + private String loadPreviewFile(String filename) { + String separator= System.getProperty("line.separator"); //$NON-NLS-1$ + StringBuffer btxt= new StringBuffer(512); + BufferedReader rin= null; + try { + rin= new BufferedReader(new InputStreamReader(getClass().getResourceAsStream(filename))); + String line; + while ((line= rin.readLine()) != null) { + btxt.append(line); + btxt.append(separator); + } + } catch (IOException io) { + PHPeclipsePlugin.log(io); + } finally { + if (rin != null) { + try { rin.close(); } catch (IOException e) {} + } + } + return btxt.toString(); + } + + + private void updatePreview() { + ICodeFormatter formatter= ToolFactory.createDefaultCodeFormatter(fWorkingValues); + fPreviewDocument.set(formatter.format(fPreviewText, 0, null, "\n")); //$NON-NLS-1$ + } + + private void updateControls() { + // update the UI + for (int i= fCheckBoxes.size() - 1; i >= 0; i--) { + Button curr= (Button) fCheckBoxes.get(i); + ControlData data= (ControlData) curr.getData(); + + String currValue= (String) fWorkingValues.get(data.getKey()); + curr.setSelection(data.getSelection(currValue) == 0); + } + for (int i= fTextBoxes.size() - 1; i >= 0; i--) { + Text curr= (Text) fTextBoxes.get(i); + String key= (String) curr.getData(); + String currValue= (String) fWorkingValues.get(key); + curr.setText(currValue); + } + } + + private IStatus validatePositiveNumber(String number) { + StatusInfo status= new StatusInfo(); + if (number.length() == 0) { + status.setError(PHPUIMessages.getString("CodeFormatterPreferencePage.empty_input")); //$NON-NLS-1$ + } else { + try { + int value= Integer.parseInt(number); + if (value < 0) { + status.setError(PHPUIMessages.getFormattedString("CodeFormatterPreferencePage.invalid_input", number)); //$NON-NLS-1$ + } + } catch (NumberFormatException e) { + status.setError(PHPUIMessages.getFormattedString("CodeFormatterPreferencePage.invalid_input", number)); //$NON-NLS-1$ + } + } + return status; + } + + + private void updateStatus(IStatus status) { + if (!status.matches(IStatus.ERROR)) { + // look if there are more severe errors + for (int i= 0; i < fTextBoxes.size(); i++) { + Text curr= (Text) fTextBoxes.get(i); + if (!(curr == fTabSizeTextBox && usesTabs())) { + IStatus currStatus= validatePositiveNumber(curr.getText()); + status= StatusUtil.getMoreSevere(currStatus, status); + } + } + } + setValid(!status.matches(IStatus.ERROR)); + StatusUtil.applyToStatusLine(this, status); + } + + private boolean usesTabs() { + return TAB.equals(fWorkingValues.get(PREF_TAB_CHAR)); + } + + +} + + diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/preferences/CodeFormatterPreviewCode.txt b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/preferences/CodeFormatterPreviewCode.txt new file mode 100644 index 0000000..c4ae226 --- /dev/null +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/preferences/CodeFormatterPreviewCode.txt @@ -0,0 +1,16 @@ +{ + +if (size < currentSize) { +try { +size = inStream.available(); +} catch (IOException e) { +} +} +else if (size == currentSize) { +++size; +} +else { +--size; +} + +} \ No newline at end of file diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/java/JavaFormattingStrategy.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/java/JavaFormattingStrategy.java new file mode 100644 index 0000000..e2a9d52 --- /dev/null +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/java/JavaFormattingStrategy.java @@ -0,0 +1,55 @@ +/* + * (c) Copyright IBM Corp. 2000, 2001. + * All Rights Reserved. + */ +package net.sourceforge.phpdt.internal.ui.text.java; + +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.formatter.IFormattingStrategy; +import org.eclipse.jface.text.source.ISourceViewer; + +import net.sourceforge.phpdt.core.ICodeFormatter; +import net.sourceforge.phpdt.core.ToolFactory; + +import net.sourceforge.phpdt.internal.corext.codemanipulation.StubUtility; +import net.sourceforge.phpdt.internal.corext.util.Strings; +import net.sourceforge.phpdt.internal.ui.preferences.CodeFormatterPreferencePage; + +public class JavaFormattingStrategy implements IFormattingStrategy { + + private String fInitialIndentation; + private ISourceViewer fViewer; + + public JavaFormattingStrategy(ISourceViewer viewer) { + fViewer = viewer; + } + + /** + * @see IFormattingStrategy#formatterStarts(String) + */ + public void formatterStarts(String initialIndentation) { + fInitialIndentation= initialIndentation; + } + + /** + * @see IFormattingStrategy#formatterStops() + */ + public void formatterStops() { + } + + /** + * @see IFormattingStrategy#format(String, boolean, String, int[]) + */ + public String format(String content, boolean isLineStart, String indentation, int[] positions) { + ICodeFormatter formatter= ToolFactory.createCodeFormatter(); + + IDocument doc= fViewer.getDocument(); + String lineDelimiter= StubUtility.getLineDelimiterFor(doc); + + int indent= 0; + if (fInitialIndentation != null) { + indent= Strings.computeIndent(fInitialIndentation, CodeFormatterPreferencePage.getTabSize()); + } + return formatter.format(content, indent, positions, lineDelimiter); + } +} \ No newline at end of file diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/util/TabFolderLayout.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/util/TabFolderLayout.java new file mode 100644 index 0000000..ba37d7b --- /dev/null +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/util/TabFolderLayout.java @@ -0,0 +1,47 @@ +/* + * (c) Copyright IBM Corp. 2000, 2001. + * All Rights Reserved + */ +package net.sourceforge.phpdt.internal.ui.util; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Layout; + +public class TabFolderLayout extends Layout { + + protected Point computeSize (Composite composite, int wHint, int hHint, boolean flushCache) { + if (wHint != SWT.DEFAULT && hHint != SWT.DEFAULT) + return new Point(wHint, hHint); + + Control [] children = composite.getChildren (); + int count = children.length; + int maxWidth = 0, maxHeight = 0; + for (int i=0; i"org.phpeclipse.phpdt.core"). + */ + public static final String PLUGIN_ID = "net.sourceforge.phpeclipse.core"; //$NON-NLS-1$ + + /** + * Possible configurable option ID. + * @see #getDefaultOptions + * @since 2.0 + */ + public static final String CORE_ENCODING = PLUGIN_ID + ".encoding"; //$NON-NLS-1$ + + /** + * Possible configurable option ID. + * @see #getDefaultOptions + * @since 2.0 + */ + public static final String FORMATTER_NEWLINE_OPENING_BRACE = PLUGIN_ID + ".formatter.newline.openingBrace"; //$NON-NLS-1$ + /** + * Possible configurable option ID. + * @see #getDefaultOptions + * @since 2.0 + */ + public static final String FORMATTER_NEWLINE_CONTROL = PLUGIN_ID + ".formatter.newline.controlStatement"; //$NON-NLS-1$ + /** + * Possible configurable option ID. + * @see #getDefaultOptions + * @since 2.0 + */ + public static final String FORMATTER_NEWLINE_ELSE_IF = PLUGIN_ID + ".formatter.newline.elseIf"; //$NON-NLS-1$ + /** + * Possible configurable option ID. + * @see #getDefaultOptions + * @since 2.0 + */ + public static final String FORMATTER_NEWLINE_EMPTY_BLOCK = PLUGIN_ID + ".formatter.newline.emptyBlock"; //$NON-NLS-1$ + /** + * Possible configurable option ID. + * @see #getDefaultOptions + * @since 2.0 + */ + public static final String FORMATTER_CLEAR_BLANK_LINES = PLUGIN_ID + ".formatter.newline.clearAll"; //$NON-NLS-1$ + /** + * Possible configurable option ID. + * @see #getDefaultOptions + * @since 2.0 + */ + public static final String FORMATTER_LINE_SPLIT = PLUGIN_ID + ".formatter.lineSplit"; //$NON-NLS-1$ + /** + * Possible configurable option ID. + * @see #getDefaultOptions + * @since 2.0 + */ + public static final String FORMATTER_COMPACT_ASSIGNMENT = PLUGIN_ID + ".formatter.style.assignment"; //$NON-NLS-1$ + /** + * Possible configurable option ID. + * @see #getDefaultOptions + * @since 2.0 + */ + public static final String FORMATTER_TAB_CHAR = PLUGIN_ID + ".formatter.tabulation.char"; //$NON-NLS-1$ + /** + * Possible configurable option ID. + * @see #getDefaultOptions + * @since 2.0 + */ + public static final String FORMATTER_TAB_SIZE = PLUGIN_ID + ".formatter.tabulation.size"; //$NON-NLS-1$ + /** + * Possible configurable option value. + * @see #getDefaultOptions + * @since 2.0 + */ + public static final String INSERT = "insert"; //$NON-NLS-1$ + /** + * Possible configurable option value. + * @see #getDefaultOptions + * @since 2.0 + */ + public static final String DO_NOT_INSERT = "do not insert"; //$NON-NLS-1$ + + /** + * Possible configurable option value. + * @see #getDefaultOptions + * @since 2.0 + */ + public static final String PRESERVE_ONE = "preserve one"; //$NON-NLS-1$ + /** + * Possible configurable option value. + * @see #getDefaultOptions + * @since 2.0 + */ + public static final String CLEAR_ALL = "clear all"; //$NON-NLS-1$ + /** + * Possible configurable option value. + * @see #getDefaultOptions + * @since 2.0 + */ + public static final String NORMAL = "normal"; //$NON-NLS-1$ + /** + * Possible configurable option value. + * @see #getDefaultOptions + * @since 2.0 + */ + public static final String COMPACT = "compact"; //$NON-NLS-1$ + /** + * Possible configurable option value. + * @see #getDefaultOptions + * @since 2.0 + */ + public static final String TAB = "tab"; //$NON-NLS-1$ + /** + * Possible configurable option value. + * @see #getDefaultOptions + * @since 2.0 + */ + public static final String SPACE = "space"; //$NON-NLS-1$ + /** + * Possible configurable option value. + * @see #getDefaultOptions + * @since 2.0 + */ + public static final String ENABLED = "enabled"; //$NON-NLS-1$ + /** + * Possible configurable option value. + * @see #getDefaultOptions + * @since 2.0 + */ + public static final String DISABLED = "disabled"; //$NON-NLS-1$ + /** + * Possible configurable option value. + * @see #getDefaultOptions + * @since 2.1 + */ + public static final String CLEAN = "clean"; //$NON-NLS-1$ + + /** + * Returns a table of all known configurable options with their default values. + * These options allow to configure the behaviour of the underlying components. + * The client may safely use the result as a template that they can modify and + * then pass to setOptions. + * + * Helper constants have been defined on JavaCore for each of the option ID and + * their possible constant values. + * + * Note: more options might be added in further releases. + *
+   * RECOGNIZED OPTIONS:
+   * COMPILER / Generating Local Variable Debug Attribute
+   *    When generated, this attribute will enable local variable names 
+   *    to be displayed in debugger, only in place where variables are 
+   *    definitely assigned (.class file is then bigger)
+   *     - option id:         "org.phpeclipse.phpdt.core.compiler.debug.localVariable"
+   *     - possible values:   { "generate", "do not generate" }
+   *     - default:           "generate"
+   *
+   * COMPILER / Generating Line Number Debug Attribute 
+   *    When generated, this attribute will enable source code highlighting in debugger 
+   *    (.class file is then bigger).
+   *     - option id:         "org.phpeclipse.phpdt.core.compiler.debug.lineNumber"
+   *     - possible values:   { "generate", "do not generate" }
+   *     - default:           "generate"
+   *    
+   * COMPILER / Generating Source Debug Attribute 
+   *    When generated, this attribute will enable the debugger to present the 
+   *    corresponding source code.
+   *     - option id:         "org.phpeclipse.phpdt.core.compiler.debug.sourceFile"
+   *     - possible values:   { "generate", "do not generate" }
+   *     - default:           "generate"
+   *    
+   * COMPILER / Preserving Unused Local Variables
+   *    Unless requested to preserve unused local variables (i.e. never read), the 
+   *    compiler will optimize them out, potentially altering debugging
+   *     - option id:         "org.phpeclipse.phpdt.core.compiler.codegen.unusedLocal"
+   *     - possible values:   { "preserve", "optimize out" }
+   *     - default:           "preserve"
+   * 
+   * COMPILER / Defining Target Java Platform
+   *    For binary compatibility reason, .class files can be tagged to with certain VM versions and later.
+   *    Note that "1.4" target require to toggle compliance mode to "1.4" too.
+   *     - option id:         "org.phpeclipse.phpdt.core.compiler.codegen.targetPlatform"
+   *     - possible values:   { "1.1", "1.2", "1.3", "1.4" }
+   *     - default:           "1.1"
+   *
+   * COMPILER / Reporting Unreachable Code
+   *    Unreachable code can optionally be reported as an error, warning or simply 
+   *    ignored. The bytecode generation will always optimized it out.
+   *     - option id:         "org.phpeclipse.phpdt.core.compiler.problem.unreachableCode"
+   *     - possible values:   { "error", "warning", "ignore" }
+   *     - default:           "error"
+   *
+   * COMPILER / Reporting Invalid Import
+   *    An import statement that cannot be resolved might optionally be reported 
+   *    as an error, as a warning or ignored.
+   *     - option id:         "org.phpeclipse.phpdt.core.compiler.problem.invalidImport"
+   *     - possible values:   { "error", "warning", "ignore" }
+   *     - default:           "error"
+   *
+   * COMPILER / Reporting Attempt to Override Package-Default Method
+   *    A package default method is not visible in a different package, and thus 
+   *    cannot be overridden. When enabling this option, the compiler will signal 
+   *    such scenarii either as an error or a warning.
+   *     - option id:         "org.phpeclipse.phpdt.core.compiler.problem.overridingPackageDefaultMethod"
+   *     - possible values:   { "error", "warning", "ignore" }
+   *     - default:           "warning"
+   *
+   * COMPILER / Reporting Method With Constructor Name
+   *    Naming a method with a constructor name is generally considered poor 
+   *    style programming. When enabling this option, the compiler will signal such 
+   *    scenarii either as an error or a warning.
+   *     - option id:         "org.phpeclipse.phpdt.core.compiler.problem.methodWithConstructorName"
+   *     - possible values:   { "error", "warning", "ignore" }
+   *     - default:           "warning"
+   *
+   * COMPILER / Reporting Deprecation
+   *    When enabled, the compiler will signal use of deprecated API either as an 
+   *    error or a warning.
+   *     - option id:         "org.phpeclipse.phpdt.core.compiler.problem.deprecation"
+   *     - possible values:   { "error", "warning", "ignore" }
+   *     - default:           "warning"
+   *
+   * COMPILER / Reporting Deprecation Inside Deprecated Code
+   *    When enabled, the compiler will signal use of deprecated API inside deprecated code.
+   *    The severity of the problem is controlled with option "org.phpeclipse.phpdt.core.compiler.problem.deprecation".
+   *     - option id:         "org.phpeclipse.phpdt.core.compiler.problem.deprecationInDeprecatedCode"
+   *     - possible values:   { "enabled", "disabled" }
+   *     - default:           "disabled"
+   *
+   * COMPILER / Reporting Hidden Catch Block
+   *    Locally to a try statement, some catch blocks may hide others , e.g.
+   *      try {  throw new java.io.CharConversionException();
+   *      } catch (java.io.CharConversionException e) {
+   *      } catch (java.io.IOException e) {}. 
+   *    When enabling this option, the compiler will issue an error or a warning for hidden 
+   *    catch blocks corresponding to checked exceptions
+   *     - option id:         "org.phpeclipse.phpdt.core.compiler.problem.hiddenCatchBlock"
+   *     - possible values:   { "error", "warning", "ignore" }
+   *     - default:           "warning"
+   *
+   * COMPILER / Reporting Unused Local
+   *    When enabled, the compiler will issue an error or a warning for unused local 
+   *    variables (i.e. variables never read from)
+   *     - option id:         "org.phpeclipse.phpdt.core.compiler.problem.unusedLocal"
+   *     - possible values:   { "error", "warning", "ignore" }
+   *     - default:           "ignore"
+   *
+   * COMPILER / Reporting Unused Parameter
+   *    When enabled, the compiler will issue an error or a warning for unused method 
+   *    parameters (i.e. parameters never read from)
+   *     - option id:         "org.phpeclipse.phpdt.core.compiler.problem.unusedParameter"
+   *     - possible values:   { "error", "warning", "ignore" }
+   *     - default:           "ignore"
+   *
+   * COMPILER / Reporting Unused Import
+   *    When enabled, the compiler will issue an error or a warning for unused import 
+   *    reference 
+   *     - option id:         "org.phpeclipse.phpdt.core.compiler.problem.unusedImport"
+   *     - possible values:   { "error", "warning", "ignore" }
+   *     - default:           "warning"
+   *
+   * COMPILER / Reporting Synthetic Access Emulation
+   *    When enabled, the compiler will issue an error or a warning whenever it emulates 
+   *    access to a non-accessible member of an enclosing type. Such access can have
+   *    performance implications.
+   *     - option id:         "org.phpeclipse.phpdt.core.compiler.problem.syntheticAccessEmulation"
+   *     - possible values:   { "error", "warning", "ignore" }
+   *     - default:           "ignore"
+   *
+   * COMPILER / Reporting Non-Externalized String Literal
+   *    When enabled, the compiler will issue an error or a warning for non externalized 
+   *    String literal (i.e. non tagged with //$NON-NLS-$). 
+   *     - option id:         "org.phpeclipse.phpdt.core.compiler.problem.nonExternalizedStringLiteral"
+   *     - possible values:   { "error", "warning", "ignore" }
+   *     - default:           "ignore"
+   * 
+   * COMPILER / Reporting Usage of 'assert' Identifier
+   *    When enabled, the compiler will issue an error or a warning whenever 'assert' is 
+   *    used as an identifier (reserved keyword in 1.4)
+   *     - option id:         "org.phpeclipse.phpdt.core.compiler.problem.assertIdentifier"
+   *     - possible values:   { "error", "warning", "ignore" }
+   *     - default:           "ignore"
+   * 
+   * COMPILER / Reporting Usage of expression receiver on static invocation/field access
+   *    When enabled, the compiler will issue an error or a warning whenever a static field
+   *    or method is accessed with an expression receiver.
+   *     - option id:         "org.phpeclipse.phpdt.core.compiler.problem.staticAccessReceiver"
+   *     - possible values:   { "error", "warning", "ignore" }
+   *     - default:           "warning"
+   * 
+   * COMPILER / Reporting Assignment with no effect
+   *    When enabled, the compiler will issue an error or a warning whenever an assignment
+   *    has no effect (e.g 'x = x').
+   *     - option id:         "org.phpeclipse.phpdt.core.compiler.problem.noEffectAssignment"
+   *     - possible values:   { "error", "warning", "ignore" }
+   *     - default:           "warning"
+   * 
+   * COMPILER / Setting Source Compatibility Mode
+   *    Specify whether source is 1.3 or 1.4 compatible. From 1.4 on, 'assert' is a keyword
+   *    reserved for assertion support. Also note, than when toggling to 1.4 mode, the target VM
+   *   level should be set to "1.4" and the compliance mode should be "1.4".
+   *     - option id:         "org.phpeclipse.phpdt.core.compiler.source"
+   *     - possible values:   { "1.3", "1.4" }
+   *     - default:           "1.3"
+   * 
+   * COMPILER / Setting Compliance Level
+   *    Select the compliance level for the compiler. In "1.3" mode, source and target settings
+   *    should not go beyond "1.3" level.
+   *     - option id:         "org.phpeclipse.phpdt.core.compiler.compliance"
+   *     - possible values:   { "1.3", "1.4" }
+   *     - default:           "1.3"
+   * 
+   * COMPILER / Maximum number of problems reported per compilation unit
+   *    Specify the maximum number of problems reported on each compilation unit.
+   *     - option id:         "org.phpeclipse.phpdt.core.compiler.maxProblemPerUnit"
+   *     - possible values:	"" where  is zero or a positive integer (if zero then all problems are reported).
+   *     - default:           "100"
+   * 
+   * COMPILER / Define the Automatic Task Tags
+   *    When the tag is non empty, the compiler will issue a task marker whenever it encounters
+   *    one of the corresponding tag inside any comment in Java source code.
+   *    Generated task messages will include the tag, and range until the next line separator or comment ending, and will be trimmed.
+   *     - option id:         "org.phpeclipse.phpdt.core.compiler.taskTags"
+   *     - possible values:   { "[,]*" } where  is a String without any wild-card 
+   *     - default:           ""
+   * 
+   * COMPILER / Define the Automatic Task Priorities
+   *    In parallel with the Automatic Task Tags, this list defines the priorities (high, normal or low)
+   *    of the task markers issued by the compiler.
+   *    If the default is specified, the priority of each task marker is "NORMAL".
+   *     - option id:         "org.phpeclipse.phpdt.core.compiler.taskPriorities"
+   *     - possible values:   { "[,]*" } where  is one of "HIGH", "NORMAL" or "LOW"
+   *     - default:           ""
+   * 
+   * BUILDER / Specifying Filters for Resource Copying Control
+   *    Allow to specify some filters to control the resource copy process.
+   *     - option id:         "org.phpeclipse.phpdt.core.builder.resourceCopyExclusionFilter"
+   *     - possible values:   { "[,]* } where  is a file name pattern (* and ? wild-cards allowed)
+   *       or the name of a folder which ends with '/'
+   *     - default:           ""
+   * 
+   * BUILDER / Abort if Invalid Classpath
+   *    Allow to toggle the builder to abort if the classpath is invalid
+   *     - option id:         "org.phpeclipse.phpdt.core.builder.invalidClasspath"
+   *     - possible values:   { "abort", "ignore" }
+   *     - default:           "ignore"
+   * 
+   * BUILDER / Cleaning Output Folder(s)
+   *    Indicate whether the JavaBuilder is allowed to clean the output folders
+   *    when performing full build operations.
+   *     - option id:         "org.phpeclipse.phpdt.core.builder.cleanOutputFolder"
+   *     - possible values:   { "clean", "ignore" }
+   *     - default:           "clean"
+   * 
+   * JAVACORE / Computing Project Build Order
+   *    Indicate whether JavaCore should enforce the project build order to be based on
+   *    the classpath prerequisite chain. When requesting to compute, this takes over
+   *    the platform default order (based on project references).
+   *     - option id:         "org.phpeclipse.phpdt.core.computeJavaBuildOrder"
+   *     - possible values:   { "compute", "ignore" }
+   *     - default:           "ignore"	 
+   * 
+   * JAVACORE / Specify Default Source Encoding Format
+   *    Get the encoding format for compiled sources. This setting is read-only, it is equivalent
+   *    to 'ResourcesPlugin.getEncoding()'.
+   *     - option id:         "org.phpeclipse.phpdt.core.encoding"
+   *     - possible values:   { any of the supported encoding name}.
+   *     - default:           
+   * 
+   * JAVACORE / Reporting Incomplete Classpath
+   *    An entry on the classpath doesn't exist or is not visible (e.g. a referenced project is closed).
+   *     - option id:         "org.phpeclipse.phpdt.core.incompleteClasspath"
+   *     - possible values:   { "error", "warning"}
+   *     - default:           "error"
+   * 
+   * JAVACORE / Reporting Classpath Cycle
+   *    A project is involved in a cycle.
+   *     - option id:         "org.phpeclipse.phpdt.core.circularClasspath"
+   *     - possible values:   { "error", "warning" }
+   *     - default:           "error"
+   * 
+   *	FORMATTER / Inserting New Line Before Opening Brace
+   *    When Insert, a new line is inserted before an opening brace, otherwise nothing
+   *    is inserted
+   *     - option id:         "org.phpeclipse.phpdt.core.formatter.newline.openingBrace"
+   *     - possible values:   { "insert", "do not insert" }
+   *     - default:           "do not insert"
+   * 
+   *	FORMATTER / Inserting New Line Inside Control Statement
+   *    When Insert, a new line is inserted between } and following else, catch, finally
+   *     - option id:         "org.phpeclipse.phpdt.core.formatter.newline.controlStatement"
+   *     - possible values:   { "insert", "do not insert" }
+   *     - default:           "do not insert"
+   * 
+   *	FORMATTER / Clearing Blank Lines
+   *    When Clear all, all blank lines are removed. When Preserve one, only one is kept
+   *    and all others removed.
+   *     - option id:         "org.phpeclipse.phpdt.core.formatter.newline.clearAll"
+   *     - possible values:   { "clear all", "preserve one" }
+   *     - default:           "preserve one"
+   * 
+   *	FORMATTER / Inserting New Line Between Else/If 
+   *    When Insert, a blank line is inserted between an else and an if when they are 
+   *    contiguous. When choosing to not insert, else-if will be kept on the same
+   *    line when possible.
+   *     - option id:         "org.phpeclipse.phpdt.core.formatter.newline.elseIf"
+   *     - possible values:   { "insert", "do not insert" }
+   *     - default:           "do not insert"
+   * 
+   *	FORMATTER / Inserting New Line In Empty Block
+   *    When insert, a line break is inserted between contiguous { and }, if } is not followed
+   *    by a keyword.
+   *     - option id:         "org.phpeclipse.phpdt.core.formatter.newline.emptyBlock"
+   *     - possible values:   { "insert", "do not insert" }
+   *     - default:           "insert"
+   * 
+   *	FORMATTER / Splitting Lines Exceeding Length
+   *    Enable splitting of long lines (exceeding the configurable length). Length of 0 will
+   *    disable line splitting
+   *     - option id:         "org.phpeclipse.phpdt.core.formatter.lineSplit"
+   *     - possible values:	"", where n is zero or a positive integer
+   *     - default:           "80"
+   * 
+   *	FORMATTER / Compacting Assignment
+   *    Assignments can be formatted asymmetrically, e.g. 'int x= 2;', when Normal, a space
+   *    is inserted before the assignment operator
+   *     - option id:         "org.phpeclipse.phpdt.core.formatter.style.assignment"
+   *     - possible values:   { "compact", "normal" }
+   *     - default:           "normal"
+   * 
+   *	FORMATTER / Defining Indentation Character
+   *    Either choose to indent with tab characters or spaces
+   *     - option id:         "org.phpeclipse.phpdt.core.formatter.tabulation.char"
+   *     - possible values:   { "tab", "space" }
+   *     - default:           "tab"
+   * 
+   *	FORMATTER / Defining Space Indentation Length
+   *    When using spaces, set the amount of space characters to use for each 
+   *    indentation mark.
+   *     - option id:         "org.phpeclipse.phpdt.core.formatter.tabulation.size"
+   *     - possible values:	"", where n is a positive integer
+   *     - default:           "4"
+   * 
+   *	CODEASSIST / Activate Visibility Sensitive Completion
+   *    When active, completion doesn't show that you can not see
+   *    (e.g. you can not see private methods of a super class).
+   *     - option id:         "org.phpeclipse.phpdt.core.codeComplete.visibilityCheck"
+   *     - possible values:   { "enabled", "disabled" }
+   *     - default:           "disabled"
+   * 
+   *	CODEASSIST / Automatic Qualification of Implicit Members
+   *    When active, completion automatically qualifies completion on implicit
+   *    field references and message expressions.
+   *     - option id:         "org.phpeclipse.phpdt.core.codeComplete.forceImplicitQualification"
+   *     - possible values:   { "enabled", "disabled" }
+   *     - default:           "disabled"
+   * 
+   *  CODEASSIST / Define the Prefixes for Field Name
+   *    When the prefixes is non empty, completion for field name will begin with
+   *    one of the proposed prefixes.
+   *     - option id:         "org.phpeclipse.phpdt.core.codeComplete.fieldPrefixes"
+   *     - possible values:   { "[,]*" } where  is a String without any wild-card 
+   *     - default:           ""
+   * 
+   *  CODEASSIST / Define the Prefixes for Static Field Name
+   *    When the prefixes is non empty, completion for static field name will begin with
+   *    one of the proposed prefixes.
+   *     - option id:         "org.phpeclipse.phpdt.core.codeComplete.staticFieldPrefixes"
+   *     - possible values:   { "[,]*" } where  is a String without any wild-card 
+   *     - default:           ""
+   * 
+   *  CODEASSIST / Define the Prefixes for Local Variable Name
+   *    When the prefixes is non empty, completion for local variable name will begin with
+   *    one of the proposed prefixes.
+   *     - option id:         "org.phpeclipse.phpdt.core.codeComplete.localPrefixes"
+   *     - possible values:   { "[,]*" } where  is a String without any wild-card 
+   *     - default:           ""
+   * 
+   *  CODEASSIST / Define the Prefixes for Argument Name
+   *    When the prefixes is non empty, completion for argument name will begin with
+   *    one of the proposed prefixes.
+   *     - option id:         "org.phpeclipse.phpdt.core.codeComplete.argumentPrefixes"
+   *     - possible values:   { "[,]*" } where  is a String without any wild-card 
+   *     - default:           ""
+   * 
+   *  CODEASSIST / Define the Suffixes for Field Name
+   *    When the suffixes is non empty, completion for field name will end with
+   *    one of the proposed suffixes.
+   *     - option id:         "org.phpeclipse.phpdt.core.codeComplete.fieldSuffixes"
+   *     - possible values:   { "[,]*" } where  is a String without any wild-card 
+   *     - default:           ""
+   * 
+   *  CODEASSIST / Define the Suffixes for Static Field Name
+   *    When the suffixes is non empty, completion for static field name will end with
+   *    one of the proposed suffixes.
+   *     - option id:         "org.phpeclipse.phpdt.core.codeComplete.staticFieldSuffixes"
+   *     - possible values:   { "[,]*" } where  is a String without any wild-card 
+   *     - default:           ""
+   * 
+   *  CODEASSIST / Define the Suffixes for Local Variable Name
+   *    When the suffixes is non empty, completion for local variable name will end with
+   *    one of the proposed suffixes.
+   *     - option id:         "org.phpeclipse.phpdt.core.codeComplete.localSuffixes"
+   *     - possible values:   { "[,]*" } where  is a String without any wild-card 
+   *     - default:           ""
+   * 
+   *  CODEASSIST / Define the Suffixes for Argument Name
+   *    When the suffixes is non empty, completion for argument name will end with
+   *    one of the proposed suffixes.
+   *     - option id:         "org.phpeclipse.phpdt.core.codeComplete.argumentSuffixes"
+   *     - possible values:   { "[,]*" } where  is a String without any wild-card 
+   *     - default:           ""
+   * 
+ * + * @return a mutable table containing the default settings of all known options + * (key type: String; value type: String) + * @see #setOptions + */ + public static Hashtable getDefaultOptions() { + + Hashtable defaultOptions = new Hashtable(10); + + // see #initializeDefaultPluginPreferences() for changing default settings + Preferences preferences = getPlugin().getPluginPreferences(); + HashSet optionNames = OptionNames; + + // get preferences set to their default + String[] defaultPropertyNames = preferences.defaultPropertyNames(); + for (int i = 0; i < defaultPropertyNames.length; i++) { + String propertyName = defaultPropertyNames[i]; + if (optionNames.contains(propertyName)) { + defaultOptions.put( + propertyName, + preferences.getDefaultString(propertyName)); + } + } + // get preferences not set to their default + String[] propertyNames = preferences.propertyNames(); + for (int i = 0; i < propertyNames.length; i++) { + String propertyName = propertyNames[i]; + if (optionNames.contains(propertyName)) { + defaultOptions.put( + propertyName, + preferences.getDefaultString(propertyName)); + } + } + // get encoding through resource plugin + defaultOptions.put(CORE_ENCODING, ResourcesPlugin.getEncoding()); + + return defaultOptions; + } + /** + * Helper method for returning one option value only. Equivalent to (String)JavaCore.getOptions().get(optionName) + * Note that it may answer null if this option does not exist. + *

+ * For a complete description of the configurable options, see getDefaultOptions. + *

+ * + * @param optionName the name of an option + * @return the String value of a given option + * @see JavaCore#getDefaultOptions + * @since 2.0 + */ + public static String getOption(String optionName) { + + if (CORE_ENCODING.equals(optionName)) { + return ResourcesPlugin.getEncoding(); + } + if (OptionNames.contains(optionName)) { + Preferences preferences = getPlugin().getPluginPreferences(); + return preferences.getString(optionName).trim(); + } + return null; + } + + /** + * Returns the table of the current options. Initially, all options have their default values, + * and this method returns a table that includes all known options. + *

+ * For a complete description of the configurable options, see getDefaultOptions. + *

+ * + * @return table of current settings of all options + * (key type: String; value type: String) + * @see JavaCore#getDefaultOptions + */ + public static Hashtable getOptions() { + + Hashtable options = new Hashtable(10); + + // see #initializeDefaultPluginPreferences() for changing default settings + Plugin plugin = getPlugin(); + if (plugin != null) { + Preferences preferences = getPlugin().getPluginPreferences(); + HashSet optionNames = OptionNames; + + // get preferences set to their default + String[] defaultPropertyNames = preferences.defaultPropertyNames(); + for (int i = 0; i < defaultPropertyNames.length; i++) { + String propertyName = defaultPropertyNames[i]; + if (optionNames.contains(propertyName)) { + options.put(propertyName, preferences.getDefaultString(propertyName)); + } + } + // get preferences not set to their default + String[] propertyNames = preferences.propertyNames(); + for (int i = 0; i < propertyNames.length; i++) { + String propertyName = propertyNames[i]; + if (optionNames.contains(propertyName)) { + options.put(propertyName, preferences.getString(propertyName).trim()); + } + } + // get encoding through resource plugin + options.put(CORE_ENCODING, ResourcesPlugin.getEncoding()); + } + return options; + } + /** + * Sets the current table of options. All and only the options explicitly included in the given table + * are remembered; all previous option settings are forgotten, including ones not explicitly + * mentioned. + *

+ * For a complete description of the configurable options, see getDefaultOptions. + *

+ * + * @param newOptions the new options (key type: String; value type: String), + * or null to reset all options to their default values + * @see JavaCore#getDefaultOptions + */ + public static void setOptions(Hashtable newOptions) { + + // see #initializeDefaultPluginPreferences() for changing default settings + Preferences preferences = getPlugin().getPluginPreferences(); + + if (newOptions == null) { + newOptions = getDefaultOptions(); + } + Enumeration keys = newOptions.keys(); + while (keys.hasMoreElements()) { + String key = (String) keys.nextElement(); + if (!OptionNames.contains(key)) + continue; // unrecognized option + if (key.equals(CORE_ENCODING)) + continue; // skipped, contributed by resource prefs + String value = (String) newOptions.get(key); + preferences.setValue(key, value); + } + + // persist options + getPlugin().savePluginPreferences(); + } public static IProject[] getPHPProjects() { List phpProjectsList = new ArrayList(); - IProject[] workspaceProjects = PHPeclipsePlugin.getWorkspace().getRoot().getProjects(); + IProject[] workspaceProjects = + PHPeclipsePlugin.getWorkspace().getRoot().getProjects(); for (int i = 0; i < workspaceProjects.length; i++) { IProject iProject = workspaceProjects[i]; @@ -29,7 +686,8 @@ public class PHPCore { } public static PHPProject getPHPProject(String name) { - IProject aProject = PHPeclipsePlugin.getWorkspace().getRoot().getProject(name); + IProject aProject = + PHPeclipsePlugin.getWorkspace().getRoot().getProject(name); if (isPHPProject(aProject)) { PHPProject thePHPProject = new PHPProject(); thePHPProject.setProject(aProject); @@ -72,13 +730,15 @@ public class PHPCore { return project; } } catch (CoreException e) { - System.err.println("Exception occurred in PHPCore#create(IProject): " + e.toString()); + System.err.println( + "Exception occurred in PHPCore#create(IProject): " + e.toString()); } return null; } - public static void addPHPNature(IProject project, IProgressMonitor monitor) throws CoreException { + public static void addPHPNature(IProject project, IProgressMonitor monitor) + throws CoreException { if (!project.hasNature(PHPeclipsePlugin.PHP_NATURE_ID)) { IProjectDescription description = project.getDescription(); String[] prevNatures = description.getNatureIds(); @@ -89,4 +749,184 @@ public class PHPCore { project.setDescription(description, monitor); } } + + /** + * Returns the single instance of the PHP core plug-in runtime class. + * + * @return the single instance of the PHP core plug-in runtime class + */ + public static Plugin getPlugin() { + return PHPeclipsePlugin.getDefault(); + } + +/** + * Initializes the default preferences settings for this plug-in. + */ + protected static void initializeDefaultPluginPreferences() { + + Preferences preferences = PHPeclipsePlugin.getDefault().getPluginPreferences(); + HashSet optionNames = OptionNames; + +// // Compiler settings +// preferences.setDefault(COMPILER_LOCAL_VARIABLE_ATTR, GENERATE); +// optionNames.add(COMPILER_LOCAL_VARIABLE_ATTR); +// +// preferences.setDefault(COMPILER_LINE_NUMBER_ATTR, GENERATE); +// optionNames.add(COMPILER_LINE_NUMBER_ATTR); +// +// preferences.setDefault(COMPILER_SOURCE_FILE_ATTR, GENERATE); +// optionNames.add(COMPILER_SOURCE_FILE_ATTR); +// +// preferences.setDefault(COMPILER_CODEGEN_UNUSED_LOCAL, PRESERVE); +// optionNames.add(COMPILER_CODEGEN_UNUSED_LOCAL); +// +// preferences.setDefault(COMPILER_CODEGEN_TARGET_PLATFORM, VERSION_1_1); +// optionNames.add(COMPILER_CODEGEN_TARGET_PLATFORM); +// +// preferences.setDefault(COMPILER_PB_UNREACHABLE_CODE, ERROR); +// optionNames.add(COMPILER_PB_UNREACHABLE_CODE); +// +// preferences.setDefault(COMPILER_PB_INVALID_IMPORT, ERROR); +// optionNames.add(COMPILER_PB_INVALID_IMPORT); +// +// preferences.setDefault(COMPILER_PB_OVERRIDING_PACKAGE_DEFAULT_METHOD, WARNING); +// optionNames.add(COMPILER_PB_OVERRIDING_PACKAGE_DEFAULT_METHOD); +// +// preferences.setDefault(COMPILER_PB_METHOD_WITH_CONSTRUCTOR_NAME, WARNING); +// optionNames.add(COMPILER_PB_METHOD_WITH_CONSTRUCTOR_NAME); +// +// preferences.setDefault(COMPILER_PB_DEPRECATION, WARNING); +// optionNames.add(COMPILER_PB_DEPRECATION); +// +// preferences.setDefault(COMPILER_PB_DEPRECATION_IN_DEPRECATED_CODE, DISABLED); +// optionNames.add(COMPILER_PB_DEPRECATION_IN_DEPRECATED_CODE); +// +// preferences.setDefault(COMPILER_PB_HIDDEN_CATCH_BLOCK, WARNING); +// optionNames.add(COMPILER_PB_HIDDEN_CATCH_BLOCK); +// +// preferences.setDefault(COMPILER_PB_UNUSED_LOCAL, IGNORE); +// optionNames.add(COMPILER_PB_UNUSED_LOCAL); +// +// preferences.setDefault(COMPILER_PB_UNUSED_PARAMETER, IGNORE); +// optionNames.add(COMPILER_PB_UNUSED_PARAMETER); +// +// preferences.setDefault(COMPILER_PB_UNUSED_IMPORT, WARNING); +// optionNames.add(COMPILER_PB_UNUSED_IMPORT); +// +// preferences.setDefault(COMPILER_PB_SYNTHETIC_ACCESS_EMULATION, IGNORE); +// optionNames.add(COMPILER_PB_SYNTHETIC_ACCESS_EMULATION); +// +// preferences.setDefault(COMPILER_PB_NON_NLS_STRING_LITERAL, IGNORE); +// optionNames.add(COMPILER_PB_NON_NLS_STRING_LITERAL); +// +// preferences.setDefault(COMPILER_PB_ASSERT_IDENTIFIER, IGNORE); +// optionNames.add(COMPILER_PB_ASSERT_IDENTIFIER); +// +// preferences.setDefault(COMPILER_PB_STATIC_ACCESS_RECEIVER, WARNING); +// optionNames.add(COMPILER_PB_STATIC_ACCESS_RECEIVER); +// +// preferences.setDefault(COMPILER_PB_NO_EFFECT_ASSIGNMENT, WARNING); +// optionNames.add(COMPILER_PB_NO_EFFECT_ASSIGNMENT); +// +// preferences.setDefault(COMPILER_TASK_TAGS, ""); //$NON-NLS-1$ +// optionNames.add(COMPILER_TASK_TAGS); +// +// preferences.setDefault(COMPILER_TASK_PRIORITIES, ""); //$NON-NLS-1$ +// optionNames.add(COMPILER_TASK_PRIORITIES); +// +// preferences.setDefault(COMPILER_SOURCE, VERSION_1_3); +// optionNames.add(COMPILER_SOURCE); +// +// preferences.setDefault(COMPILER_COMPLIANCE, VERSION_1_3); +// optionNames.add(COMPILER_COMPLIANCE); +// +// preferences.setDefault(COMPILER_PB_MAX_PER_UNIT, "100"); //$NON-NLS-1$ +// optionNames.add(COMPILER_PB_MAX_PER_UNIT); +// +// // Builder settings +// preferences.setDefault(CORE_JAVA_BUILD_RESOURCE_COPY_FILTER, ""); //$NON-NLS-1$ +// optionNames.add(CORE_JAVA_BUILD_RESOURCE_COPY_FILTER); +// +// preferences.setDefault(CORE_JAVA_BUILD_INVALID_CLASSPATH, ABORT); +// optionNames.add(CORE_JAVA_BUILD_INVALID_CLASSPATH); +// +// preferences.setDefault(CORE_JAVA_BUILD_DUPLICATE_RESOURCE, WARNING); +// optionNames.add(CORE_JAVA_BUILD_DUPLICATE_RESOURCE); +// +// preferences.setDefault(CORE_JAVA_BUILD_CLEAN_OUTPUT_FOLDER, CLEAN); +// optionNames.add(CORE_JAVA_BUILD_CLEAN_OUTPUT_FOLDER); +// +// // JavaCore settings +// preferences.setDefault(CORE_JAVA_BUILD_ORDER, IGNORE); +// optionNames.add(CORE_JAVA_BUILD_ORDER); +// +// preferences.setDefault(CORE_CIRCULAR_CLASSPATH, ERROR); +// optionNames.add(CORE_CIRCULAR_CLASSPATH); +// +// preferences.setDefault(CORE_INCOMPLETE_CLASSPATH, ERROR); +// optionNames.add(CORE_INCOMPLETE_CLASSPATH); + + // encoding setting comes from resource plug-in + optionNames.add(CORE_ENCODING); + + // Formatter settings + preferences.setDefault(FORMATTER_NEWLINE_OPENING_BRACE, DO_NOT_INSERT); + optionNames.add(FORMATTER_NEWLINE_OPENING_BRACE); + + preferences.setDefault(FORMATTER_NEWLINE_CONTROL, DO_NOT_INSERT); + optionNames.add(FORMATTER_NEWLINE_CONTROL); + + preferences.setDefault(FORMATTER_CLEAR_BLANK_LINES, PRESERVE_ONE); + optionNames.add(FORMATTER_CLEAR_BLANK_LINES); + + preferences.setDefault(FORMATTER_NEWLINE_ELSE_IF, DO_NOT_INSERT); + optionNames.add(FORMATTER_NEWLINE_ELSE_IF); + + preferences.setDefault(FORMATTER_NEWLINE_EMPTY_BLOCK, INSERT); + optionNames.add(FORMATTER_NEWLINE_EMPTY_BLOCK); + + preferences.setDefault(FORMATTER_LINE_SPLIT, "80"); //$NON-NLS-1$ + optionNames.add(FORMATTER_LINE_SPLIT); + + preferences.setDefault(FORMATTER_COMPACT_ASSIGNMENT, NORMAL); + optionNames.add(FORMATTER_COMPACT_ASSIGNMENT); + + preferences.setDefault(FORMATTER_TAB_CHAR, TAB); + optionNames.add(FORMATTER_TAB_CHAR); + + preferences.setDefault(FORMATTER_TAB_SIZE, "4"); //$NON-NLS-1$ + optionNames.add(FORMATTER_TAB_SIZE); + + // CodeAssist settings +// preferences.setDefault(CODEASSIST_VISIBILITY_CHECK, DISABLED); //$NON-NLS-1$ +// optionNames.add(CODEASSIST_VISIBILITY_CHECK); +// +// preferences.setDefault(CODEASSIST_IMPLICIT_QUALIFICATION, DISABLED); //$NON-NLS-1$ +// optionNames.add(CODEASSIST_IMPLICIT_QUALIFICATION); +// +// preferences.setDefault(CODEASSIST_FIELD_PREFIXES, ""); //$NON-NLS-1$ +// optionNames.add(CODEASSIST_FIELD_PREFIXES); +// +// preferences.setDefault(CODEASSIST_STATIC_FIELD_PREFIXES, ""); //$NON-NLS-1$ +// optionNames.add(CODEASSIST_STATIC_FIELD_PREFIXES); +// +// preferences.setDefault(CODEASSIST_LOCAL_PREFIXES, ""); //$NON-NLS-1$ +// optionNames.add(CODEASSIST_LOCAL_PREFIXES); +// +// preferences.setDefault(CODEASSIST_ARGUMENT_PREFIXES, ""); //$NON-NLS-1$ +// optionNames.add(CODEASSIST_ARGUMENT_PREFIXES); +// +// preferences.setDefault(CODEASSIST_FIELD_SUFFIXES, ""); //$NON-NLS-1$ +// optionNames.add(CODEASSIST_FIELD_SUFFIXES); +// +// preferences.setDefault(CODEASSIST_STATIC_FIELD_SUFFIXES, ""); //$NON-NLS-1$ +// optionNames.add(CODEASSIST_STATIC_FIELD_SUFFIXES); +// +// preferences.setDefault(CODEASSIST_LOCAL_SUFFIXES, ""); //$NON-NLS-1$ +// optionNames.add(CODEASSIST_LOCAL_SUFFIXES); +// +// preferences.setDefault(CODEASSIST_ARGUMENT_SUFFIXES, ""); //$NON-NLS-1$ +// optionNames.add(CODEASSIST_ARGUMENT_SUFFIXES); + + } } \ No newline at end of file diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/PHPeclipsePlugin.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/PHPeclipsePlugin.java index 7bfbf25..24e9395 100644 --- a/net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/PHPeclipsePlugin.java +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/PHPeclipsePlugin.java @@ -381,8 +381,10 @@ public class PHPeclipsePlugin store.setDefault(PHP_OUTLINE_VAR, "true"); //$NON-NLS-1$ TemplatePreferencePage.initDefaults(store); - new PHPSyntaxRdr(); //this will initialize the static fields in the syntaxrdr class + new PHPSyntaxRdr(); + + PHPCore.initializeDefaultPluginPreferences(); } public void startup() throws CoreException { diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/phpeditor/PHPSourceViewerConfiguration.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/phpeditor/PHPSourceViewerConfiguration.java index af8b262..09662be 100644 --- a/net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/phpeditor/PHPSourceViewerConfiguration.java +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/phpeditor/PHPSourceViewerConfiguration.java @@ -13,6 +13,7 @@ package net.sourceforge.phpeclipse.phpeditor; import java.util.Vector; +import net.sourceforge.phpdt.internal.ui.text.java.JavaFormattingStrategy; import net.sourceforge.phpeclipse.PHPeclipsePlugin; import net.sourceforge.phpeclipse.phpeditor.html.HTMLFormattingStrategy; import net.sourceforge.phpeclipse.phpeditor.php.HTMLCompletionProcessor; @@ -33,6 +34,7 @@ import org.eclipse.jface.text.contentassist.ContentAssistant; import org.eclipse.jface.text.contentassist.IContentAssistant; import org.eclipse.jface.text.formatter.ContentFormatter; import org.eclipse.jface.text.formatter.IContentFormatter; +import org.eclipse.jface.text.formatter.IFormattingStrategy; import org.eclipse.jface.text.presentation.IPresentationReconciler; import org.eclipse.jface.text.presentation.PresentationReconciler; import org.eclipse.jface.text.rules.BufferedRuleBasedScanner; @@ -75,16 +77,39 @@ public class PHPSourceViewerConfiguration extends SourceViewerConfiguration { * @see SourceViewerConfiguration#getContentFormatter(ISourceViewer) */ public IContentFormatter getContentFormatter(ISourceViewer sourceViewer) { +// if (fFormatter == null) { +// fFormatter = new ContentFormatter(); +// fFormattingStrategy = new HTMLFormattingStrategy(this, sourceViewer); +// fFormatter.setFormattingStrategy(fFormattingStrategy, HTML_DEFAULT); +// fFormatter.enablePartitionAwareFormatting(false); +// fFormatter.setPartitionManagingPositionCategories(getConfiguredContentTypes(null)); +// } +// return fFormatter; + if (fFormatter == null) { - fFormatter = new ContentFormatter(); - fFormattingStrategy = new HTMLFormattingStrategy(this, sourceViewer); - fFormatter.setFormattingStrategy(fFormattingStrategy, HTML_DEFAULT); - fFormatter.enablePartitionAwareFormatting(false); - fFormatter.setPartitionManagingPositionCategories(getConfiguredContentTypes(null)); + //ContentFormatter + fFormatter= new ContentFormatter(); + IFormattingStrategy strategy= new JavaFormattingStrategy(sourceViewer); + + fFormatter.setFormattingStrategy(strategy, IDocument.DEFAULT_CONTENT_TYPE); + fFormatter.enablePartitionAwareFormatting(false); + fFormatter.setPartitionManagingPositionCategories(getPartitionManagingPositionCategories()); } return fFormatter; } + /** + * Returns the names of the document position categories used by the document + * partitioners created by this object to manage their partition information. + * If the partitioners don't use document position categories, the returned + * result is null. + * + * @return the partition managing position categories or null + * if there is none + */ + public String[] getPartitionManagingPositionCategories() { + return new String[] { DefaultPartitioner.CONTENT_TYPES_CATEGORY }; + } // /** // * Returns the names of the document position categories used by the document // * partitioners created by this object to manage their partition information. diff --git a/net.sourceforge.phpeclipse/src/org/phpeclipse/phpdt/internal/formatter/CodeFormatter.java b/net.sourceforge.phpeclipse/src/org/phpeclipse/phpdt/internal/formatter/CodeFormatter.java new file mode 100644 index 0000000..0366a03 --- /dev/null +++ b/net.sourceforge.phpeclipse/src/org/phpeclipse/phpdt/internal/formatter/CodeFormatter.java @@ -0,0 +1,2556 @@ +/******************************************************************************* + * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v0.5 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v05.html + * + * Contributors: + * IBM Corporation - initial API and implementation + ******************************************************************************/ +package org.phpeclipse.phpdt.internal.formatter; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.Hashtable; +import java.util.Locale; +import java.util.Map; + +import net.sourceforge.phpdt.core.ICodeFormatter; +import net.sourceforge.phpdt.core.compiler.CharOperation; +import net.sourceforge.phpdt.core.compiler.ITerminalSymbols; +import net.sourceforge.phpdt.core.compiler.InvalidInputException; +import net.sourceforge.phpdt.internal.compiler.ConfigurableOption; +import net.sourceforge.phpdt.internal.compiler.parser.Scanner; + +import org.phpeclipse.phpdt.internal.formatter.impl.FormatterOptions; +import org.phpeclipse.phpdt.internal.formatter.impl.SplitLine; + +/**

How to format a piece of code ?

+ *
  • Create an instance of CodeFormatter + *
  • Use the method void format(aString) + * on this instance to format aString. + * It will return the formatted string.
+*/ +public class CodeFormatter implements ITerminalSymbols, ICodeFormatter { + + public FormatterOptions options; + + /** + * Represents a block in the constructions stack. + */ + public static final int BLOCK = ITerminalSymbols.TokenNameLBRACE; + + /** + * Represents a block following a control statement in the constructions stack. + */ + public static final int NONINDENT_BLOCK = -100; + + /** + * Contains the formatted output. + */ + StringBuffer formattedSource; + + /** + * Contains the current line.
+ * Will be dumped at the next "newline" + */ + StringBuffer currentLineBuffer; + + /** + * Used during the formatting to get each token. + */ + Scanner scanner; + + /** + * Contains the tokens responsible for the current indentation level + * and the blocks not closed yet. + */ + private int[] constructions; + + /** + * Index in the constructions array. + */ + private int constructionsCount; + + /** + * Level of indentation of the current token (number of tab char put in front of it). + */ + private int indentationLevel; + + /** + * Regular level of indentation of all the lines + */ + private int initialIndentationLevel; + + /** + * Used to split a line. + */ + Scanner splitScanner; + + /** + * To remember the offset between the beginning of the line and the + * beginning of the comment. + */ + int currentCommentOffset; + int currentLineIndentationLevel; + int maxLineSize = 30; + private boolean containsOpenCloseBraces; + private int indentationLevelForOpenCloseBraces; + + /** + * Collections of positions to map + */ + private int[] positionsToMap; + + /** + * Collections of mapped positions + */ + private int[] mappedPositions; + + private int indexToMap; + + private int indexInMap; + + private int globalDelta; + + private int lineDelta; + + private int splitDelta; + + private int beginningOfLineIndex; + + private int multipleLineCommentCounter; + + /** + * Creates a new instance of Code Formatter using the given settings. + * + * @deprecated backport 1.0 internal functionality + */ + public CodeFormatter(ConfigurableOption[] settings) { + this(convertConfigurableOptions(settings)); + } + + /** + * Creates a new instance of Code Formatter using the FormattingOptions object + * given as argument + * @deprecated Use CodeFormatter(ConfigurableOption[]) instead + */ + public CodeFormatter() { + this((Map)null); + } + /** + * Creates a new instance of Code Formatter using the given settings. + */ + public CodeFormatter(Map settings) { + + // initialize internal state + constructionsCount = 0; + constructions = new int[10]; + currentLineIndentationLevel = indentationLevel = initialIndentationLevel; + currentCommentOffset = -1; + + // initialize primary and secondary scanners + scanner = new Scanner(true /*comment*/, true /*whitespace*/, false /*nls*/, false /*assert*/); // regular scanner for forming lines + scanner.recordLineSeparator = true; + + // to remind of the position of the beginning of the line. + splitScanner = new Scanner(true /*comment*/, true /*whitespace*/, false /*nls*/, false /*assert*/); + // secondary scanner to split long lines formed by primary scanning + + // initialize current line buffer + currentLineBuffer = new StringBuffer(); + this.options = new FormatterOptions(settings); + } + + /** + * Returns true if a lineSeparator has to be inserted before operator + * false otherwise. + */ + private static boolean breakLineBeforeOperator(int operator) { + switch (operator) { + case TokenNameCOMMA : + case TokenNameSEMICOLON : + case TokenNameEQUAL : + return false; + default : + return true; + } + } + + /** + * @deprecated backport 1.0 internal functionality + */ + private static Map convertConfigurableOptions(ConfigurableOption[] settings) { + Hashtable options = new Hashtable(10); + + for (int i = 0; i < settings.length; i++) { + if(settings[i].getComponentName().equals(CodeFormatter.class.getName())){ + String optionName = settings[i].getOptionName(); + int valueIndex = settings[i].getCurrentValueIndex(); + + if(optionName.equals("newline.openingBrace")) { //$NON-NLS-1$ + options.put("org.phpeclipse.phpdt.core.formatter.newline.openingBrace", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + } else if(optionName.equals("newline.controlStatement")) { //$NON-NLS-1$ + options.put("org.phpeclipse.phpdt.core.formatter.newline.controlStatement", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + } else if(optionName.equals("newline.clearAll")) { //$NON-NLS-1$ + options.put("org.phpeclipse.phpdt.core.formatter.newline.clearAll", valueIndex == 0 ? "clear all" : "preserve one"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + } else if(optionName.equals("newline.elseIf")) { //$NON-NLS-1$ + options.put("org.phpeclipse.phpdt.core.formatter.newline.elseIf", valueIndex == 0 ? "do not insert" : "insert" ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + } else if(optionName.equals("newline.emptyBlock")) { //$NON-NLS-1$ + options.put("org.phpeclipse.phpdt.core.formatter.newline.emptyBlock", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + } else if(optionName.equals("lineSplit")) { //$NON-NLS-1$ + options.put("org.phpeclipse.phpdt.core.formatter.lineSplit", String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$ + + } else if(optionName.equals("style.assignment")) { //$NON-NLS-1$ + options.put("org.phpeclipse.phpdt.core.formatter.style.assignment", valueIndex == 0 ? "compact" : "normal"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + } else if(optionName.equals("tabulation.char")) { //$NON-NLS-1$ + options.put("org.phpeclipse.phpdt.core.formatter.tabulation.char", valueIndex == 0 ? "tab" : "space"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + } else if(optionName.equals("tabulation.size")) { //$NON-NLS-1$ + options.put("org.phpeclipse.phpdt.core.formatter.tabulation.size", String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + } + + return options; + } + + /** + * Returns the end of the source code. + */ + private final String copyRemainingSource() { + char str[] = scanner.source; + int startPosition = scanner.startPosition; + int length = str.length - startPosition; + StringBuffer bufr = new StringBuffer(length); + if (startPosition < str.length) { + bufr.append(str, startPosition, length); + } + return (bufr.toString()); + } + + /** + * Inserts tabCount tab character or their equivalent number of spaces. + */ + private void dumpTab(int tabCount) { + if (options.indentWithTab) { + for (int j = 0; j < tabCount; j++) { + formattedSource.append('\t'); + increaseSplitDelta(1); + } + } else { + for (int i = 0, max = options.tabSize * tabCount; i < max; i++) { + formattedSource.append(' '); + increaseSplitDelta(1); + } + } + } + + /** + * Dumps currentLineBuffer into the formatted string. + */ + private void flushBuffer() { + String currentString = currentLineBuffer.toString(); + splitDelta = 0; + beginningOfLineIndex = formattedSource.length(); + if (containsOpenCloseBraces) { + containsOpenCloseBraces = false; + outputLine( + currentString, + false, + indentationLevelForOpenCloseBraces, + 0, + -1, + null, + 0); + indentationLevelForOpenCloseBraces = currentLineIndentationLevel; + } else { + outputLine(currentString, false, currentLineIndentationLevel, 0, -1, null, 0); + } + int scannerSourceLength = scanner.source.length; + if (scannerSourceLength > 2) { + if (scanner.source[scannerSourceLength - 1] == '\n' && + scanner.source[scannerSourceLength - 2] == '\r') { + formattedSource.append(options.lineSeparatorSequence); + increaseGlobalDelta(options.lineSeparatorSequence.length - 2); + } else if (scanner.source[scannerSourceLength - 1] == '\n') { + formattedSource.append(options.lineSeparatorSequence); + increaseGlobalDelta(options.lineSeparatorSequence.length - 1); + } else if (scanner.source[scannerSourceLength - 1] == '\r') { + formattedSource.append(options.lineSeparatorSequence); + increaseGlobalDelta(options.lineSeparatorSequence.length - 1); + } + } + updateMappedPositions(scanner.startPosition); + } + + /** + * Formats the input string. + */ + private void format() { + int token = 0; + int previousToken = 0; + int previousCompilableToken = 0; + int indentationOffset = 0; + int newLinesInWhitespace = 0; + + // number of new lines in the previous whitespace token + // (used to leave blank lines before comments) + int pendingNewLines = 0; + boolean expectingOpenBrace = false; + boolean clearNonBlockIndents = false; + // true if all indentations till the 1st { (usefull after } or ;) + boolean pendingSpace = true; + boolean pendingNewlineAfterParen = false; + // true when a cr is to be put after a ) (in conditional statements) + boolean inAssignment = false; + boolean inArrayAssignment = false; + boolean inThrowsClause = false; + boolean inClassOrInterfaceHeader = false; + + // openBracketCount is used to count the number of open brackets not closed yet. + int openBracketCount = 0; + int unarySignModifier = 0; + + // openParenthesis[0] is used to count the parenthesis not belonging to a condition + // (eg foo();). parenthesis in for (...) are count elsewhere in the array. + int openParenthesisCount = 1; + int[] openParenthesis = new int[10]; + + // tokenBeforeColon is used to know what token goes along with the current : + // it can be case or ? + int tokenBeforeColonCount = 0; + int[] tokenBeforeColon = new int[10]; + + constructionsCount = 0; // initializes the constructions count. + + // contains DO if in a DO..WHILE statement, UNITIALIZED otherwise. + int nlicsToken = 0; + + // fix for 1FF17XY: LFCOM:ALL - Format problem on not matching } and else + boolean specialElse = false; + + // OPTION (IndentationLevel): initial indentation level may be non-zero. + currentLineIndentationLevel += constructionsCount; + + // An InvalidInputException exception might cause the termination of this loop. + try { + while (true) { + // Get the next token. Catch invalid input and output it + // with minimal formatting, also catch end of input and + // exit the loop. + try { + token = scanner.getNextToken(); + + // Patch for line comment + // See PR http://dev.eclipse.org/bugs/show_bug.cgi?id=23096 + if (token == ITerminalSymbols.TokenNameCOMMENT_LINE) { + int length = scanner.currentPosition; + loop: for (int index = length - 1; index >= 0; index--) { + switch(scanner.source[index]) { + case '\r' : + case '\n' : + scanner.currentPosition--; + break; + default: + break loop; + } + } + } + } catch (InvalidInputException e) { + if (!handleInvalidToken(e)) { + throw e; + } + token = 0; + } + if (token == Scanner.TokenNameEOF) + break; + + /* ## MODIFYING the indentation level before generating new lines + and indentation in the output string + */ + + // Removes all the indentations made by statements not followed by a block + // except if the current token is ELSE, CATCH or if we are in a switch/case + if (clearNonBlockIndents && (token != Scanner.TokenNameWHITESPACE)) { + switch (token) { + case TokenNameelse : + if (constructionsCount > 0 + && constructions[constructionsCount - 1] == TokenNameelse) { + pendingNewLines = 1; + specialElse = true; + } + indentationLevel += popInclusiveUntil(TokenNameif); + break; +// case TokenNamecatch : +// indentationLevel += popInclusiveUntil(TokenNamecatch); +// break; +// case TokenNamefinally : +// indentationLevel += popInclusiveUntil(TokenNamecatch); +// break; + case TokenNamewhile : + if (nlicsToken == TokenNamedo) { + indentationLevel += pop(TokenNamedo); + break; + } + default : + indentationLevel += popExclusiveUntilBlockOrCase(); + // clear until a CASE, DEFAULT or BLOCK is encountered. + // Thus, the indentationLevel is correctly cleared either + // in a switch/case statement or in any other situation. + } + clearNonBlockIndents = false; + } + // returns to the indentation level created by the SWITCH keyword + // if the current token is a CASE or a DEFAULT + if (token == TokenNamecase || token == TokenNamedefault) { + indentationLevel += pop(TokenNamecase); + } +// if (token == Scanner.TokenNamethrows) { +// inThrowsClause = true; +// } + if ((token == Scanner.TokenNameclass + // || token == Scanner.TokenNameinterface + ) && previousToken != Scanner.TokenNameDOT) { + inClassOrInterfaceHeader = true; + } + + /* ## APPEND newlines and indentations to the output string + */ + // Do not add a new line between ELSE and IF, if the option elseIfOnSameLine is true. + // Fix for 1ETLWPZ: IVJCOM:ALL - incorrect "else if" formatting + if (pendingNewlineAfterParen + && previousCompilableToken == TokenNameelse + && token == TokenNameif + && options.compactElseIfMode) { + pendingNewlineAfterParen = false; + pendingNewLines = 0; + indentationLevel += pop(TokenNameelse); + // because else if is now one single statement, + // the indentation level after it is increased by one and not by 2 + // (else = 1 indent, if = 1 indent, but else if = 1 indent, not 2). + } + // Add a newline & indent to the formatted source string if + // a for/if-else/while statement was scanned and there is no block + // following it. + pendingNewlineAfterParen = + pendingNewlineAfterParen + || (previousCompilableToken == TokenNameRPAREN && token == TokenNameLBRACE); + if (pendingNewlineAfterParen && token != Scanner.TokenNameWHITESPACE) { + pendingNewlineAfterParen = false; + + // Do to add a newline & indent sequence if the current token is an + // open brace or a period or if the current token is a semi-colon and the + // previous token is a close paren. + // add a new line if a parenthesis belonging to a for() statement + // has been closed and the current token is not an opening brace + if (token != TokenNameLBRACE + && !isComment(token) // to avoid adding new line between else and a comment + && token != TokenNameDOT + && !(previousCompilableToken == TokenNameRPAREN && token == TokenNameSEMICOLON)) { + newLine(1); + currentLineIndentationLevel = indentationLevel; + pendingNewLines = 0; + pendingSpace = false; + } else { + if (token == TokenNameLBRACE && options.newLineBeforeOpeningBraceMode) { + newLine(1); + if (constructionsCount > 0 + && constructions[constructionsCount - 1] != BLOCK + && constructions[constructionsCount - 1] != NONINDENT_BLOCK) { + currentLineIndentationLevel = indentationLevel - 1; + } else { + currentLineIndentationLevel = indentationLevel; + } + pendingNewLines = 0; + pendingSpace = false; + } + } + } + if (token == TokenNameLBRACE + && options.newLineBeforeOpeningBraceMode + && constructionsCount > 0 + && constructions[constructionsCount - 1] == TokenNamedo) { + newLine(1); + currentLineIndentationLevel = indentationLevel - 1; + pendingNewLines = 0; + pendingSpace = false; + } + // see PR 1G5G8EC + if (token == TokenNameLBRACE && inThrowsClause) { + inThrowsClause = false; + if (options.newLineBeforeOpeningBraceMode) { + newLine(1); + currentLineIndentationLevel = indentationLevel; + pendingNewLines = 0; + pendingSpace = false; + } + } + // see PR 1G5G82G + if (token == TokenNameLBRACE && inClassOrInterfaceHeader) { + inClassOrInterfaceHeader = false; + if (options.newLineBeforeOpeningBraceMode) { + newLine(1); + currentLineIndentationLevel = indentationLevel; + pendingNewLines = 0; + pendingSpace = false; + } + } + // Add pending new lines to the formatted source string. + // Note: pending new lines are not added if the current token + // is a single line comment or whitespace. + // if the comment is between parenthesis, there is no blank line preservation + // (if it's a one-line comment, a blank line is added after it). + if (((pendingNewLines > 0 && (!isComment(token))) + || (newLinesInWhitespace > 0 && (openParenthesisCount <= 1 && isComment(token))) + || (previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE)) + && token != Scanner.TokenNameWHITESPACE) { + + // Do not add newline & indent between an adjoining close brace and + // close paren. Anonymous inner classes may use this form. + boolean closeBraceAndCloseParen = + previousToken == TokenNameRBRACE && token == TokenNameRPAREN; + + // OPTION (NewLineInCompoundStatement): do not add newline & indent + // between close brace and else, (do) while, catch, and finally if + // newlineInCompoundStatement is true. + boolean nlicsOption = + previousToken == TokenNameRBRACE + && !options.newlineInControlStatementMode + && (token == TokenNameelse + || (token == TokenNamewhile && nlicsToken == TokenNamedo)); +// || token == TokenNamecatch +// || token == TokenNamefinally); + + // Do not add a newline & indent between a close brace and semi-colon. + boolean semiColonAndCloseBrace = + previousToken == TokenNameRBRACE && token == TokenNameSEMICOLON; + + // Do not add a new line & indent between a multiline comment and a opening brace + boolean commentAndOpenBrace = + previousToken == Scanner.TokenNameCOMMENT_BLOCK && token == TokenNameLBRACE; + + // Do not add a newline & indent between a close brace and a colon (in array assignments, for example). + boolean commaAndCloseBrace = + previousToken == TokenNameRBRACE && token == TokenNameCOMMA; + + // Add a newline and indent, if appropriate. + if (specialElse + || (!commentAndOpenBrace + && !closeBraceAndCloseParen + && !nlicsOption + && !semiColonAndCloseBrace + && !commaAndCloseBrace)) { + + // if clearAllBlankLinesMode=false, leaves the blank lines + // inserted by the user + // if clearAllBlankLinesMode=true, removes all of then + // and insert only blank lines required by the formatting. + if (!options.clearAllBlankLinesMode) { + // (isComment(token)) + pendingNewLines = + (pendingNewLines < newLinesInWhitespace) + ? newLinesInWhitespace + : pendingNewLines; + pendingNewLines = (pendingNewLines > 2) ? 2 : pendingNewLines; + } + if (previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE) { + containsOpenCloseBraces = true; + indentationLevelForOpenCloseBraces = currentLineIndentationLevel; + if (isComment(previousToken)) { + newLine(pendingNewLines); + } else { + /* if (!(constructionsCount > 1 + && constructions[constructionsCount-1] == NONINDENT_BLOCK + && (constructions[constructionsCount-2] == TokenNamefor + || constructions[constructionsCount-2] == TokenNamewhile))) {*/ + if (options.newLineInEmptyBlockMode) { + if (inArrayAssignment) { + newLine(1); // array assigment with an empty block + } else { + newLine(pendingNewLines); + } + } + // } + } + } else { + // see PR 1FKKC3U: LFCOM:WINNT - Format problem with a comment before the ';' + if (!((previousToken == Scanner.TokenNameCOMMENT_BLOCK + || previousToken == Scanner.TokenNameCOMMENT_PHPDOC) + && token == TokenNameSEMICOLON)) { + newLine(pendingNewLines); + } + } + if (((previousCompilableToken == TokenNameSEMICOLON) + || (previousCompilableToken == TokenNameLBRACE) + || (previousCompilableToken == TokenNameRBRACE) + || (isComment(previousToken))) + && (token == TokenNameRBRACE)) { + indentationOffset = -1; + indentationLevel += popExclusiveUntilBlock(); + } + if (previousToken == Scanner.TokenNameCOMMENT_LINE && inAssignment) { + // PR 1FI5IPO + currentLineIndentationLevel++; + } else { + currentLineIndentationLevel = indentationLevel + indentationOffset; + } + pendingSpace = false; + indentationOffset = 0; + } + pendingNewLines = 0; + newLinesInWhitespace = 0; + specialElse = false; + + if (nlicsToken == TokenNamedo && token == TokenNamewhile) { + nlicsToken = 0; + } + } + switch (token) { + case TokenNameelse : + // case TokenNamefinally : + expectingOpenBrace = true; + pendingNewlineAfterParen = true; + indentationLevel += pushControlStatement(token); + break; + case TokenNamecase : + case TokenNamedefault : + if (tokenBeforeColonCount == tokenBeforeColon.length) { + System.arraycopy( + tokenBeforeColon, + 0, + (tokenBeforeColon = new int[tokenBeforeColonCount * 2]), + 0, + tokenBeforeColonCount); + } + tokenBeforeColon[tokenBeforeColonCount++] = TokenNamecase; + indentationLevel += pushControlStatement(TokenNamecase); + break; + case TokenNameQUESTION : + if (tokenBeforeColonCount == tokenBeforeColon.length) { + System.arraycopy( + tokenBeforeColon, + 0, + (tokenBeforeColon = new int[tokenBeforeColonCount * 2]), + 0, + tokenBeforeColonCount); + } + tokenBeforeColon[tokenBeforeColonCount++] = token; + break; + case TokenNameswitch : + case TokenNamefor : + case TokenNameif : + case TokenNamewhile : + if (openParenthesisCount == openParenthesis.length) { + System.arraycopy( + openParenthesis, + 0, + (openParenthesis = new int[openParenthesisCount * 2]), + 0, + openParenthesisCount); + } + openParenthesis[openParenthesisCount++] = 0; + expectingOpenBrace = true; + + indentationLevel += pushControlStatement(token); + break; +// case TokenNametry : +// pendingNewlineAfterParen = true; +// case TokenNamecatch : +// // several CATCH statements can be contiguous. +// // a CATCH is encountered pop until first CATCH (if a CATCH follows a TRY it works the same way, +// // as CATCH and TRY are the same token in the stack). +// expectingOpenBrace = true; +// indentationLevel += pushControlStatement(TokenNamecatch); +// break; + + case TokenNamedo : + expectingOpenBrace = true; + indentationLevel += pushControlStatement(token); + nlicsToken = token; + break; + case TokenNamenew : + break; + case TokenNameLPAREN : +// if (previousToken == TokenNamesynchronized) { +// indentationLevel += pushControlStatement(previousToken); +// } else { + // Put a space between the previous and current token if the + // previous token was not a keyword, open paren, logical + // compliment (eg: !), semi-colon, open brace, close brace, + // super, or this. + if (previousCompilableToken != TokenNameLBRACKET + && previousToken != TokenNameIdentifier + && previousToken != 0 + && previousToken != TokenNameNOT + && previousToken != TokenNameLPAREN + && previousToken != TokenNameTWIDDLE + && previousToken != TokenNameSEMICOLON + && previousToken != TokenNameLBRACE + && previousToken != TokenNameRBRACE) { +// && previousToken != TokenNamesuper +// && previousToken != TokenNamethis) { + space(); + } + // If in a for/if/while statement, increase the parenthesis count + // for the current openParenthesisCount + // else increase the count for stand alone parenthesis. + if (openParenthesisCount > 0) + openParenthesis[openParenthesisCount - 1]++; + else + openParenthesis[0]++; + + pendingSpace = false; + //S } + break; + case TokenNameRPAREN : + + // Decrease the parenthesis count + // if there is no more unclosed parenthesis, + // a new line and indent may be append (depending on the next token). + if ((openParenthesisCount > 1) + && (openParenthesis[openParenthesisCount - 1] > 0)) { + openParenthesis[openParenthesisCount - 1]--; + if (openParenthesis[openParenthesisCount - 1] <= 0) { + pendingNewlineAfterParen = true; + inAssignment = false; + openParenthesisCount--; + } + } else { + openParenthesis[0]--; + } + pendingSpace = false; + break; + case TokenNameLBRACE : + if ((previousCompilableToken == TokenNameRBRACKET) + || (previousCompilableToken == TokenNameEQUAL)) { + // if (previousCompilableToken == TokenNameRBRACKET) { + inArrayAssignment = true; + inAssignment = false; + } + if (inArrayAssignment) { + indentationLevel += pushBlock(); + } else { + // Add new line and increase indentation level after open brace. + pendingNewLines = 1; + indentationLevel += pushBlock(); + } + break; + case TokenNameRBRACE : + if (previousCompilableToken == TokenNameRPAREN) { + pendingSpace = false; + } + if (inArrayAssignment) { + inArrayAssignment = false; + pendingNewLines = 1; + indentationLevel += popInclusiveUntilBlock(); + } else { + pendingNewLines = 1; + indentationLevel += popInclusiveUntilBlock(); + + if (previousCompilableToken == TokenNameRPAREN) { + // fix for 1FGDDV6: LFCOM:WIN98 - Weird splitting on message expression + currentLineBuffer.append(options.lineSeparatorSequence); + increaseLineDelta(options.lineSeparatorSequence.length); + } + if (constructionsCount > 0) { + switch (constructions[constructionsCount - 1]) { + case TokenNamefor : + //indentationLevel += popExclusiveUntilBlock(); + //break; + case TokenNameswitch : + case TokenNameif : + case TokenNameelse : +// case TokenNametry : +// case TokenNamecatch : +// case TokenNamefinally : + case TokenNamewhile : + case TokenNamedo : +// case TokenNamesynchronized : + clearNonBlockIndents = true; + default : + break; + } + } + } + break; + case TokenNameLBRACKET : + openBracketCount++; + pendingSpace = false; + break; + case TokenNameRBRACKET : + openBracketCount -= (openBracketCount > 0) ? 1 : 0; + // if there is no left bracket to close, the right bracket is ignored. + pendingSpace = false; + break; + case TokenNameCOMMA : + case TokenNameDOT : + pendingSpace = false; + break; + case TokenNameSEMICOLON : + + // Do not generate line terminators in the definition of + // the for statement. + // if not in this case, jump a line and reduce indentation after the brace + // if the block it closes belongs to a conditional statement (if, while, do...). + if (openParenthesisCount <= 1) { + pendingNewLines = 1; + if (expectingOpenBrace) { + clearNonBlockIndents = true; + expectingOpenBrace = false; + } + } + inAssignment = false; + pendingSpace = false; + break; + case TokenNamePLUS_PLUS : + case TokenNameMINUS_MINUS : + + // Do not put a space between a post-increment/decrement + // and the identifier being modified. + if (previousToken == TokenNameIdentifier + || previousToken == TokenNameRBRACKET) { + pendingSpace = false; + } + break; + case TokenNamePLUS : // previously ADDITION + case TokenNameMINUS : + + // Handle the unary operators plus and minus via a flag + if (!isLiteralToken(previousToken) + && previousToken != TokenNameIdentifier + && previousToken != TokenNameRPAREN + && previousToken != TokenNameRBRACKET) { + unarySignModifier = 1; + } + break; + case TokenNameCOLON : + // In a switch/case statement, add a newline & indent + // when a colon is encountered. + if (tokenBeforeColonCount > 0) { + if (tokenBeforeColon[tokenBeforeColonCount - 1] == TokenNamecase) { + pendingNewLines = 1; + } + tokenBeforeColonCount--; + } + break; + case TokenNameEQUAL : + inAssignment = true; + break; + case Scanner.TokenNameCOMMENT_LINE : + pendingNewLines = 1; + if (inAssignment) { + currentLineIndentationLevel++; + } + break; // a line is always inserted after a one-line comment + case Scanner.TokenNameCOMMENT_PHPDOC : + case Scanner.TokenNameCOMMENT_BLOCK : + currentCommentOffset = getCurrentCommentOffset(); + pendingNewLines = 1; + break; + case Scanner.TokenNameWHITESPACE : + + // Count the number of line terminators in the whitespace so + // line spacing can be preserved near comments. + char[] source = scanner.source; + newLinesInWhitespace = 0; + for (int i = scanner.startPosition, max = scanner.currentPosition; + i < max; + i++) { + if (source[i] == '\r') { + if (i < max - 1) { + if (source[++i] == '\n') { + newLinesInWhitespace++; + } else { + newLinesInWhitespace++; + } + } else { + newLinesInWhitespace++; + } + } else if (source[i] == '\n') { + newLinesInWhitespace++; + } + } + increaseLineDelta(scanner.startPosition - scanner.currentPosition); + break; + default : + if ((token == TokenNameIdentifier) + || isLiteralToken(token)) { +// || token == TokenNamesuper +// || token == TokenNamethis) { + + // Do not put a space between a unary operator + // (eg: ++, --, +, -) and the identifier being modified. + if (previousToken == TokenNamePLUS_PLUS + || previousToken == TokenNameMINUS_MINUS + || (previousToken == TokenNamePLUS && unarySignModifier > 0) + || (previousToken == TokenNameMINUS && unarySignModifier > 0)) { + pendingSpace = false; + } + unarySignModifier = 0; + } + break; + } + // Do not output whitespace tokens. + if (token != Scanner.TokenNameWHITESPACE) { + + /* Add pending space to the formatted source string. + Do not output a space under the following circumstances: + 1) this is the first pass + 2) previous token is an open paren + 3) previous token is a period + 4) previous token is the logical compliment (eg: !) + 5) previous token is the bitwise compliment (eg: ~) + 6) previous token is the open bracket (eg: [) + 7) in an assignment statement, if the previous token is an + open brace or the current token is a close brace + 8) previous token is a single line comment + */ + boolean openAndCloseBrace = + previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE; + + if (pendingSpace + && insertSpaceAfter(previousToken) + && !(inAssignment + && (previousToken == TokenNameLBRACE || token == TokenNameRBRACE)) + && previousToken != Scanner.TokenNameCOMMENT_LINE) { + if ((!(options.compactAssignmentMode && token == TokenNameEQUAL)) + && !openAndCloseBrace) + space(); + } + // Add the next token to the formatted source string. + outputCurrentToken(token); + if (token == Scanner.TokenNameCOMMENT_LINE && openParenthesisCount > 1) { + pendingNewLines = 0; + currentLineBuffer.append(options.lineSeparatorSequence); + increaseLineDelta(options.lineSeparatorSequence.length); + } + pendingSpace = true; + } + // Whitespace tokens do not need to be remembered. + if (token != Scanner.TokenNameWHITESPACE) { + previousToken = token; + if (token != Scanner.TokenNameCOMMENT_BLOCK + && token != Scanner.TokenNameCOMMENT_LINE + && token != Scanner.TokenNameCOMMENT_PHPDOC) { + previousCompilableToken = token; + } + } + } + output(copyRemainingSource()); + flushBuffer(); // dump the last token of the source in the formatted output. + } catch (InvalidInputException e) { + output(copyRemainingSource()); + flushBuffer(); // dump the last token of the source in the formatted output. + } + } + + /** + * Formats the char array sourceString, + * and returns a string containing the formatted version. + * @return the formatted ouput. + */ + public String formatSourceString(String sourceString) { + char[] sourceChars = sourceString.toCharArray(); + formattedSource = new StringBuffer(sourceChars.length); + scanner.setSource(sourceChars); + format(); + return formattedSource.toString(); + } + + /** + * Formats the char array sourceString, + * and returns a string containing the formatted version. + * @param string the string to format + * @param indentationLevel the initial indentation level + * @return the formatted ouput. + */ + public String format(String string, int indentationLevel) { + return format(string, indentationLevel, (int[])null); + } + + /** + * Formats the char array sourceString, + * and returns a string containing the formatted version. + * The positions array is modified to contain the mapped positions. + * @param string the string to format + * @param indentationLevel the initial indentation level + * @param positions the array of positions to map + * @return the formatted ouput. + */ + public String format(String string, int indentationLevel, int[] positions) { + return this.format(string, indentationLevel, positions, null); + } + + public String format(String string, int indentationLevel, int[] positions, String lineSeparator) { + if (lineSeparator != null){ + this.options.setLineSeparator(lineSeparator); + } + if (positions != null) { + this.setPositionsToMap(positions); + this.setInitialIndentationLevel(indentationLevel); + String formattedString = this.formatSourceString(string); + int[] mappedPositions = this.getMappedPositions(); + System.arraycopy(mappedPositions, 0, positions, 0, positions.length); + return formattedString; + } else { + this.setInitialIndentationLevel(indentationLevel); + return this.formatSourceString(string); + } + } + /** + * Formats the char array sourceString, + * and returns a string containing the formatted version. The initial indentation level is 0. + * @param string the string to format + * @return the formatted ouput. + */ + public String format(String string) { + return this.format(string, 0, (int[])null); + } + + /** + * Formats a given source string, starting indenting it at a particular + * depth and using the given options + * + * @deprecated backport 1.0 internal functionality + */ + public static String format(String sourceString, int initialIndentationLevel, ConfigurableOption[] options) { + CodeFormatter formatter = new CodeFormatter(options); + formatter.setInitialIndentationLevel(initialIndentationLevel); + return formatter.formatSourceString(sourceString); + } + + /** + * Returns the number of characters and tab char between the beginning of the line + * and the beginning of the comment. + */ + private int getCurrentCommentOffset() { + int linePtr = scanner.linePtr; + // if there is no beginning of line, return 0. + if (linePtr < 0) + return 0; + int offset = 0; + int beginningOfLine = scanner.lineEnds[linePtr]; + int currentStartPosition = scanner.startPosition; + char[] source = scanner.source; + + // find the position of the beginning of the line containing the comment + while (beginningOfLine > currentStartPosition) { + if (linePtr > 0) { + beginningOfLine = scanner.lineEnds[--linePtr]; + } else { + beginningOfLine = 0; + break; + } + } + for (int i = currentStartPosition - 1; i >= beginningOfLine ; i--) { + char currentCharacter = source[i]; + switch (currentCharacter) { + case '\t' : + offset += options.tabSize; + break; + case ' ' : + offset++; + break; + case '\r' : + case '\n' : + break; + default: + return offset; + } + } + return offset; + } + + /** + * Returns an array of descriptions for the configurable options. + * The descriptions may be changed and passed back to a different + * compiler. + * + * @deprecated backport 1.0 internal functionality + */ + public static ConfigurableOption[] getDefaultOptions(Locale locale) { + String componentName = CodeFormatter.class.getName(); + FormatterOptions options = new FormatterOptions(); + return new ConfigurableOption[] { + new ConfigurableOption(componentName, "newline.openingBrace", locale, options.newLineBeforeOpeningBraceMode ? 0 : 1), //$NON-NLS-1$ + new ConfigurableOption(componentName, "newline.controlStatement", locale, options.newlineInControlStatementMode ? 0 : 1), //$NON-NLS-1$ + new ConfigurableOption(componentName, "newline.clearAll", locale, options.clearAllBlankLinesMode ? 0 : 1), //$NON-NLS-1$ + new ConfigurableOption(componentName, "newline.elseIf", locale, options.compactElseIfMode ? 0 : 1), //$NON-NLS-1$ + new ConfigurableOption(componentName, "newline.emptyBlock", locale, options.newLineInEmptyBlockMode ? 0 : 1), //$NON-NLS-1$ + new ConfigurableOption(componentName, "line.split", locale, options.maxLineLength),//$NON-NLS-1$ + new ConfigurableOption(componentName, "style.compactAssignment", locale, options.compactAssignmentMode ? 0 : 1), //$NON-NLS-1$ + new ConfigurableOption(componentName, "tabulation.char", locale, options.indentWithTab ? 0 : 1), //$NON-NLS-1$ + new ConfigurableOption(componentName, "tabulation.size", locale, options.tabSize) //$NON-NLS-1$ + }; + } + + /** + * Returns the array of mapped positions. + * Returns null is no positions have been set. + * @return int[] + * @deprecated There is no need to retrieve the mapped positions anymore. + */ + public int[] getMappedPositions() { + return mappedPositions; + } + + /** + * Returns the priority of the token given as argument
+ * The most prioritary the token is, the smallest the return value is. + * @return the priority of token + * @param token the token of which the priority is requested + */ + private static int getTokenPriority(int token) { + switch (token) { + case TokenNameextends : +// case TokenNameimplements : +// case TokenNamethrows : + return 10; + case TokenNameSEMICOLON : // ; + return 20; + case TokenNameCOMMA : // , + return 25; + case TokenNameEQUAL : // = + return 30; + case TokenNameAND_AND : // && + case TokenNameOR_OR : // || + return 40; + case TokenNameQUESTION : // ? + case TokenNameCOLON : // : + return 50; // it's better cutting on ?: than on ; + case TokenNameEQUAL_EQUAL : // == + case TokenNameNOT_EQUAL : // != + return 60; + case TokenNameLESS : // < + case TokenNameLESS_EQUAL : // <= + case TokenNameGREATER : // > + case TokenNameGREATER_EQUAL : // >= +// case TokenNameinstanceof : // instanceof + return 70; + case TokenNamePLUS : // + + case TokenNameMINUS : // - + return 80; + case TokenNameMULTIPLY : // * + case TokenNameDIVIDE : // / + case TokenNameREMAINDER : // % + return 90; + case TokenNameLEFT_SHIFT : // << + case TokenNameRIGHT_SHIFT : // >> +// case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> + return 100; + case TokenNameAND : // & + case TokenNameOR : // | + case TokenNameXOR : // ^ + return 110; + case TokenNameMULTIPLY_EQUAL : // *= + case TokenNameDIVIDE_EQUAL : // /= + case TokenNameREMAINDER_EQUAL : // %= + case TokenNamePLUS_EQUAL : // += + case TokenNameMINUS_EQUAL : // -= + case TokenNameLEFT_SHIFT_EQUAL : // <<= + case TokenNameRIGHT_SHIFT_EQUAL : // >>= +// case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= + case TokenNameAND_EQUAL : // &= + case TokenNameXOR_EQUAL : // ^= + case TokenNameOR_EQUAL : // |= + return 120; + case TokenNameDOT : // . + return 130; + default : + return Integer.MAX_VALUE; + } + } + + /** + * Handles the exception raised when an invalid token is encountered. + * Returns true if the exception has been handled, false otherwise. + */ + private boolean handleInvalidToken(Exception e) { + if (e.getMessage().equals(Scanner.INVALID_CHARACTER_CONSTANT) + || e.getMessage().equals(Scanner.INVALID_CHAR_IN_STRING) + || e.getMessage().equals(Scanner.INVALID_ESCAPE)) { + return true; + } + return false; + } + + private final void increaseGlobalDelta(int offset) { + globalDelta += offset; + } + + private final void increaseLineDelta(int offset) { + lineDelta += offset; + } + + private final void increaseSplitDelta(int offset) { + splitDelta += offset; + } + + /** + * Returns true if a space has to be inserted after operator + * false otherwise. + */ + private boolean insertSpaceAfter(int token) { + switch (token) { + case TokenNameLPAREN : + case TokenNameNOT : + case TokenNameTWIDDLE : + case TokenNameDOT : + case 0 : // no token + case TokenNameLBRACKET : + case Scanner.TokenNameCOMMENT_LINE : + return false; + default : + return true; + } + } + + /** + * Returns true if a space has to be inserted before operator + * false otherwise.
+ * Cannot be static as it uses the code formatter options + * (to know if the compact assignment mode is on). + */ + private boolean insertSpaceBefore(int token) { + switch (token) { + case TokenNameEQUAL : + return (!options.compactAssignmentMode); + default : + return false; + } + } + + private static boolean isComment(int token) { + boolean result = + token == Scanner.TokenNameCOMMENT_BLOCK + || token == Scanner.TokenNameCOMMENT_LINE + || token == Scanner.TokenNameCOMMENT_PHPDOC; + return result; + } + + private static boolean isLiteralToken(int token) { + boolean result = + token == TokenNameIntegerLiteral + // || token == TokenNameLongLiteral + // || token == TokenNameFloatingPointLiteral + || token == TokenNameDoubleLiteral + // || token == TokenNameCharacterLiteral + || token == TokenNameStringLiteral; + return result; + } + + /** + * If the length of oneLineBuffer exceeds maxLineLength, + * it is split and the result is dumped in formattedSource + * @param newLineCount the number of new lines to append + */ + private void newLine(int newLineCount) { + + // format current line + splitDelta = 0; + beginningOfLineIndex = formattedSource.length(); + String currentLine = currentLineBuffer.toString(); + if (containsOpenCloseBraces) { + containsOpenCloseBraces = false; + outputLine( + currentLine, + false, + indentationLevelForOpenCloseBraces, + 0, + -1, + null, + 0); + indentationLevelForOpenCloseBraces = currentLineIndentationLevel; + } else { + outputLine(currentLine, false, currentLineIndentationLevel, 0, -1, null, 0); + } + // dump line break(s) + for (int i = 0; i < newLineCount; i++) { + formattedSource.append(options.lineSeparatorSequence); + increaseSplitDelta(options.lineSeparatorSequence.length); + } + // reset formatter for next line + int currentLength = currentLine.length(); + currentLineBuffer = + new StringBuffer( + currentLength > maxLineSize ? maxLineSize = currentLength : maxLineSize); + + increaseGlobalDelta(splitDelta); + increaseGlobalDelta(lineDelta); + lineDelta = 0; + currentLineIndentationLevel = initialIndentationLevel; + } + + private String operatorString(int operator) { + switch (operator) { + case TokenNameextends : + return "extends"; //$NON-NLS-1$ + +// case TokenNameimplements : +// return "implements"; //$NON-NLS-1$ +// +// case TokenNamethrows : +// return "throws"; //$NON-NLS-1$ + + case TokenNameSEMICOLON : // ; + return ";"; //$NON-NLS-1$ + + case TokenNameCOMMA : // , + return ","; //$NON-NLS-1$ + + case TokenNameEQUAL : // = + return "="; //$NON-NLS-1$ + + case TokenNameAND_AND : // && (15.22) + return "&&"; //$NON-NLS-1$ + + case TokenNameOR_OR : // || (15.23) + return "||"; //$NON-NLS-1$ + + case TokenNameQUESTION : // ? (15.24) + return "?"; //$NON-NLS-1$ + + case TokenNameCOLON : // : (15.24) + return ":"; //$NON-NLS-1$ + + case TokenNameEQUAL_EQUAL : // == (15.20, 15.20.1, 15.20.2, 15.20.3) + return "=="; //$NON-NLS-1$ + + case TokenNameNOT_EQUAL : // != (15.20, 15.20.1, 15.20.2, 15.20.3) + return "!="; //$NON-NLS-1$ + + case TokenNameLESS : // < (15.19.1) + return "<"; //$NON-NLS-1$ + + case TokenNameLESS_EQUAL : // <= (15.19.1) + return "<="; //$NON-NLS-1$ + + case TokenNameGREATER : // > (15.19.1) + return ">"; //$NON-NLS-1$ + + case TokenNameGREATER_EQUAL : // >= (15.19.1) + return ">="; //$NON-NLS-1$ + +// case TokenNameinstanceof : // instanceof +// return "instanceof"; //$NON-NLS-1$ + + case TokenNamePLUS : // + (15.17, 15.17.2) + return "+"; //$NON-NLS-1$ + + case TokenNameMINUS : // - (15.17.2) + return "-"; //$NON-NLS-1$ + + case TokenNameMULTIPLY : // * (15.16.1) + return "*"; //$NON-NLS-1$ + + case TokenNameDIVIDE : // / (15.16.2) + return "/"; //$NON-NLS-1$ + + case TokenNameREMAINDER : // % (15.16.3) + return "%"; //$NON-NLS-1$ + + case TokenNameLEFT_SHIFT : // << (15.18) + return "<<"; //$NON-NLS-1$ + + case TokenNameRIGHT_SHIFT : // >> (15.18) + return ">>"; //$NON-NLS-1$ + +// case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18) +// return ">>>"; //$NON-NLS-1$ + + case TokenNameAND : // & (15.21, 15.21.1, 15.21.2) + return "&"; //$NON-NLS-1$ + + case TokenNameOR : // | (15.21, 15.21.1, 15.21.2) + return "|"; //$NON-NLS-1$ + + case TokenNameXOR : // ^ (15.21, 15.21.1, 15.21.2) + return "^"; //$NON-NLS-1$ + + case TokenNameMULTIPLY_EQUAL : // *= (15.25.2) + return "*="; //$NON-NLS-1$ + + case TokenNameDIVIDE_EQUAL : // /= (15.25.2) + return "/="; //$NON-NLS-1$ + + case TokenNameREMAINDER_EQUAL : // %= (15.25.2) + return "%="; //$NON-NLS-1$ + + case TokenNamePLUS_EQUAL : // += (15.25.2) + return "+="; //$NON-NLS-1$ + + case TokenNameMINUS_EQUAL : // -= (15.25.2) + return "-="; //$NON-NLS-1$ + + case TokenNameLEFT_SHIFT_EQUAL : // <<= (15.25.2) + return "<<="; //$NON-NLS-1$ + + case TokenNameRIGHT_SHIFT_EQUAL : // >>= (15.25.2) + return ">>="; //$NON-NLS-1$ + +// case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2) +// return ">>>="; //$NON-NLS-1$ + + case TokenNameAND_EQUAL : // &= (15.25.2) + return "&="; //$NON-NLS-1$ + + case TokenNameXOR_EQUAL : // ^= (15.25.2) + return "^="; //$NON-NLS-1$ + + case TokenNameOR_EQUAL : // |= (15.25.2) + return "|="; //$NON-NLS-1$ + + case TokenNameDOT : // . + return "."; //$NON-NLS-1$ + + default : + return ""; //$NON-NLS-1$ + } + } + + /** + * Appends stringToOutput to the formatted output.
+ * If it contains \n, append a LINE_SEPARATOR and indent after it. + */ + private void output(String stringToOutput) { + char currentCharacter; + for (int i = 0, max = stringToOutput.length(); i < max; i++) { + currentCharacter = stringToOutput.charAt(i); + if (currentCharacter != '\t') { + currentLineBuffer.append(currentCharacter); + } + } + } + + /** + * Appends token to the formatted output.
+ * If it contains \n, append a LINE_SEPARATOR and indent after it. + */ + private void outputCurrentToken(int token) { + char[] source = scanner.source; + int startPosition = scanner.startPosition; + + switch (token) { + case Scanner.TokenNameCOMMENT_PHPDOC : + case Scanner.TokenNameCOMMENT_BLOCK : + case Scanner.TokenNameCOMMENT_LINE : + boolean endOfLine = false; + int currentCommentOffset = getCurrentCommentOffset(); + int beginningOfLineSpaces = 0; + endOfLine = false; + currentCommentOffset = getCurrentCommentOffset(); + beginningOfLineSpaces = 0; + boolean pendingCarriageReturn = false; + for (int i = startPosition, max = scanner.currentPosition; i < max; i++) { + char currentCharacter = source[i]; + updateMappedPositions(i); + switch (currentCharacter) { + case '\r' : + pendingCarriageReturn = true; + endOfLine = true; + break; + case '\n' : + if (pendingCarriageReturn) { + increaseGlobalDelta(options.lineSeparatorSequence.length - 2); + } else { + increaseGlobalDelta(options.lineSeparatorSequence.length - 1); + } + pendingCarriageReturn = false; + currentLineBuffer.append(options.lineSeparatorSequence); + beginningOfLineSpaces = 0; + endOfLine = true; + break; + case '\t' : + if (pendingCarriageReturn) { + pendingCarriageReturn = false; + increaseGlobalDelta(options.lineSeparatorSequence.length - 1); + currentLineBuffer.append(options.lineSeparatorSequence); + beginningOfLineSpaces = 0; + endOfLine = true; + } + if (endOfLine) { + // we remove a maximum of currentCommentOffset characters (tabs are converted to space numbers). + beginningOfLineSpaces += options.tabSize; + if (beginningOfLineSpaces > currentCommentOffset) { + currentLineBuffer.append(currentCharacter); + } else { + increaseGlobalDelta(-1); + } + } else { + currentLineBuffer.append(currentCharacter); + } + break; + case ' ' : + if (pendingCarriageReturn) { + pendingCarriageReturn = false; + increaseGlobalDelta(options.lineSeparatorSequence.length - 1); + currentLineBuffer.append(options.lineSeparatorSequence); + beginningOfLineSpaces = 0; + endOfLine = true; + } + if (endOfLine) { + // we remove a maximum of currentCommentOffset characters (tabs are converted to space numbers). + beginningOfLineSpaces++; + if (beginningOfLineSpaces > currentCommentOffset) { + currentLineBuffer.append(currentCharacter); + } else { + increaseGlobalDelta(-1); + } + } else { + currentLineBuffer.append(currentCharacter); + } + break; + default : + if (pendingCarriageReturn) { + pendingCarriageReturn = false; + increaseGlobalDelta(options.lineSeparatorSequence.length - 1); + currentLineBuffer.append(options.lineSeparatorSequence); + beginningOfLineSpaces = 0; + endOfLine = true; + } else { + beginningOfLineSpaces = 0; + currentLineBuffer.append(currentCharacter); + endOfLine = false; + } + } + } + updateMappedPositions(scanner.currentPosition - 1); + multipleLineCommentCounter++; + break; + default : + for (int i = startPosition, max = scanner.currentPosition; i < max; i++) { + char currentCharacter = source[i]; + updateMappedPositions(i); + currentLineBuffer.append(currentCharacter); + } + } + } + + /** + * Outputs currentString:
+ *
  • If its length is < maxLineLength, output + *
  • Otherwise it is split.
+ * @param currentString string to output + * @param preIndented whether the string to output was pre-indented + * @param depth number of indentation to put in front of currentString + * @param operator value of the operator belonging to currentString. + */ + private void outputLine( + String currentString, + boolean preIndented, + int depth, + int operator, + int substringIndex, + int[] startSubstringIndexes, + int offsetInGlobalLine) { + + boolean emptyFirstSubString = false; + String operatorString = operatorString(operator); + boolean placeOperatorBehind = !breakLineBeforeOperator(operator); + boolean placeOperatorAhead = !placeOperatorBehind; + + // dump prefix operator? + if (placeOperatorAhead) { + if (!preIndented) { + dumpTab(depth); + preIndented = true; + } + if (operator != 0) { + if (insertSpaceBefore(operator)) { + formattedSource.append(' '); + increaseSplitDelta(1); + } + formattedSource.append(operatorString); + increaseSplitDelta(operatorString.length()); + + if (insertSpaceAfter(operator) + // && operator != TokenNameimplements + && operator != TokenNameextends) { + // && operator != TokenNamethrows) { + formattedSource.append(' '); + increaseSplitDelta(1); + } + } + } + SplitLine splitLine = null; + if (options.maxLineLength == 0 + || getLength(currentString, depth) < options.maxLineLength + || (splitLine = split(currentString, offsetInGlobalLine)) == null) { + + // depending on the type of operator, outputs new line before of after dumping it + // indent before postfix operator + // indent also when the line cannot be split + if (operator == TokenNameextends) { +// || operator == TokenNameimplements +// || operator == TokenNamethrows) { + formattedSource.append(' '); + increaseSplitDelta(1); + } + if (placeOperatorBehind) { + if (!preIndented) { + dumpTab(depth); + } + } + int max = currentString.length(); + if (multipleLineCommentCounter != 0) { + try { + BufferedReader reader = new BufferedReader(new StringReader(currentString)); + String line = reader.readLine(); + while (line != null) { + updateMappedPositionsWhileSplitting( + beginningOfLineIndex, + beginningOfLineIndex + line.length() + options.lineSeparatorSequence.length); + formattedSource.append(line); + beginningOfLineIndex = beginningOfLineIndex + line.length(); + if ((line = reader.readLine()) != null) { + formattedSource.append(options.lineSeparatorSequence); + beginningOfLineIndex += options.lineSeparatorSequence.length; + dumpTab(currentLineIndentationLevel); + } + } + reader.close(); + } catch(IOException e) { + e.printStackTrace(); + } + } else { + updateMappedPositionsWhileSplitting( + beginningOfLineIndex, + beginningOfLineIndex + max); + for (int i = 0; i < max; i++) { + char currentChar = currentString.charAt(i); + switch (currentChar) { + case '\r' : + break; + case '\n' : + if (i != max - 1) { + // fix for 1FFYL5C: LFCOM:ALL - Incorrect indentation when split with a comment inside a condition + // a substring cannot end with a lineSeparatorSequence, + // except if it has been added by format() after a one-line comment + formattedSource.append(options.lineSeparatorSequence); + + // 1FGDDV6: LFCOM:WIN98 - Weird splitting on message expression + dumpTab(depth - 1); + } + break; + default : + formattedSource.append(currentChar); + } + } + } + // update positions inside the mappedPositions table + if (substringIndex != -1) { + if (multipleLineCommentCounter == 0) { + int startPosition = + beginningOfLineIndex + startSubstringIndexes[substringIndex]; + updateMappedPositionsWhileSplitting(startPosition, startPosition + max); + } + + // compute the splitDelta resulting with the operator and blank removal + if (substringIndex + 1 != startSubstringIndexes.length) { + increaseSplitDelta( + startSubstringIndexes[substringIndex] + + max + - startSubstringIndexes[substringIndex + 1]); + } + } + // dump postfix operator? + if (placeOperatorBehind) { + if (insertSpaceBefore(operator)) { + formattedSource.append(' '); + if (operator != 0) { + increaseSplitDelta(1); + } + } + formattedSource.append(operatorString); + if (operator != 0) { + increaseSplitDelta(operatorString.length()); + } + } + return; + } + // fix for 1FG0BA3: LFCOM:WIN98 - Weird splitting on interfaces + // extends has to stand alone on a line when currentString has been split. + if (options.maxLineLength != 0 + && splitLine != null + && (operator == TokenNameextends)) { +// || operator == TokenNameimplements +// || operator == TokenNamethrows)) { + formattedSource.append(options.lineSeparatorSequence); + increaseSplitDelta(options.lineSeparatorSequence.length); + dumpTab(depth + 1); + } else { + if (operator == TokenNameextends) { +// || operator == TokenNameimplements +// || operator == TokenNamethrows) { + formattedSource.append(' '); + increaseSplitDelta(1); + } + } + // perform actual splitting + String result[] = splitLine.substrings; + int[] splitOperators = splitLine.operators; + + if (result[0].length() == 0) { + // when the substring 0 is null, the substring 1 is correctly indented. + depth--; + emptyFirstSubString = true; + } + // the operator going in front of the result[0] string is the operator parameter + for (int i = 0, max = result.length; i < max; i++) { + // the new depth is the current one if this is the first substring, + // the current one + 1 otherwise. + // if the substring is a comment, use the current indentation Level instead of the depth + // (-1 because the ouputline increases depth). + // (fix for 1FFC72R: LFCOM:ALL - Incorrect line split in presence of line comments) + String currentResult = result[i]; + + if (currentResult.length() != 0 || splitOperators[i] != 0) { + int newDepth = + (currentResult.startsWith("/*") //$NON-NLS-1$ + || currentResult.startsWith("//")) //$NON-NLS-1$ + ? indentationLevel - 1 : depth; + outputLine( + currentResult, + i == 0 || (i == 1 && emptyFirstSubString) ? preIndented : false, + i == 0 ? newDepth : newDepth + 1, + splitOperators[i], + i, + splitLine.startSubstringsIndexes, + currentString.indexOf(currentResult)); + if (i != max - 1) { + formattedSource.append(options.lineSeparatorSequence); + increaseSplitDelta(options.lineSeparatorSequence.length); + } + } + } + if (result.length == splitOperators.length - 1) { + int lastOperator = splitOperators[result.length]; + String lastOperatorString = operatorString(lastOperator); + formattedSource.append(options.lineSeparatorSequence); + increaseSplitDelta(options.lineSeparatorSequence.length); + + if (breakLineBeforeOperator(lastOperator)) { + dumpTab(depth + 1); + if (lastOperator != 0) { + if (insertSpaceBefore(lastOperator)) { + formattedSource.append(' '); + increaseSplitDelta(1); + } + formattedSource.append(lastOperatorString); + increaseSplitDelta(lastOperatorString.length()); + + if (insertSpaceAfter(lastOperator) + // && lastOperator != TokenNameimplements + && lastOperator != TokenNameextends ) { + // && lastOperator != TokenNamethrows) { + formattedSource.append(' '); + increaseSplitDelta(1); + } + } + } + } + if (placeOperatorBehind) { + if (insertSpaceBefore(operator)) { + formattedSource.append(' '); + increaseSplitDelta(1); + } + formattedSource.append(operatorString); + //increaseSplitDelta(operatorString.length()); + } + } + + /** + * Pops the top statement of the stack if it is token + */ + private int pop(int token) { + int delta = 0; + if ((constructionsCount > 0) + && (constructions[constructionsCount - 1] == token)) { + delta--; + constructionsCount--; + } + return delta; + } + + /** + * Pops the top statement of the stack if it is a BLOCK or a NONINDENT_BLOCK. + */ + private int popBlock() { + int delta = 0; + if ((constructionsCount > 0) + && ((constructions[constructionsCount - 1] == BLOCK) + || (constructions[constructionsCount - 1] == NONINDENT_BLOCK))) { + if (constructions[constructionsCount - 1] == BLOCK) + delta--; + constructionsCount--; + } + return delta; + } + + /** + * Pops elements until the stack is empty or the top element is token.
+ * Does not remove token from the stack. + * @param token the token to be left as the top of the stack + */ + private int popExclusiveUntil(int token) { + int delta = 0; + int startCount = constructionsCount; + for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) { + if (constructions[i] != NONINDENT_BLOCK) + delta--; + constructionsCount--; + } + return delta; + } + + /** + * Pops elements until the stack is empty or the top element is + * a BLOCK or a NONINDENT_BLOCK.
+ * Does not remove it from the stack. + */ + private int popExclusiveUntilBlock() { + int startCount = constructionsCount; + int delta = 0; + for (int i = startCount - 1; + i >= 0 && constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK; + i--) { + constructionsCount--; + delta--; + } + return delta; + } + + /** + * Pops elements until the stack is empty or the top element is + * a BLOCK, a NONINDENT_BLOCK or a CASE.
+ * Does not remove it from the stack. + */ + private int popExclusiveUntilBlockOrCase() { + int startCount = constructionsCount; + int delta = 0; + for (int i = startCount - 1; + i >= 0 + && constructions[i] != BLOCK + && constructions[i] != NONINDENT_BLOCK + && constructions[i] != TokenNamecase; + i--) { + constructionsCount--; + delta--; + } + return delta; + } + + /** + * Pops elements until the stack is empty or the top element is token.
+ * Removes token from the stack too. + * @param token the token to remove from the stack + */ + private int popInclusiveUntil(int token) { + int startCount = constructionsCount; + int delta = 0; + for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) { + if (constructions[i] != NONINDENT_BLOCK) + delta--; + constructionsCount--; + } + if (constructionsCount > 0) { + if (constructions[constructionsCount - 1] != NONINDENT_BLOCK) + delta--; + constructionsCount--; + } + return delta; + } + + /** + * Pops elements until the stack is empty or the top element is + * a BLOCK or a NONINDENT_BLOCK.
+ * Does not remove it from the stack. + */ + private int popInclusiveUntilBlock() { + int startCount = constructionsCount; + int delta = 0; + for (int i = startCount - 1; + i >= 0 && (constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK); + i--) { + delta--; + constructionsCount--; + } + if (constructionsCount > 0) { + if (constructions[constructionsCount - 1] == BLOCK) + delta--; + constructionsCount--; + } + return delta; + } + + /** + * Pushes a block in the stack.
+ * Pushes a BLOCK if the stack is empty or if the top element is a BLOCK, + * pushes NONINDENT_BLOCK otherwise. + * Creates a new bigger array if the current one is full. + */ + private int pushBlock() { + int delta = 0; + if (constructionsCount == constructions.length) + System.arraycopy( + constructions, + 0, + (constructions = new int[constructionsCount * 2]), + 0, + constructionsCount); + + if ((constructionsCount == 0) + || (constructions[constructionsCount - 1] == BLOCK) + || (constructions[constructionsCount - 1] == NONINDENT_BLOCK) + || (constructions[constructionsCount - 1] == TokenNamecase)) { + delta++; + constructions[constructionsCount++] = BLOCK; + } else { + constructions[constructionsCount++] = NONINDENT_BLOCK; + } + return delta; + } + + /** + * Pushes token.
+ * Creates a new bigger array if the current one is full. + */ + private int pushControlStatement(int token) { + if (constructionsCount == constructions.length) + System.arraycopy( + constructions, + 0, + (constructions = new int[constructionsCount * 2]), + 0, + constructionsCount); + constructions[constructionsCount++] = token; + return 1; + } + + private static boolean separateFirstArgumentOn(int currentToken) { + //return (currentToken == TokenNameCOMMA || currentToken == TokenNameSEMICOLON); + return currentToken != TokenNameif + && currentToken != TokenNameLPAREN + && currentToken != TokenNameNOT + && currentToken != TokenNamewhile + && currentToken != TokenNamefor + && currentToken != TokenNameswitch; + } + + /** + * Set the positions to map. The mapped positions should be retrieved using the + * getMappedPositions() method. + * @param positions int[] + * @deprecated Set the positions to map using the format(String, int, int[]) method. + * + * @see #getMappedPositions() + */ + public void setPositionsToMap(int[] positions) { + positionsToMap = positions; + lineDelta = 0; + globalDelta = 0; + mappedPositions = new int[positions.length]; + } + + /** + * Appends a space character to the current line buffer. + */ + private void space() { + currentLineBuffer.append(' '); + increaseLineDelta(1); + } + + /** + * Splits stringToSplit on the top level token
+ * If there are several identical token at the same level, + * the string is cut into many pieces. + * @return an object containing the operator and all the substrings + * or null if the string cannot be split + */ + public SplitLine split(String stringToSplit) { + return split(stringToSplit, 0); + } + + /** + * Splits stringToSplit on the top level token
+ * If there are several identical token at the same level, + * the string is cut into many pieces. + * @return an object containing the operator and all the substrings + * or null if the string cannot be split + */ + public SplitLine split(String stringToSplit, int offsetInGlobalLine) { + /* + * See http://dev.eclipse.org/bugs/show_bug.cgi?id=12540 and + * http://dev.eclipse.org/bugs/show_bug.cgi?id=14387 + */ + if (stringToSplit.indexOf("//$NON-NLS") != -1) { //$NON-NLS-1$ + return null; + } + // local variables + int currentToken = 0; + int splitTokenType = 0; + int splitTokenDepth = Integer.MAX_VALUE; + int splitTokenPriority = Integer.MAX_VALUE; + + int[] substringsStartPositions = new int[10]; + // contains the start position of substrings + int[] substringsEndPositions = new int[10]; + // contains the start position of substrings + int substringsCount = 1; // index in the substringsStartPosition array + int[] splitOperators = new int[10]; + // contains the start position of substrings + int splitOperatorsCount = 0; // index in the substringsStartPosition array + int[] openParenthesisPosition = new int[10]; + int openParenthesisPositionCount = 0; + int position = 0; + int lastOpenParenthesisPosition = -1; + // used to remember the position of the 1st open parenthesis + // needed for a pattern like: A.B(C); we want formatted like A.B( split C); + // setup the scanner with a new source + int lastCommentStartPosition = -1; + // to remember the start position of the last comment + int firstTokenOnLine = -1; + // to remember the first token of the line + int previousToken = -1; + // to remember the previous token. + splitScanner.setSource(stringToSplit.toCharArray()); + + try { + // start the loop + while (true) { + // takes the next token + try { + if (currentToken != Scanner.TokenNameWHITESPACE) + previousToken = currentToken; + currentToken = splitScanner.getNextToken(); + } catch (InvalidInputException e) { + if (!handleInvalidToken(e)) + throw e; + currentToken = 0; // this value is not modify when an exception is raised. + } + if (currentToken == TokenNameEOF) + break; + + if (firstTokenOnLine == -1) { + firstTokenOnLine = currentToken; + } + switch (currentToken) { + case TokenNameRBRACE : + case TokenNameRPAREN : + if (openParenthesisPositionCount > 0) { + if (openParenthesisPositionCount == 1 + && lastOpenParenthesisPosition < openParenthesisPosition[0]) { + lastOpenParenthesisPosition = openParenthesisPosition[0]; + } else if ( + (splitTokenDepth == Integer.MAX_VALUE) + || (splitTokenDepth > openParenthesisPositionCount + && openParenthesisPositionCount == 1)) { + splitTokenType = 0; + splitTokenDepth = openParenthesisPositionCount; + splitTokenPriority = Integer.MAX_VALUE; + substringsStartPositions[0] = 0; + // better token means the whole line until now is the first substring + substringsCount = 1; // resets the count of substrings + substringsEndPositions[0] = openParenthesisPosition[0]; + // substring ends on operator start + position = openParenthesisPosition[0]; + // the string mustn't be cut before the closing parenthesis but after the opening one. + splitOperatorsCount = 1; // resets the count of split operators + splitOperators[0] = 0; + } + openParenthesisPositionCount--; + } + break; + case TokenNameLBRACE : + case TokenNameLPAREN : + if (openParenthesisPositionCount == openParenthesisPosition.length) { + System.arraycopy( + openParenthesisPosition, + 0, + (openParenthesisPosition = new int[openParenthesisPositionCount * 2]), + 0, + openParenthesisPositionCount); + } + openParenthesisPosition[openParenthesisPositionCount++] = + splitScanner.currentPosition; + if (currentToken == TokenNameLPAREN && previousToken == TokenNameRPAREN) { + openParenthesisPosition[openParenthesisPositionCount - 1] = + splitScanner.startPosition; + } + break; + case TokenNameSEMICOLON : // ; + case TokenNameCOMMA : // , + case TokenNameEQUAL : // = + if (openParenthesisPositionCount < splitTokenDepth + || (openParenthesisPositionCount == splitTokenDepth + && splitTokenPriority > getTokenPriority(currentToken))) { + // the current token is better than the one we currently have + // (in level or in priority if same level) + // reset the substringsCount + splitTokenDepth = openParenthesisPositionCount; + splitTokenType = currentToken; + splitTokenPriority = getTokenPriority(currentToken); + substringsStartPositions[0] = 0; + // better token means the whole line until now is the first substring + + if (separateFirstArgumentOn(firstTokenOnLine) + && openParenthesisPositionCount > 0) { + substringsCount = 2; // resets the count of substrings + + substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth - 1]; + substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth - 1]; + substringsEndPositions[1] = splitScanner.startPosition; + splitOperatorsCount = 2; // resets the count of split operators + splitOperators[0] = 0; + splitOperators[1] = currentToken; + position = splitScanner.currentPosition; + // next substring will start from operator end + } else { + substringsCount = 1; // resets the count of substrings + + substringsEndPositions[0] = splitScanner.startPosition; + // substring ends on operator start + position = splitScanner.currentPosition; + // next substring will start from operator end + splitOperatorsCount = 1; // resets the count of split operators + splitOperators[0] = currentToken; + } + } else { + if ((openParenthesisPositionCount == splitTokenDepth + && splitTokenPriority == getTokenPriority(currentToken)) + && splitTokenType != TokenNameEQUAL + && currentToken != TokenNameEQUAL) { + // fix for 1FG0BCN: LFCOM:WIN98 - Missing one indentation after split + // take only the 1st = into account. + // if another token with the same priority is found, + // push the start position of the substring and + // push the token into the stack. + // create a new array object if the current one is full. + if (substringsCount == substringsStartPositions.length) { + System.arraycopy( + substringsStartPositions, + 0, + (substringsStartPositions = new int[substringsCount * 2]), + 0, + substringsCount); + System.arraycopy( + substringsEndPositions, + 0, + (substringsEndPositions = new int[substringsCount * 2]), + 0, + substringsCount); + } + if (splitOperatorsCount == splitOperators.length) { + System.arraycopy( + splitOperators, + 0, + (splitOperators = new int[splitOperatorsCount * 2]), + 0, + splitOperatorsCount); + } + substringsStartPositions[substringsCount] = position; + substringsEndPositions[substringsCount++] = splitScanner.startPosition; + // substring ends on operator start + position = splitScanner.currentPosition; + // next substring will start from operator end + splitOperators[splitOperatorsCount++] = currentToken; + } + } + break; + + case TokenNameCOLON : // : (15.24) + // see 1FK7C5R, we only split on a colon, when it is associated with a question-mark. + // indeed it might appear also behind a case statement, and we do not to break at this point. + if ((splitOperatorsCount == 0) + || splitOperators[splitOperatorsCount - 1] != TokenNameQUESTION) { + break; + } + case TokenNameextends : + // case TokenNameimplements : + // case TokenNamethrows : + + case TokenNameDOT : // . + case TokenNameMULTIPLY : // * (15.16.1) + case TokenNameDIVIDE : // / (15.16.2) + case TokenNameREMAINDER : // % (15.16.3) + case TokenNamePLUS : // + (15.17, 15.17.2) + case TokenNameMINUS : // - (15.17.2) + case TokenNameLEFT_SHIFT : // << (15.18) + case TokenNameRIGHT_SHIFT : // >> (15.18) + // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18) + case TokenNameLESS : // < (15.19.1) + case TokenNameLESS_EQUAL : // <= (15.19.1) + case TokenNameGREATER : // > (15.19.1) + case TokenNameGREATER_EQUAL : // >= (15.19.1) + // case TokenNameinstanceof : // instanceof + case TokenNameEQUAL_EQUAL : // == (15.20, 15.20.1, 15.20.2, 15.20.3) + case TokenNameNOT_EQUAL : // != (15.20, 15.20.1, 15.20.2, 15.20.3) + case TokenNameAND : // & (15.21, 15.21.1, 15.21.2) + case TokenNameOR : // | (15.21, 15.21.1, 15.21.2) + case TokenNameXOR : // ^ (15.21, 15.21.1, 15.21.2) + case TokenNameAND_AND : // && (15.22) + case TokenNameOR_OR : // || (15.23) + case TokenNameQUESTION : // ? (15.24) + case TokenNameMULTIPLY_EQUAL : // *= (15.25.2) + case TokenNameDIVIDE_EQUAL : // /= (15.25.2) + case TokenNameREMAINDER_EQUAL : // %= (15.25.2) + case TokenNamePLUS_EQUAL : // += (15.25.2) + case TokenNameMINUS_EQUAL : // -= (15.25.2) + case TokenNameLEFT_SHIFT_EQUAL : // <<= (15.25.2) + case TokenNameRIGHT_SHIFT_EQUAL : // >>= (15.25.2) +// case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2) + case TokenNameAND_EQUAL : // &= (15.25.2) + case TokenNameXOR_EQUAL : // ^= (15.25.2) + case TokenNameOR_EQUAL : // |= (15.25.2) + + if ((openParenthesisPositionCount < splitTokenDepth + || (openParenthesisPositionCount == splitTokenDepth + && splitTokenPriority > getTokenPriority(currentToken))) + && !((currentToken == TokenNamePLUS || currentToken == TokenNameMINUS) + && (previousToken == TokenNameLBRACE + || previousToken == TokenNameLBRACKET + || splitScanner.startPosition == 0))) { + // the current token is better than the one we currently have + // (in level or in priority if same level) + // reset the substringsCount + splitTokenDepth = openParenthesisPositionCount; + splitTokenType = currentToken; + splitTokenPriority = getTokenPriority(currentToken); + substringsStartPositions[0] = 0; + // better token means the whole line until now is the first substring + + if (separateFirstArgumentOn(firstTokenOnLine) + && openParenthesisPositionCount > 0) { + substringsCount = 2; // resets the count of substrings + + substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth - 1]; + substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth - 1]; + substringsEndPositions[1] = splitScanner.startPosition; + splitOperatorsCount = 3; // resets the count of split operators + splitOperators[0] = 0; + splitOperators[1] = 0; + splitOperators[2] = currentToken; + position = splitScanner.currentPosition; + // next substring will start from operator end + } else { + substringsCount = 1; // resets the count of substrings + + substringsEndPositions[0] = splitScanner.startPosition; + // substring ends on operator start + position = splitScanner.currentPosition; + // next substring will start from operator end + splitOperatorsCount = 2; // resets the count of split operators + splitOperators[0] = 0; + // nothing for first operand since operator will be inserted in front of the second operand + splitOperators[1] = currentToken; + + } + } else { + if (openParenthesisPositionCount == splitTokenDepth + && splitTokenPriority == getTokenPriority(currentToken)) { + // if another token with the same priority is found, + // push the start position of the substring and + // push the token into the stack. + // create a new array object if the current one is full. + if (substringsCount == substringsStartPositions.length) { + System.arraycopy( + substringsStartPositions, + 0, + (substringsStartPositions = new int[substringsCount * 2]), + 0, + substringsCount); + System.arraycopy( + substringsEndPositions, + 0, + (substringsEndPositions = new int[substringsCount * 2]), + 0, + substringsCount); + } + if (splitOperatorsCount == splitOperators.length) { + System.arraycopy( + splitOperators, + 0, + (splitOperators = new int[splitOperatorsCount * 2]), + 0, + splitOperatorsCount); + } + substringsStartPositions[substringsCount] = position; + substringsEndPositions[substringsCount++] = splitScanner.startPosition; + // substring ends on operator start + position = splitScanner.currentPosition; + // next substring will start from operator end + splitOperators[splitOperatorsCount++] = currentToken; + } + } + default : + break; + } + if (isComment(currentToken)) { + lastCommentStartPosition = splitScanner.startPosition; + } else { + lastCommentStartPosition = -1; + } + } + } catch (InvalidInputException e) { + return null; + } + // if the string cannot be split, return null. + if (splitOperatorsCount == 0) + return null; + + // ## SPECIAL CASES BEGIN + if (((splitOperatorsCount == 2 + && splitOperators[1] == TokenNameDOT + && splitTokenDepth == 0 + && lastOpenParenthesisPosition > -1) + || (splitOperatorsCount > 2 + && splitOperators[1] == TokenNameDOT + && splitTokenDepth == 0 + && lastOpenParenthesisPosition > -1 + && lastOpenParenthesisPosition <= options.maxLineLength) + || (separateFirstArgumentOn(firstTokenOnLine) + && splitTokenDepth > 0 + && lastOpenParenthesisPosition > -1)) + && (lastOpenParenthesisPosition < splitScanner.source.length + && splitScanner.source[lastOpenParenthesisPosition] != ')')) { + // fix for 1FH4J2H: LFCOM:WINNT - Formatter - Empty parenthesis should not be broken on two lines + // only one split on a top level . + // or more than one split on . and substring before open parenthesis fits one line. + // or split inside parenthesis and first token is not a for/while/if + SplitLine sl = + split( + stringToSplit.substring(lastOpenParenthesisPosition), + lastOpenParenthesisPosition); + if (sl == null || sl.operators[0] != TokenNameCOMMA) { + // trim() is used to remove the extra blanks at the end of the substring. See PR 1FGYPI1 + return new SplitLine( + new int[] { 0, 0 }, + new String[] { + stringToSplit.substring(0, lastOpenParenthesisPosition).trim(), + stringToSplit.substring(lastOpenParenthesisPosition)}, + new int[] { + offsetInGlobalLine, + lastOpenParenthesisPosition + offsetInGlobalLine }); + } else { + // right substring can be split and is split on comma + // copy substrings and operators + // except if the 1st string is empty. + int startIndex = (sl.substrings[0].length() == 0) ? 1 : 0; + int subStringsLength = sl.substrings.length + 1 - startIndex; + String[] result = new String[subStringsLength]; + int[] startIndexes = new int[subStringsLength]; + int operatorsLength = sl.operators.length + 1 - startIndex; + int[] operators = new int[operatorsLength]; + + result[0] = stringToSplit.substring(0, lastOpenParenthesisPosition); + operators[0] = 0; + + System.arraycopy( + sl.startSubstringsIndexes, + startIndex, + startIndexes, + 1, + subStringsLength - 1); + for (int i = subStringsLength - 1; i >= 0; i--) { + startIndexes[i] += offsetInGlobalLine; + } + System.arraycopy(sl.substrings, startIndex, result, 1, subStringsLength - 1); + System.arraycopy(sl.operators, startIndex, operators, 1, operatorsLength - 1); + + return new SplitLine(operators, result, startIndexes); + } + } + // if the last token is a comment and the substring before the comment fits on a line, + // split before the comment and return the result. + if (lastCommentStartPosition > -1 + && lastCommentStartPosition < options.maxLineLength + && splitTokenPriority > 50) { + int end = lastCommentStartPosition; + int start = lastCommentStartPosition; + if (stringToSplit.charAt(end - 1) == ' ') { + end--; + } + if (start != end && stringToSplit.charAt(start) == ' ') { + start++; + } + return new SplitLine( + new int[] { 0, 0 }, + new String[] { stringToSplit.substring(0, end), stringToSplit.substring(start)}, + new int[] { 0, start }); + } + if (position != stringToSplit.length()) { + if (substringsCount == substringsStartPositions.length) { + System.arraycopy( + substringsStartPositions, + 0, + (substringsStartPositions = new int[substringsCount * 2]), + 0, + substringsCount); + System.arraycopy( + substringsEndPositions, + 0, + (substringsEndPositions = new int[substringsCount * 2]), + 0, + substringsCount); + } + // avoid empty extra substring, e.g. line terminated with a semi-colon + substringsStartPositions[substringsCount] = position; + substringsEndPositions[substringsCount++] = stringToSplit.length(); + } + if (splitOperatorsCount == splitOperators.length) { + System.arraycopy( + splitOperators, + 0, + (splitOperators = new int[splitOperatorsCount * 2]), + 0, + splitOperatorsCount); + } + splitOperators[splitOperatorsCount] = 0; + + // the last element of the stack is the position of the end of StringToSPlit + // +1 because the substring method excludes the last character + String[] result = new String[substringsCount]; + for (int i = 0; i < substringsCount; i++) { + int start = substringsStartPositions[i]; + int end = substringsEndPositions[i]; + if (stringToSplit.charAt(start) == ' ') { + start++; + substringsStartPositions[i]++; + } + if (end != start && stringToSplit.charAt(end - 1) == ' ') { + end--; + } + result[i] = stringToSplit.substring(start, end); + substringsStartPositions[i] += offsetInGlobalLine; + } + if (splitOperatorsCount > substringsCount) { + System.arraycopy( + substringsStartPositions, + 0, + (substringsStartPositions = new int[splitOperatorsCount]), + 0, + substringsCount); + System.arraycopy( + substringsEndPositions, + 0, + (substringsEndPositions = new int[splitOperatorsCount]), + 0, + substringsCount); + for (int i = substringsCount; i < splitOperatorsCount; i++) { + substringsStartPositions[i] = position; + substringsEndPositions[i] = position; + } + System.arraycopy( + splitOperators, + 0, + (splitOperators = new int[splitOperatorsCount]), + 0, + splitOperatorsCount); + } else { + System.arraycopy( + substringsStartPositions, + 0, + (substringsStartPositions = new int[substringsCount]), + 0, + substringsCount); + System.arraycopy( + substringsEndPositions, + 0, + (substringsEndPositions = new int[substringsCount]), + 0, + substringsCount); + System.arraycopy( + splitOperators, + 0, + (splitOperators = new int[substringsCount]), + 0, + substringsCount); + } + SplitLine splitLine = + new SplitLine(splitOperators, result, substringsStartPositions); + return splitLine; + } + + private void updateMappedPositions(int startPosition) { + if (positionsToMap == null) { + return; + } + char[] source = scanner.source; + int sourceLength = source.length; + while (indexToMap < positionsToMap.length + && positionsToMap[indexToMap] <= startPosition) { + int posToMap = positionsToMap[indexToMap]; + if (posToMap < 0 + || posToMap >= sourceLength) { // protection against out of bounds position + if (posToMap == sourceLength) { + mappedPositions[indexToMap] = formattedSource.length(); + } + indexToMap = positionsToMap.length; // no more mapping + return; + } + if (CharOperation.isWhitespace(source[posToMap])) { + mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta; + } else { + if (posToMap == sourceLength - 1) { + mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta; + } else { + mappedPositions[indexToMap] = posToMap + globalDelta + lineDelta; + } + } + indexToMap++; + } + } + + private void updateMappedPositionsWhileSplitting( + int startPosition, + int endPosition) { + if (mappedPositions == null || mappedPositions.length == indexInMap) + return; + + while (indexInMap < mappedPositions.length + && startPosition <= mappedPositions[indexInMap] + && mappedPositions[indexInMap] < endPosition + && indexInMap < indexToMap) { + mappedPositions[indexInMap] += splitDelta; + indexInMap++; + } + } + + private int getLength(String s, int tabDepth) { + int length = 0; + for (int i = 0; i < tabDepth; i++) { + length += options.tabSize; + } + for (int i = 0, max = s.length(); i < max; i++) { + char currentChar = s.charAt(i); + switch (currentChar) { + case '\t' : + length += options.tabSize; + break; + default : + length++; + } + } + return length; + } + + /** + * Sets the initial indentation level + * @param indentationLevel new indentation level + * + * @deprecated + */ + public void setInitialIndentationLevel(int newIndentationLevel) { + this.initialIndentationLevel = + currentLineIndentationLevel = indentationLevel = newIndentationLevel; + } +} \ No newline at end of file diff --git a/net.sourceforge.phpeclipse/src/org/phpeclipse/phpdt/internal/formatter/Options.properties b/net.sourceforge.phpeclipse/src/org/phpeclipse/phpdt/internal/formatter/Options.properties new file mode 100644 index 0000000..822c1c9 --- /dev/null +++ b/net.sourceforge.phpeclipse/src/org/phpeclipse/phpdt/internal/formatter/Options.properties @@ -0,0 +1,59 @@ +newline.openingBrace.number=1 +newline.openingBrace.category=Newline +newline.openingBrace.name=I&nsert new line before opening brace +newline.openingBrace.possibleValues=2|Insert|Do not insert +newline.openingBrace.description=When Insert, a new line is inserted before an opening brace, otherwise nothing is inserted + +newline.controlStatement.number=2 +newline.controlStatement.category=Newline +newline.controlStatement.name=Insert new &line in control statement +newline.controlStatement.possibleValues=2|Insert|Do not insert +newline.controlStatement.description=When Insert, a new line is inserted between } and else, catch, finally + +newline.clearAll.number=3 +newline.clearAll.category=Newline +newline.clearAll.name=Clear all &blank lines +newline.clearAll.possibleValues=2|Clear|Preserve one +newline.clearAll.description=When Clear, all blank lines are removed. When Preserve one, only one is kept and all others removed. + +newline.elseIf.number=4 +newline.elseIf.category=Newline +newline.elseIf.name=&Keep else if on the same line +newline.elseIf.possibleValues=2|Yes|No +newline.elseIf.description=When Yes, a blank line is inserted between a else and a if when they are contiguous + +newline.emptyBlock.number=5 +newline.emptyBlock.category=Newline +newline.emptyBlock.name=In&sert a new line inside an empty block +newline.emptyBlock.possibleValues=2|Insert|Do not insert +newline.emptyBlock.description=When insert, a line break is inserted between contiguous { and }, if } is not followed by a keyword. + +line.split.number=6 +line.split.category=Line splitting +line.split.name=Ma&ximum line length +line.split.possibleValues=-1 +line.split.description=Enable splitting of long lines (exceeding the configurable length). Length of 0 will disable line splitting + +style.compactAssignment.number=7 +style.compactAssignment.category=Style +style.compactAssignment.name=&Compact assignment +style.compactAssignment.possibleValues=2|Compact|Normal +style.compactAssignment.description=Assignments can be formatted asymmetrically, e.g. 'int x= 2;', when Normal, a space is inserted before the assignment operator + +style.reuseExistingLayout.number=8 +style.reuseExistingLayout.category=Style +style.reuseExistingLayout.name=&Reuse existing layout +style.reuseExistingLayout.possibleValues=2|Reuse|Do not reuse +style.reuseExistingLayout.description=If the user has formatted his code a certain way, the formatter does not try to reformat it + +tabulation.char.number=9 +tabulation.char.category=Style +tabulation.char.name=Indentation is represented by &tab +tabulation.char.possibleValues=2|Tab|Spaces +tabulation.char.description=Either choose to indent with tab characters or spaces + +tabulation.size.number=10 +tabulation.size.category=Style +tabulation.size.name=&Amount of spaces representing a tab +tabulation.size.possibleValues=-1 +tabulation.size.description=Tabulation size in term of space characters \ No newline at end of file diff --git a/net.sourceforge.phpeclipse/src/org/phpeclipse/phpdt/internal/formatter/impl/FormatterOptions.java b/net.sourceforge.phpeclipse/src/org/phpeclipse/phpdt/internal/formatter/impl/FormatterOptions.java new file mode 100644 index 0000000..a10281f --- /dev/null +++ b/net.sourceforge.phpeclipse/src/org/phpeclipse/phpdt/internal/formatter/impl/FormatterOptions.java @@ -0,0 +1,199 @@ +/******************************************************************************* + * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v0.5 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v05.html + * + * Contributors: + * IBM Corporation - initial API and implementation + ******************************************************************************/ +package org.phpeclipse.phpdt.internal.formatter.impl; + import java.util.Map; + +public class FormatterOptions { + /** + * Option IDs + */ + public static final String OPTION_InsertNewlineBeforeOpeningBrace = "org.phpeclipse.phpdt.core.formatter.newline.openingBrace"; //$NON-NLS-1$ + public static final String OPTION_InsertNewlineInControlStatement = "org.phpeclipse.phpdt.core.formatter.newline.controlStatement"; //$NON-NLS-1$ + public static final String OPTION_InsertNewLineBetweenElseAndIf = "org.phpeclipse.phpdt.core.formatter.newline.elseIf"; //$NON-NLS-1$ + public static final String OPTION_InsertNewLineInEmptyBlock = "org.phpeclipse.phpdt.core.formatter.newline.emptyBlock"; //$NON-NLS-1$ + public static final String OPTION_ClearAllBlankLines = "org.phpeclipse.phpdt.core.formatter.newline.clearAll"; //$NON-NLS-1$ + public static final String OPTION_SplitLineExceedingLength = "org.phpeclipse.phpdt.core.formatter.lineSplit"; //$NON-NLS-1$ + public static final String OPTION_CompactAssignment = "org.phpeclipse.phpdt.core.formatter.style.assignment"; //$NON-NLS-1$ + public static final String OPTION_TabulationChar = "org.phpeclipse.phpdt.core.formatter.tabulation.char"; //$NON-NLS-1$ + public static final String OPTION_TabulationSize = "org.phpeclipse.phpdt.core.formatter.tabulation.size"; //$NON-NLS-1$ + + public static final String INSERT = "insert"; //$NON-NLS-1$ + public static final String DO_NOT_INSERT = "do not insert"; //$NON-NLS-1$ + public static final String PRESERVE_ONE = "preserve one"; //$NON-NLS-1$ + public static final String CLEAR_ALL = "clear all"; //$NON-NLS-1$ + public static final String NORMAL = "normal"; //$NON-NLS-1$ + public static final String COMPACT = "compact"; //$NON-NLS-1$ + public static final String TAB = "tab"; //$NON-NLS-1$ + public static final String SPACE = "space"; //$NON-NLS-1$ + + // by default, do not insert blank line before opening brace + public boolean newLineBeforeOpeningBraceMode = false; + + // by default, do not insert blank line behind keywords (ELSE, CATCH, FINALLY,...) in control statements + public boolean newlineInControlStatementMode = false; + + // by default, preserve one blank line per sequence of blank lines + public boolean clearAllBlankLinesMode = false; + + // line splitting will occur when line exceeds this length + public int maxLineLength = 80; + + public boolean compactAssignmentMode = false; // if isTrue, assignments look like x= 12 (not like x = 12); + + //number of consecutive spaces used to replace the tab char + public int tabSize = 4; // n spaces for one tab + public boolean indentWithTab = true; + + public boolean compactElseIfMode = true; // if true, else and if are kept on the same line. + public boolean newLineInEmptyBlockMode = true; // if false, no new line in {} if it's empty. + + public char[] lineSeparatorSequence = System.getProperty("line.separator").toCharArray(); //$NON-NLS-1$ +/** + * Initializing the formatter options with default settings + */ +public FormatterOptions(){ +} +/** + * Initializing the formatter options with external settings + */ +public FormatterOptions(Map settings){ + if (settings == null) return; + + // filter options which are related to the assist component + Object[] entries = settings.entrySet().toArray(); + for (int i = 0, max = entries.length; i < max; i++){ + Map.Entry entry = (Map.Entry)entries[i]; + if (!(entry.getKey() instanceof String)) continue; + if (!(entry.getValue() instanceof String)) continue; + String optionID = (String) entry.getKey(); + String optionValue = (String) entry.getValue(); + + if(optionID.equals(OPTION_InsertNewlineBeforeOpeningBrace)){ + if (optionValue.equals(INSERT)){ + this.newLineBeforeOpeningBraceMode = true; + } else if (optionValue.equals(DO_NOT_INSERT)){ + this.newLineBeforeOpeningBraceMode = false; + } + continue; + } + if(optionID.equals(OPTION_InsertNewlineInControlStatement)){ + if (optionValue.equals(INSERT)){ + this.newlineInControlStatementMode = true; + } else if (optionValue.equals(DO_NOT_INSERT)){ + this.newlineInControlStatementMode = false; + } + continue; + } + if(optionID.equals(OPTION_ClearAllBlankLines)){ + if (optionValue.equals(CLEAR_ALL)){ + this.clearAllBlankLinesMode = true; + } else if (optionValue.equals(PRESERVE_ONE)){ + this.clearAllBlankLinesMode = false; + } + continue; + } + if(optionID.equals(OPTION_InsertNewLineBetweenElseAndIf)){ + if (optionValue.equals(INSERT)){ + this.compactElseIfMode = false; + } else if (optionValue.equals(DO_NOT_INSERT)){ + this.compactElseIfMode = true; + } + continue; + } + if(optionID.equals(OPTION_InsertNewLineInEmptyBlock)){ + if (optionValue.equals(INSERT)){ + this.newLineInEmptyBlockMode = true; + } else if (optionValue.equals(DO_NOT_INSERT)){ + this.newLineInEmptyBlockMode = false; + } + continue; + } + if(optionID.equals(OPTION_SplitLineExceedingLength)){ + try { + int val = Integer.parseInt(optionValue); + if (val >= 0) this.maxLineLength = val; + } catch(NumberFormatException e){ + } + } + if(optionID.equals(OPTION_CompactAssignment)){ + if (optionValue.equals(COMPACT)){ + this.compactAssignmentMode = true; + } else if (optionValue.equals(NORMAL)){ + this.compactAssignmentMode = false; + } + continue; + } + if(optionID.equals(OPTION_TabulationChar)){ + if (optionValue.equals(TAB)){ + this.indentWithTab = true; + } else if (optionValue.equals(SPACE)){ + this.indentWithTab = false; + } + continue; + } + if(optionID.equals(OPTION_TabulationSize)){ + try { + int val = Integer.parseInt(optionValue); + if (val > 0) this.tabSize = val; + } catch(NumberFormatException e){ + } + } + } +} + +/** + * + * @return int + */ +public int getMaxLineLength() { + return maxLineLength; +} +public int getTabSize() { + return tabSize; +} +public boolean isAddingNewLineBeforeOpeningBrace() { + return newLineBeforeOpeningBraceMode; +} +public boolean isAddingNewLineInControlStatement() { + return newlineInControlStatementMode; +} +public boolean isAddingNewLineInEmptyBlock() { + return newLineInEmptyBlockMode; +} +public boolean isClearingAllBlankLines() { + return clearAllBlankLinesMode; +} +public boolean isCompactingAssignment() { + return compactAssignmentMode; +} +public boolean isCompactingElseIf() { + return compactElseIfMode; +} +public boolean isUsingTabForIndenting() { + return indentWithTab; +} +public void setLineSeparator(String lineSeparator) { + lineSeparatorSequence = lineSeparator.toCharArray(); +} +/** + * @deprecated - should use a Map when creating the options. + */ +public void setMaxLineLength(int maxLineLength) { + this.maxLineLength = maxLineLength; +} +/** + * @deprecated - should use a Map when creating the options. + */ +public void setCompactElseIfMode(boolean flag) { + compactElseIfMode = flag; +} + +} diff --git a/net.sourceforge.phpeclipse/src/org/phpeclipse/phpdt/internal/formatter/impl/SplitLine.java b/net.sourceforge.phpeclipse/src/org/phpeclipse/phpdt/internal/formatter/impl/SplitLine.java new file mode 100644 index 0000000..275baf0 --- /dev/null +++ b/net.sourceforge.phpeclipse/src/org/phpeclipse/phpdt/internal/formatter/impl/SplitLine.java @@ -0,0 +1,211 @@ +/******************************************************************************* + * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v0.5 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v05.html + * + * Contributors: + * IBM Corporation - initial API and implementation + ******************************************************************************/ +package org.phpeclipse.phpdt.internal.formatter.impl; + +import net.sourceforge.phpdt.core.compiler.ITerminalSymbols; + +/** Represents a split line: contains an operator and all substrings +*/ +public class SplitLine implements ITerminalSymbols{ + public int[] operators; // the operator on which the string was split. + public String[] substrings; + public int[] startSubstringsIndexes; +/** + * SplitLine constructor comment. + */ +public SplitLine(int[] operators, String[] substrings) { + this(operators, substrings, null); +} +/** + * SplitLine constructor comment. + */ +public SplitLine(int[] operators, String[] substrings, int[] startIndexes) { + super(); + this.operators=operators; + this.substrings=substrings; + this.startSubstringsIndexes = startIndexes; +} +/** + * Prints a nice representation of the receiver + * @return java.lang.String + */ +public String toString() { + StringBuffer result=new StringBuffer(); + String operatorString = new String(); + + for (int i=0,max=substrings.length;i (15.19.1) + operatorString=">"; //$NON-NLS-1$ + break; + + case TokenNameGREATER_EQUAL : // >= (15.19.1) + operatorString=">="; //$NON-NLS-1$ + break; + +// case TokenNameinstanceof : // instanceof +// operatorString="instanceof"; //$NON-NLS-1$ +// break; + case TokenNamePLUS : // + (15.17, 15.17.2) + operatorString="+"; //$NON-NLS-1$ + break; + + case TokenNameMINUS : // - (15.17.2) + operatorString="-"; //$NON-NLS-1$ + break; + case TokenNameMULTIPLY : // * (15.16.1) + operatorString="*"; //$NON-NLS-1$ + break; + + case TokenNameDIVIDE : // / (15.16.2) + operatorString="/"; //$NON-NLS-1$ + break; + + case TokenNameREMAINDER : // % (15.16.3) + operatorString="%"; //$NON-NLS-1$ + break; + case TokenNameLEFT_SHIFT : // << (15.18) + operatorString="<<"; //$NON-NLS-1$ + break; + + case TokenNameRIGHT_SHIFT : // >> (15.18) + operatorString=">>"; //$NON-NLS-1$ + break; + +// case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18) +// operatorString=">>>"; //$NON-NLS-1$ +// break; + case TokenNameAND : // & (15.21, 15.21.1, 15.21.2) + operatorString="&"; //$NON-NLS-1$ + break; + + case TokenNameOR : // | (15.21, 15.21.1, 15.21.2) + operatorString="|"; //$NON-NLS-1$ + break; + + case TokenNameXOR : // ^ (15.21, 15.21.1, 15.21.2) + operatorString="^"; //$NON-NLS-1$ + break; + case TokenNameMULTIPLY_EQUAL : // *= (15.25.2) + operatorString="*="; //$NON-NLS-1$ + break; + + case TokenNameDIVIDE_EQUAL : // /= (15.25.2) + operatorString="/="; //$NON-NLS-1$ + break; + case TokenNameREMAINDER_EQUAL : // %= (15.25.2) + operatorString="%="; //$NON-NLS-1$ + break; + + case TokenNamePLUS_EQUAL : // += (15.25.2) + operatorString="+="; //$NON-NLS-1$ + break; + + case TokenNameMINUS_EQUAL : // -= (15.25.2) + operatorString="-="; //$NON-NLS-1$ + break; + + case TokenNameLEFT_SHIFT_EQUAL : // <<= (15.25.2) + operatorString="<<="; //$NON-NLS-1$ + break; + + case TokenNameRIGHT_SHIFT_EQUAL : // >>= (15.25.2) + operatorString=">>="; //$NON-NLS-1$ + break; + +// case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2) +// operatorString=">>>="; //$NON-NLS-1$ +// break; + + case TokenNameAND_EQUAL : // &= (15.25.2) + operatorString="&="; //$NON-NLS-1$ + break; + + case TokenNameXOR_EQUAL : // ^= (15.25.2) + operatorString="^="; //$NON-NLS-1$ + break; + + case TokenNameOR_EQUAL : // |= (15.25.2) + operatorString="|="; //$NON-NLS-1$ + break; + case TokenNameDOT : // . + operatorString="."; //$NON-NLS-1$ + break; + + default: + operatorString=""; //$NON-NLS-1$ + } + if (placeOperatorAhead){ + result.append(operatorString); + } + result.append(currentString); + if (placeOperatorBehind){ + result.append(operatorString); + } + result.append('\n'); + } + return ""; //$NON-NLS-1$ +} +}