content: /*******************************************************************************
   * Copyright (c) 2000, 2010 IBM Corporation and others.
   * All rights reserved. This program and the accompanying materials
   * are made available under the terms of the Eclipse Public License v1.0
   * which accompanies this distribution, and is available at
   * http://www.eclipse.org/legal/epl-v10.html
   *
   * Contributors:
   *     IBM Corporation - initial API and implementation
   *     John Kaplan, johnkaplantech@gmail.com - 108071 [code templates] template for body of newly created class
   *******************************************************************************/
  package org.eclipse.wst.jsdt.internal.corext.codemanipulation;
  
  import java.io.IOException;
  import java.lang.reflect.Modifier;
  import java.util.AbstractList;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collection;
  import java.util.Comparator;
  import java.util.HashSet;
  import java.util.LinkedHashSet;
  import java.util.List;
  import java.util.Set;
  import java.util.StringTokenizer;
  
  import org.eclipse.core.resources.IProject;
  import org.eclipse.core.resources.ProjectScope;
  import org.eclipse.core.runtime.CoreException;
  import org.eclipse.core.runtime.IStatus;
  import org.eclipse.core.runtime.Platform;
  import org.eclipse.core.runtime.Status;
  import org.eclipse.core.runtime.preferences.IScopeContext;
  import org.eclipse.core.runtime.preferences.InstanceScope;
  import org.eclipse.jface.text.BadLocationException;
  import org.eclipse.jface.text.Document;
  import org.eclipse.jface.text.IDocument;
  import org.eclipse.jface.text.IRegion;
  import org.eclipse.jface.text.templates.Template;
  import org.eclipse.jface.text.templates.TemplateBuffer;
  import org.eclipse.jface.text.templates.TemplateException;
  import org.eclipse.jface.text.templates.TemplateVariable;
  import org.eclipse.jface.text.templates.persistence.TemplatePersistenceData;
  import org.eclipse.jface.text.templates.persistence.TemplateStore;
  import org.eclipse.text.edits.DeleteEdit;
  import org.eclipse.text.edits.MalformedTreeException;
  import org.eclipse.text.edits.MultiTextEdit;
  import org.eclipse.wst.jsdt.core.Flags;
  import org.eclipse.wst.jsdt.core.IBuffer;
  import org.eclipse.wst.jsdt.core.IFunction;
  import org.eclipse.wst.jsdt.core.IJavaScriptElement;
  import org.eclipse.wst.jsdt.core.IJavaScriptProject;
  import org.eclipse.wst.jsdt.core.IJavaScriptUnit;
  import org.eclipse.wst.jsdt.core.IOpenable;
  import org.eclipse.wst.jsdt.core.IPackageFragment;
  import org.eclipse.wst.jsdt.core.IParent;
  import org.eclipse.wst.jsdt.core.ISourceReference;
  import org.eclipse.wst.jsdt.core.IType;
  import org.eclipse.wst.jsdt.core.JavaScriptConventions;
  import org.eclipse.wst.jsdt.core.JavaScriptCore;
  import org.eclipse.wst.jsdt.core.JavaScriptModelException;
  import org.eclipse.wst.jsdt.core.NamingConventions;
  import org.eclipse.wst.jsdt.core.Signature;
  import org.eclipse.wst.jsdt.core.dom.AST;
  import org.eclipse.wst.jsdt.core.dom.ASTNode;
  import org.eclipse.wst.jsdt.core.dom.ASTParser;
  import org.eclipse.wst.jsdt.core.dom.AbstractTypeDeclaration;
  import org.eclipse.wst.jsdt.core.dom.ArrayType;
  import org.eclipse.wst.jsdt.core.dom.ClassInstanceCreation;
  import org.eclipse.wst.jsdt.core.dom.ConstructorInvocation;
  import org.eclipse.wst.jsdt.core.dom.Expression;
  import org.eclipse.wst.jsdt.core.dom.FieldAccess;
  import org.eclipse.wst.jsdt.core.dom.FunctionDeclaration;
  import org.eclipse.wst.jsdt.core.dom.FunctionInvocation;
  import org.eclipse.wst.jsdt.core.dom.IBinding;
  import org.eclipse.wst.jsdt.core.dom.IFunctionBinding;
  import org.eclipse.wst.jsdt.core.dom.ITypeBinding;
  import org.eclipse.wst.jsdt.core.dom.IVariableBinding;
  import org.eclipse.wst.jsdt.core.dom.JavaScriptUnit;
  import org.eclipse.wst.jsdt.core.dom.Name;
  import org.eclipse.wst.jsdt.core.dom.NumberLiteral;
  import org.eclipse.wst.jsdt.core.dom.SimpleName;
  import org.eclipse.wst.jsdt.core.dom.SingleVariableDeclaration;
  import org.eclipse.wst.jsdt.core.dom.StringLiteral;
  import org.eclipse.wst.jsdt.core.dom.StructuralPropertyDescriptor;
  import org.eclipse.wst.jsdt.core.dom.SuperConstructorInvocation;
  import org.eclipse.wst.jsdt.core.dom.SuperMethodInvocation;
  import org.eclipse.wst.jsdt.core.dom.Type;
  import org.eclipse.wst.jsdt.core.dom.rewrite.ImportRewrite;
  import org.eclipse.wst.jsdt.core.formatter.IndentManipulation;
  import org.eclipse.wst.jsdt.internal.corext.dom.ASTNodes;
  import org.eclipse.wst.jsdt.internal.corext.dom.Bindings;
  import org.eclipse.wst.jsdt.internal.corext.template.java.CodeTemplateContext;
  import org.eclipse.wst.jsdt.internal.corext.template.java.CodeTemplateContextType;
  import org.eclipse.wst.jsdt.internal.corext.util.JavaModelUtil;
  import org.eclipse.wst.jsdt.internal.corext.util.Strings;
  import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin;
  import org.eclipse.wst.jsdt.internal.ui.JavaUIStatus;
  import org.eclipse.wst.jsdt.internal.ui.text.correction.ASTResolving;
  import org.eclipse.wst.jsdt.internal.ui.viewsupport.ProjectTemplateStore;
  import org.eclipse.wst.jsdt.ui.CodeStyleConfiguration;
  import org.eclipse.wst.jsdt.ui.PreferenceConstants;
  /**
  *
  * Provisional API: This class/interface is part of an interim API that is still under development and expected to
  * change significantly before reaching stability. It is being made available at this early stage to solicit feedback
  * from pioneering adopters on the understanding that any code that uses this API will almost certainly be broken
  * (repeatedly) as the API evolves.
  */
  public class StubUtility {
  	
  	private static final String[] EMPTY= new String[0];
  	
  	private static final Set VALID_TYPE_BODY_TEMPLATES;
  	static {
  		VALID_TYPE_BODY_TEMPLATES= new HashSet();
  		VALID_TYPE_BODY_TEMPLATES.add(CodeTemplateContextType.CLASSBODY_ID);
  	}
  	
  	/*
  	 * Don't use this method directly, use CodeGeneration.
  	 */
  	public static String getMethodBodyContent(boolean isConstructor, IJavaScriptProject project, String destTypeName, String methodName, String bodyStatement, String lineDelimiter) throws CoreException {
  		String templateName= isConstructor ? CodeTemplateContextType.CONSTRUCTORSTUB_ID : CodeTemplateContextType.METHODSTUB_ID;
  		Template template= getCodeTemplate(templateName, project);
  		if (template == null) {
  			return bodyStatement;
  		}
  		CodeTemplateContext context= new CodeTemplateContext(template.getContextTypeId(), project, lineDelimiter);
  		context.setVariable(CodeTemplateContextType.ENCLOSING_METHOD, methodName);
  		context.setVariable(CodeTemplateContextType.ENCLOSING_TYPE, destTypeName);
  		context.setVariable(CodeTemplateContextType.BODY_STATEMENT, bodyStatement);
  		String str= evaluateTemplate(context, template, new String[] { CodeTemplateContextType.BODY_STATEMENT });
  		if (str == null && !Strings.containsOnlyWhitespaces(bodyStatement)) {
  			return bodyStatement;
  		}
  		return str;
  	}
  	
  	/*
  	 * Don't use this method directly, use CodeGeneration.
  	 */
  	public static String getGetterMethodBodyContent(IJavaScriptProject project, String destTypeName, String methodName, String fieldName, String lineDelimiter) throws CoreException {
  		String templateName= CodeTemplateContextType.GETTERSTUB_ID;
  		Template template= getCodeTemplate(templateName, project);
  		if (template == null) {
  			return null;
  		}
  		CodeTemplateContext context= new CodeTemplateContext(template.getContextTypeId(), project, lineDelimiter);
  		context.setVariable(CodeTemplateContextType.ENCLOSING_METHOD, methodName);
  		context.setVariable(CodeTemplateContextType.ENCLOSING_TYPE, destTypeName);
  		context.setVariable(CodeTemplateContextType.FIELD, fieldName);
  		
  		return evaluateTemplate(context, template);
  	}
  	
  	/*
  	 * Don't use this method directly, use CodeGeneration.
  	 */
  	public static String getSetterMethodBodyContent(IJavaScriptProject project, String destTypeName, String methodName, String fieldName, String paramName, String lineDelimiter) throws CoreException {
  		String templateName= CodeTemplateContextType.SETTERSTUB_ID;
  		Template template= getCodeTemplate(templateName, project);
  		if (template == null) {
  			return null;
  		}
  		CodeTemplateContext context= new CodeTemplateContext(template.getContextTypeId(), project, lineDelimiter);
  		context.setVariable(CodeTemplateContextType.ENCLOSING_METHOD, methodName);
  		context.setVariable(CodeTemplateContextType.ENCLOSING_TYPE, destTypeName);
  		context.setVariable(CodeTemplateContextType.FIELD, fieldName);
  		context.setVariable(CodeTemplateContextType.FIELD_TYPE, fieldName);
  		context.setVariable(CodeTemplateContextType.PARAM, paramName);
  		
  		return evaluateTemplate(context, template);
  	}
  	
  	public static String getCatchBodyContent(IJavaScriptUnit cu, String exceptionType, String variableName, ASTNode locationInAST, String lineDelimiter) throws CoreException {
  		String enclosingType= ""; //$NON-NLS-1$
  		String enclosingMethod= ""; //$NON-NLS-1$
  			
  		if (locationInAST != null) {
  			FunctionDeclaration parentMethod= ASTResolving.findParentMethodDeclaration(locationInAST);
  			if (parentMethod != null) {
  				enclosingMethod= parentMethod.getName().getIdentifier();
  				locationInAST= parentMethod;
  			}
  			ASTNode parentType= ASTResolving.findParentType(locationInAST);
  			if (parentType instanceof AbstractTypeDeclaration) {
  				enclosingType= ((AbstractTypeDeclaration) parentType).getName().getIdentifier();
  			}
  		}
  		return getCatchBodyContent(cu, exceptionType, variableName, enclosingType, enclosingMethod, lineDelimiter);
  	}
  	
  	
  	public static String getCatchBodyContent(IJavaScriptUnit cu, String exceptionType, String variableName, String enclosingType, String enclosingMethod, String lineDelimiter) throws CoreException {
  		Template template= getCodeTemplate(CodeTemplateContextType.CATCHBLOCK_ID, cu.getJavaScriptProject());
  		if (template == null) {
  			return null;
  		}
  
  		CodeTemplateContext context= new CodeTemplateContext(template.getContextTypeId(), cu.getJavaScriptProject(), lineDelimiter);
  		context.setVariable(CodeTemplateContextType.ENCLOSING_TYPE, enclosingType);
  		context.setVariable(CodeTemplateContextType.ENCLOSING_METHOD, enclosingMethod); 
  		context.setVariable(CodeTemplateContextType.EXCEPTION_TYPE, exceptionType);
  		context.setVariable(CodeTemplateContextType.EXCEPTION_VAR, variableName); 
  		return evaluateTemplate(context, template);
  	}
  	
  	/*
  	 * Don't use this method directly, use CodeGeneration.
  	 * @see org.eclipse.wst.jsdt.ui.CodeGeneration#getCompilationUnitContent(IJavaScriptUnit, String, String, String, String)
  	 */	
  	public static String getCompilationUnitContent(IJavaScriptUnit cu, String fileComment, String typeComment, String typeContent, String lineDelimiter) throws CoreException {
  		IPackageFragment pack= (IPackageFragment) cu.getParent();
  		String packDecl= pack.isDefaultPackage() ? "" : "package " + pack.getElementName() + ';'; //$NON-NLS-1$ //$NON-NLS-2$
  		return getCompilationUnitContent(cu, packDecl, fileComment, typeComment, typeContent, lineDelimiter);
  	}
  	
  	public static String getCompilationUnitContent(IJavaScriptUnit cu, String packDecl, String fileComment, String typeComment, String typeContent, String lineDelimiter) throws CoreException {
  		Template template= getCodeTemplate(CodeTemplateContextType.NEWTYPE_ID, cu.getJavaScriptProject());
  		if (template == null) {
  			return null;
  		}
  		
  		IJavaScriptProject project= cu.getJavaScriptProject();
  		CodeTemplateContext context= new CodeTemplateContext(template.getContextTypeId(), project, lineDelimiter);
  		context.setCompilationUnitVariables(cu);
  		context.setVariable(CodeTemplateContextType.PACKAGE_DECLARATION, packDecl);
  		context.setVariable(CodeTemplateContextType.TYPE_COMMENT, typeComment != null ? typeComment : ""); //$NON-NLS-1$
  		context.setVariable(CodeTemplateContextType.FILE_COMMENT, fileComment != null ? fileComment : ""); //$NON-NLS-1$
  		context.setVariable(CodeTemplateContextType.TYPE_DECLARATION, typeContent);
  		context.setVariable(CodeTemplateContextType.TYPENAME, JavaScriptCore.removeJavaScriptLikeExtension(cu.getElementName()));
  		
  		String[] fullLine= { CodeTemplateContextType.PACKAGE_DECLARATION, CodeTemplateContextType.FILE_COMMENT, CodeTemplateContextType.TYPE_COMMENT };
  		return evaluateTemplate(context, template, fullLine);
  	}
  	
  	
  	/*
  	 * Don't use this method directly, use CodeGeneration.
  	 * @see org.eclipse.wst.jsdt.ui.CodeGeneration#getFileComment(IJavaScriptUnit, String)
  	 */	
  	public static String getFileComment(IJavaScriptUnit cu, String lineDelimiter) throws CoreException {
  		Template template= getCodeTemplate(CodeTemplateContextType.FILECOMMENT_ID, cu.getJavaScriptProject());
  		if (template == null) {
  			return null;
  		}
  		
  		IJavaScriptProject project= cu.getJavaScriptProject();
  		CodeTemplateContext context= new CodeTemplateContext(template.getContextTypeId(), project, lineDelimiter);
  		context.setCompilationUnitVariables(cu);
  		context.setVariable(CodeTemplateContextType.TYPENAME, JavaScriptCore.removeJavaScriptLikeExtension(cu.getElementName()));
  		return evaluateTemplate(context, template);
  	}	
  
  	/*
  	 * Don't use this method directly, use CodeGeneration.
  	 * @see org.eclipse.wst.jsdt.ui.CodeGeneration#getTypeComment(IJavaScriptUnit, String, String[], String)
  	 */		
  	public static String getTypeComment(IJavaScriptUnit cu, String typeQualifiedName, String lineDelim) throws CoreException {
  		Template template= getCodeTemplate(CodeTemplateContextType.TYPECOMMENT_ID, cu.getJavaScriptProject());
  		if (template == null) {
  			return null;
  		}
  		CodeTemplateContext context= new CodeTemplateContext(template.getContextTypeId(), cu.getJavaScriptProject(), lineDelim);
  		context.setCompilationUnitVariables(cu);
  		context.setVariable(CodeTemplateContextType.ENCLOSING_TYPE, Signature.getQualifier(typeQualifiedName));
  		context.setVariable(CodeTemplateContextType.TYPENAME, Signature.getSimpleName(typeQualifiedName));
  
  		TemplateBuffer buffer;
  		try {
  			buffer= context.evaluate(template);
  		} catch (BadLocationException e) {
  			throw new CoreException(Status.CANCEL_STATUS);
  		} catch (TemplateException e) {
  			throw new CoreException(Status.CANCEL_STATUS);
  		}
  		String str= buffer.getString();
  		if (Strings.containsOnlyWhitespaces(str)) {
  			return null;
  		}
  		
  		TemplateVariable position= findVariable(buffer, CodeTemplateContextType.TAGS); // look if Javadoc tags have to be added
  		if (position == null) {
  			return str;
  		}
  		
  		IDocument document= new Document(str);
  		int[] tagOffsets= position.getOffsets();
  		for (int i= tagOffsets.length - 1; i >= 0; i--) { // from last to first
  			try {
  				insertTag(document, tagOffsets[i], position.getLength(), EMPTY, EMPTY, null, false, lineDelim);
  			} catch (BadLocationException e) {
  				throw new CoreException(JavaUIStatus.createError(IStatus.ERROR, e));
  			}
  		}
  		return document.get();
  	}
  
  	/*
  	 * Returns the parameters type names used in see tags. Currently, these are always fully qualified.
  	 */
  	public static String[] getParameterTypeNamesForSeeTag(IFunctionBinding binding) {
  		ITypeBinding[] typeBindings= binding.getParameterTypes();
  		String[] result= new String[typeBindings.length];
  		for (int i= 0; i < result.length; i++) {
  			ITypeBinding curr= typeBindings[i];
  			curr= curr.getTypeDeclaration(); // no parameterized types
  			result[i]= curr.getQualifiedName();
  		}
  		return result;
  	}
  	
  	/*
  	 * Returns the parameters type names used in see tags. Currently, these are always fully qualified.
  	 */
  	private static String[] getParameterTypeNamesForSeeTag(IFunction overridden) {
  		try {
  			ASTParser parser= ASTParser.newParser(AST.JLS3);
  			parser.setProject(overridden.getJavaScriptProject());
  			IBinding[] bindings= parser.createBindings(new IJavaScriptElement[] { overridden }, null);
  			if (bindings.length == 1 && bindings[0] instanceof IFunctionBinding) {
  				return getParameterTypeNamesForSeeTag((IFunctionBinding) bindings[0]);
  			}
  		} catch (IllegalStateException e) {
  			// method does not exist
  		}
  		// fall back code. Not good for generic methods!
  		String[] paramTypes= overridden.getParameterTypes();
  		String[] paramTypeNames= new String[paramTypes.length];
  		for (int i= 0; i < paramTypes.length; i++) {
  			paramTypeNames[i]= Signature.toString(paramTypes[i]);
  		}
  		return paramTypeNames;
  	}
  
  	private static String getSeeTag(String declaringClassQualifiedName, String methodName, String[] parameterTypesQualifiedNames) {
  		StringBuffer buf= new StringBuffer();
  		buf.append("@see "); //$NON-NLS-1$
  		buf.append(declaringClassQualifiedName);
  		buf.append('#'); 
  		buf.append(methodName);
  		buf.append('(');
  		for (int i= 0; i < parameterTypesQualifiedNames.length; i++) {
  			if (i > 0) {
  				buf.append(", "); //$NON-NLS-1$
  			}
  			buf.append(parameterTypesQualifiedNames[i]);
  		}
  		buf.append(')');
  		return buf.toString();
  	}
  
  	/**
       * Don't use this method directly, use CodeGeneration.
  	 * @see org.eclipse.wst.jsdt.ui.CodeGeneration#getTypeBody(String, IJavaScriptUnit, String, String)
  	 */		
  	public static String getTypeBody(String templateID, IJavaScriptUnit cu, String typeName, String lineDelim) throws CoreException {
  		if ( !VALID_TYPE_BODY_TEMPLATES.contains(templateID)) {
  			throw new IllegalArgumentException("Invalid code template ID: " + templateID);  //$NON-NLS-1$
  		}
  		
  		Template template= getCodeTemplate(templateID, cu.getJavaScriptProject());
  		if (template == null) {
  			return null;
  		}
  		CodeTemplateContext context= new CodeTemplateContext(template.getContextTypeId(), cu.getJavaScriptProject(), lineDelim);
  		context.setCompilationUnitVariables(cu);
  		context.setVariable(CodeTemplateContextType.TYPENAME, typeName);
  
  		return evaluateTemplate(context, template);
  	}
  	
  	/*
  	 * Don't use this method directly, use CodeGeneration.
  	 * @see org.eclipse.wst.jsdt.ui.CodeGeneration#getMethodComment(IJavaScriptUnit, String, String, String[], String[], String, String[], IFunction, String)
  	 */
  	public static String getMethodComment(IJavaScriptUnit cu, String typeName, String methodName, String[] paramNames, String[] excTypeSig, String retTypeSig, IFunction target, boolean delegate, String lineDelimiter) throws CoreException {
  		String templateName= CodeTemplateContextType.METHODCOMMENT_ID;
  		if (retTypeSig == null) {
  			templateName= CodeTemplateContextType.CONSTRUCTORCOMMENT_ID;
  		} else if (target != null) {
  			if (delegate)
  				templateName= CodeTemplateContextType.DELEGATECOMMENT_ID;
  			else
  				templateName= CodeTemplateContextType.OVERRIDECOMMENT_ID;
  		}
  		Template template= getCodeTemplate(templateName, cu.getJavaScriptProject());
  		if (template == null) {
  			return null;
  		}		
  		CodeTemplateContext context= new CodeTemplateContext(template.getContextTypeId(), cu.getJavaScriptProject(), lineDelimiter);
  		context.setCompilationUnitVariables(cu);
  		context.setVariable(CodeTemplateContextType.ENCLOSING_TYPE, typeName);
  		context.setVariable(CodeTemplateContextType.ENCLOSING_METHOD, methodName);
  				
  		if (retTypeSig != null) {
  			context.setVariable(CodeTemplateContextType.RETURN_TYPE, Signature.toString(retTypeSig));
  		}
  		if (target != null && target.getDeclaringType()!=null) {
  			String targetTypeName= target.getDeclaringType().getFullyQualifiedName('.');
  			String[] targetParamTypeNames= getParameterTypeNamesForSeeTag(target);
  			if (delegate)
  				context.setVariable(CodeTemplateContextType.SEE_TO_TARGET_TAG, getSeeTag(targetTypeName, methodName, targetParamTypeNames));
  			else
  				context.setVariable(CodeTemplateContextType.SEE_TO_OVERRIDDEN_TAG, getSeeTag(targetTypeName, methodName, targetParamTypeNames));
  		}
  		TemplateBuffer buffer;
  		try {
  			buffer= context.evaluate(template);
  		} catch (BadLocationException e) {
  			throw new CoreException(Status.CANCEL_STATUS);
  		} catch (TemplateException e) {
  			throw new CoreException(Status.CANCEL_STATUS);
  		}
  		if (buffer == null) {
  			return null;
  		}
  		
  		String str= buffer.getString();
  		if (Strings.containsOnlyWhitespaces(str)) {
  			return null;
  		}
  		TemplateVariable position= findVariable(buffer, CodeTemplateContextType.TAGS); // look if Javadoc tags have to be added
  		if (position == null) {
  			return str;
  		}
  			
  		IDocument document= new Document(str);
  		String[] exceptionNames= new String[excTypeSig.length];
  		for (int i= 0; i < excTypeSig.length; i++) {
  			exceptionNames[i]= Signature.toString(excTypeSig[i]);
  		}
  		String returnType= retTypeSig != null ? Signature.toString(retTypeSig) : null;
  		int[] tagOffsets= position.getOffsets();
  		for (int i= tagOffsets.length - 1; i >= 0; i--) { // from last to first
  			try {
  				insertTag(document, tagOffsets[i], position.getLength(), paramNames, exceptionNames, returnType, false, lineDelimiter);
  			} catch (BadLocationException e) {
  				throw new CoreException(JavaUIStatus.createError(IStatus.ERROR, e));
  			}
  		}
  		return document.get();
  	}
  	
  	// remove lines for empty variables
  	private static String fixEmptyVariables(TemplateBuffer buffer, String[] variables) throws MalformedTreeException, BadLocationException {
  		IDocument doc= new Document(buffer.getString());
  		int nLines= doc.getNumberOfLines();
  		MultiTextEdit edit= new MultiTextEdit();
  		HashSet removedLines= new HashSet();
  		for (int i= 0; i < variables.length; i++) {
  			TemplateVariable position= findVariable(buffer, variables[i]); // look if Javadoc tags have to be added
  			if (position == null || position.getLength() > 0) {
  				continue;
  			}
  			int[] offsets= position.getOffsets();
  			for (int k= 0; k < offsets.length; k++) {
  				int line= doc.getLineOfOffset(offsets[k]);
  				IRegion lineInfo= doc.getLineInformation(line);
  				int offset= lineInfo.getOffset();
  				String str= doc.get(offset, lineInfo.getLength());
  				if (Strings.containsOnlyWhitespaces(str) && nLines > line + 1 && removedLines.add(Integer.valueOf(line))) {
  					int nextStart= doc.getLineOffset(line + 1);
  					edit.addChild(new DeleteEdit(offset, nextStart - offset));
  				}
  			}
  		}
  		edit.apply(doc, 0);
  		return doc.get();
  	}
  	
  	/*
  	 * Don't use this method directly, use CodeGeneration.
  	 */
  	public static String getFieldComment(IJavaScriptUnit cu, String typeName, String fieldName, String lineDelimiter) throws CoreException {
  		Template template= getCodeTemplate(CodeTemplateContextType.FIELDCOMMENT_ID, cu.getJavaScriptProject());
  		if (template == null) {
  			return null;
  		}
  		CodeTemplateContext context= new CodeTemplateContext(template.getContextTypeId(), cu.getJavaScriptProject(), lineDelimiter);
  		context.setCompilationUnitVariables(cu);
  		context.setVariable(CodeTemplateContextType.FIELD_TYPE, typeName);
  		context.setVariable(CodeTemplateContextType.FIELD, fieldName);
  		
  		return evaluateTemplate(context, template);
  	}	
  	
  	
  	/*
  	 * Don't use this method directly, use CodeGeneration.
  	 * @see org.eclipse.wst.jsdt.ui.CodeGeneration#getSetterComment(IJavaScriptUnit, String, String, String, String, String, String, String)
  	 */
  	public static String getSetterComment(IJavaScriptUnit cu, String typeName, String methodName, String fieldName, String fieldType, String paramName, String bareFieldName, String lineDelimiter) throws CoreException {
  		String templateName= CodeTemplateContextType.SETTERCOMMENT_ID;
  		Template template= getCodeTemplate(templateName, cu.getJavaScriptProject());
  		if (template == null) {
  			return null;
  		}
  		
  		CodeTemplateContext context= new CodeTemplateContext(template.getContextTypeId(), cu.getJavaScriptProject(), lineDelimiter);
  		context.setCompilationUnitVariables(cu);
  		context.setVariable(CodeTemplateContextType.ENCLOSING_TYPE, typeName);
  		context.setVariable(CodeTemplateContextType.ENCLOSING_METHOD, methodName);
  		context.setVariable(CodeTemplateContextType.FIELD, fieldName);
  		context.setVariable(CodeTemplateContextType.FIELD_TYPE, fieldType);
  		context.setVariable(CodeTemplateContextType.BARE_FIELD_NAME, bareFieldName);
  		context.setVariable(CodeTemplateContextType.PARAM, paramName);
  
  		return evaluateTemplate(context, template);
  	}	
  	
  	/*
  	 * Don't use this method directly, use CodeGeneration.
  	 * @see org.eclipse.wst.jsdt.ui.CodeGeneration#getGetterComment(IJavaScriptUnit, String, String, String, String, String, String)
  	 */
  	public static String getGetterComment(IJavaScriptUnit cu, String typeName, String methodName, String fieldName, String fieldType, String bareFieldName, String lineDelimiter) throws CoreException {
  		String templateName= CodeTemplateContextType.GETTERCOMMENT_ID;
  		Template template= getCodeTemplate(templateName, cu.getJavaScriptProject());
  		if (template == null) {
  			return null;
  		}		
  		CodeTemplateContext context= new CodeTemplateContext(template.getContextTypeId(), cu.getJavaScriptProject(), lineDelimiter);
  		context.setCompilationUnitVariables(cu);
  		context.setVariable(CodeTemplateContextType.ENCLOSING_TYPE, typeName);
  		context.setVariable(CodeTemplateContextType.ENCLOSING_METHOD, methodName);
  		context.setVariable(CodeTemplateContextType.FIELD, fieldName);
  		context.setVariable(CodeTemplateContextType.FIELD_TYPE, fieldType);
  		context.setVariable(CodeTemplateContextType.BARE_FIELD_NAME, bareFieldName);
  
  		return evaluateTemplate(context, template);
  	}
  	
  	private static String evaluateTemplate(CodeTemplateContext context, Template template) throws CoreException {
  		TemplateBuffer buffer;
  		try {
  			buffer= context.evaluate(template);
  		} catch (BadLocationException e) {
  			throw new CoreException(Status.CANCEL_STATUS);
  		} catch (TemplateException e) {
  			throw new CoreException(Status.CANCEL_STATUS);
  		}
  		if (buffer == null)
  			return null;
  		String str= buffer.getString();
  		if (Strings.containsOnlyWhitespaces(str)) {
  			return null;
  		}
  		return str;
  	}
  	
  	private static String evaluateTemplate(CodeTemplateContext context, Template template, String[] fullLineVariables) throws CoreException {
  		TemplateBuffer buffer;
  		try {
  			buffer= context.evaluate(template);
  			if (buffer == null)
  				return null;
  			String str= fixEmptyVariables(buffer, fullLineVariables);
  			if (Strings.containsOnlyWhitespaces(str)) {
  				return null;
  			}
  			return str;
  		} catch (BadLocationException e) {
  			throw new CoreException(Status.CANCEL_STATUS);
  		} catch (TemplateException e) {
  			throw new CoreException(Status.CANCEL_STATUS);
  		}
  	}
  
  	
  	/*
  	 * Don't use this method directly, use CodeGeneration.
  	 * @see org.eclipse.wst.jsdt.ui.CodeGeneration#getMethodComment(IJavaScriptUnit, String, FunctionDeclaration, boolean, String, String[], String)
  	 */
  	public static String getMethodComment(IJavaScriptUnit cu, String typeName, FunctionDeclaration decl, boolean isDeprecated, String targetName, String targetMethodDeclaringTypeName, String[] targetMethodParameterTypeNames, boolean delegate, String lineDelimiter) throws CoreException {
  		if (typeName==null)
  			typeName=""; //$NON-NLS-1$
  		boolean needsTarget= targetMethodDeclaringTypeName != null && targetMethodParameterTypeNames != null;
  		String templateName= CodeTemplateContextType.METHODCOMMENT_ID;
  		if (decl.isConstructor()) {
  			templateName= CodeTemplateContextType.CONSTRUCTORCOMMENT_ID;
  		} else if (needsTarget) {
  			if (delegate)
  				templateName= CodeTemplateContextType.DELEGATECOMMENT_ID;
  			else
  				templateName= CodeTemplateContextType.OVERRIDECOMMENT_ID;
  		}
  		Template template= getCodeTemplate(templateName, cu.getJavaScriptProject());
  		if (template == null) {
  			return null;
  		}		
  		CodeTemplateContext context= new CodeTemplateContext(template.getContextTypeId(), cu.getJavaScriptProject(), lineDelimiter);
  		context.setCompilationUnitVariables(cu);
  		context.setVariable(CodeTemplateContextType.ENCLOSING_TYPE, typeName);
  		context.setVariable(CodeTemplateContextType.ENCLOSING_METHOD, decl.getName().getIdentifier());
  		if (!decl.isConstructor() &&getReturnType(decl)!=null) {
  			context.setVariable(CodeTemplateContextType.RETURN_TYPE, ASTNodes.asString(getReturnType(decl)));
  		}
  		if (needsTarget) {
  			if (delegate)
  				context.setVariable(CodeTemplateContextType.SEE_TO_TARGET_TAG, getSeeTag(targetMethodDeclaringTypeName, targetName, targetMethodParameterTypeNames));
  			else
  				context.setVariable(CodeTemplateContextType.SEE_TO_OVERRIDDEN_TAG, getSeeTag(targetMethodDeclaringTypeName, targetName, targetMethodParameterTypeNames));
  		}
  		
  		TemplateBuffer buffer;
  		try {
  			buffer= context.evaluate(template);
  		} catch (BadLocationException e) {
  			throw new CoreException(Status.CANCEL_STATUS);
  		} catch (TemplateException e) {
  			throw new CoreException(Status.CANCEL_STATUS);
  		}
  		if (buffer == null)
  			return null;
  		String str= buffer.getString();
  		if (Strings.containsOnlyWhitespaces(str)) {
  			return null;
  		}
  		TemplateVariable position= findVariable(buffer, CodeTemplateContextType.TAGS);  // look if Javadoc tags have to be added
  		if (position == null) {
  			return str;
  		}
  			
  		IDocument textBuffer= new Document(str);
  		List params= decl.parameters();
  		String[] paramNames= new String[params.size()];
  		for (int i= 0; i < paramNames.length; i++) {
  			SingleVariableDeclaration elem= (SingleVariableDeclaration) params.get(i);
  			paramNames[i]= elem.getName().getIdentifier();
  		}
  		List exceptions= decl.thrownExceptions();
  		String[] exceptionNames= new String[exceptions.size()];
  		for (int i= 0; i < exceptionNames.length; i++) {
  			exceptionNames[i]= ASTNodes.getSimpleNameIdentifier((Name) exceptions.get(i));
  		}
  		
  		String returnType= null;
  		if (!decl.isConstructor()) {
  			returnType= ASTNodes.asString(getReturnType(decl));
  		}
  		int[] tagOffsets= position.getOffsets();
  		for (int i= tagOffsets.length - 1; i >= 0; i--) { // from last to first
  			try {
  				insertTag(textBuffer, tagOffsets[i], position.getLength(), paramNames, exceptionNames, returnType, isDeprecated, lineDelimiter);
  			} catch (BadLocationException e) {
  				throw new CoreException(JavaUIStatus.createError(IStatus.ERROR, e));
  			}
  		}
  		return textBuffer.get();
  	}
  	
  	/**
  	 * @deprecated Deprecated to avoid deprecated warnings
  	 */
  	private static ASTNode getReturnType(FunctionDeclaration decl) {
  		// used from API, can't eliminate
  		return (decl.getAST().apiLevel() == AST.JLS2) ? decl.getReturnType() : decl.getReturnType2();
  	}
  	
  	
  	private static TemplateVariable findVariable(TemplateBuffer buffer, String variable) {
  		TemplateVariable[] positions= buffer.getVariables();
  		for (int i= 0; i < positions.length; i++) {
  			TemplateVariable curr= positions[i];
  			if (variable.equals(curr.getType())) {
  				return curr;
  			}
  		}
  		return null;		
  	}	
  	
  	private static void insertTag(IDocument textBuffer, int offset, int length, String[] paramNames, String[] exceptionNames, String returnType, boolean isDeprecated, String lineDelimiter) throws BadLocationException {
  		IRegion region= textBuffer.getLineInformationOfOffset(offset);
  		if (region == null) {
  			return;
  		}
  		String lineStart= textBuffer.get(region.getOffset(), offset - region.getOffset());
  		
  		StringBuffer buf= new StringBuffer();
  		for (int i= 0; i < paramNames.length; i++) {
  			if (buf.length() > 0) {
  				buf.append(lineDelimiter).append(lineStart);
  			}
  			buf.append("@param ").append(paramNames[i]); //$NON-NLS-1$
  		}
  		if (returnType != null && !returnType.equals("void")) { //$NON-NLS-1$
  			if (buf.length() > 0) {
  				buf.append(lineDelimiter).append(lineStart);
  			}			
  			buf.append("@returns"); //$NON-NLS-1$
  			if(!returnType.equals("any")) { //$NON-NLS-1$
  				buf.append(" {" + returnType + "}"); //$NON-NLS-1$ //$NON-NLS-2$
  			}
  		}
  		if (exceptionNames != null) {
  			for (int i= 0; i < exceptionNames.length; i++) {
  				if (buf.length() > 0) {
  					buf.append(lineDelimiter).append(lineStart);
  				}
  				buf.append("@throws ").append(exceptionNames[i]); //$NON-NLS-1$
  			}
  		}		
  		if (isDeprecated) {
  			if (buf.length() > 0) {
  				buf.append(lineDelimiter).append(lineStart);
  			}
  			buf.append("@deprecated"); //$NON-NLS-1$
  		}
  		if (buf.length() == 0 && isAllCommentWhitespace(lineStart)) {
  			int prevLine= textBuffer.getLineOfOffset(offset) -1;
  			if (prevLine > 0) {
  				IRegion prevRegion= textBuffer.getLineInformation(prevLine);
  				int prevLineEnd= prevRegion.getOffset() + prevRegion.getLength();
  				// clear full line
  				textBuffer.replace(prevLineEnd, offset + length - prevLineEnd, ""); //$NON-NLS-1$
  				return;
  			}
  		}
  		textBuffer.replace(offset, length, buf.toString());
  	}
  	
  	private static boolean isAllCommentWhitespace(String lineStart) {
  		for (int i= 0; i < lineStart.length(); i++) {
  			char ch= lineStart.charAt(i);
  			if (!Character.isWhitespace(ch) && ch != '*') {
  				return false;
  			}
  		}
  		return true;
  	}
  		
  	/**
  	 * Returns the line delimiter which is used in the specified project.
  	 * 
  	 * @param project the java project, or <code>null</code>
  	 * @return the used line delimiter
  	 */
  	public static String getLineDelimiterUsed(IJavaScriptProject project) {
  		return getProjectLineDelimiter(project);
  	}
  
  	private static String getProjectLineDelimiter(IJavaScriptProject javaProject) {
  		IProject project= null;
  		if (javaProject != null)
  			project= javaProject.getProject();
  		
  		String lineDelimiter= getLineDelimiterPreference(project);
  		if (lineDelimiter != null)
  			return lineDelimiter;
  		
  		return System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  	}
  	
  	public static String getLineDelimiterPreference(IProject project) {
  		IScopeContext[] scopeContext;
  		if (project != null) {
  			// project preference
  			scopeContext= new IScopeContext[] { new ProjectScope(project) };
  			String lineDelimiter= Platform.getPreferencesService().getString(Platform.PI_RUNTIME, Platform.PREF_LINE_SEPARATOR, null, scopeContext);
  			if (lineDelimiter != null)
  				return lineDelimiter;
  		}
  		// workspace preference
  		scopeContext= new IScopeContext[] { new InstanceScope() };
  		String platformDefault= System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
  		return Platform.getPreferencesService().getString(Platform.PI_RUNTIME, Platform.PREF_LINE_SEPARATOR, platformDefault, scopeContext);
  	}
  	
  	/**
  	 * Examines a string and returns the first line delimiter found.
  	 */
  	public static String getLineDelimiterUsed(IJavaScriptElement elem) {
  		while (elem != null && !(elem instanceof IOpenable)) {
  			elem= elem.getParent();
  		}
  		if (elem != null) {
  			try {
  				return ((IOpenable) elem).findRecommendedLineSeparator();
  			} catch (JavaScriptModelException exception) {
  				// Use project setting
  			}
  		}
  		return getProjectLineDelimiter(null);
  	}
  
  	/**
  	 * Evaluates the indentation used by a Java element. (in tabulators)
  	 */	
  	public static int getIndentUsed(IJavaScriptElement elem) throws JavaScriptModelException {
  		if (elem instanceof ISourceReference) {
  			IJavaScriptUnit cu= (IJavaScriptUnit) elem.getAncestor(IJavaScriptElement.JAVASCRIPT_UNIT);
  			if (cu != null) {
  				IBuffer buf= cu.getBuffer();
  				int offset= ((ISourceReference)elem).getSourceRange().getOffset();
  				int i= offset;
  				// find beginning of line
  				while (i > 0 && !IndentManipulation.isLineDelimiterChar(buf.getChar(i - 1)) ){
  					i--;
  				}
  				return Strings.computeIndentUnits(buf.getText(i, offset - i), elem.getJavaScriptProject());
  			}
  		}
  		return 0;
  	}
  		
  	/**
  	 * Returns the element after the give element.
  	 */
  	public static IJavaScriptElement findNextSibling(IJavaScriptElement member) throws JavaScriptModelException {
  		IJavaScriptElement parent= member.getParent();
  		if (parent instanceof IParent) {
  			IJavaScriptElement[] 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(IJavaScriptProject project) {
  		String markers= null;
  		if (project == null) {
  			markers= JavaScriptCore.getOption(JavaScriptCore.COMPILER_TASK_TAGS);
  		} else {
  			markers= project.getOption(JavaScriptCore.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;
  	}
  	
  	private static String removeTypeArguments(String baseName) {
  		int idx= baseName.indexOf('<');
  		if (idx != -1) {
  			return baseName.substring(0, idx);
  		}
  		return baseName;
  	}
  	
  	
  	// --------------------------- name suggestions --------------------------
  	
  	public static final int STATIC_FIELD= 1;
  	public static final int INSTANCE_FIELD= 2;
  	public static final int CONSTANT_FIELD= 3;
  	public static final int PARAMETER= 4;
  	public static final int LOCAL= 5;
  	
  	public static String[] getVariableNameSuggestions(int variableKind, IJavaScriptProject project, ITypeBinding expectedType, Expression assignedExpression, Collection excluded) {
  		LinkedHashSet res= new LinkedHashSet(); // avoid duplicates but keep order
  
  		if (assignedExpression != null) {
  			String nameFromExpression= getBaseNameFromExpression(project, assignedExpression, variableKind);
  			if (nameFromExpression != null) {
  				add(getVariableNameSuggestions(variableKind, project, nameFromExpression, 0, excluded, false), res); // pass 0 as dimension, base name already contains plural.
  			}
  		}
  		if (expectedType != null) {
  			expectedType= Bindings.normalizeTypeBinding(expectedType);
  			if (expectedType != null) {
  				int dim= 0;
  				if (expectedType.isArray()) {
  					dim= expectedType.getDimensions();
  					expectedType= expectedType.getElementType();
  				}
  				String typeName= expectedType.getQualifiedName();
  				if (typeName.length() > 0) {
  					String[] names= getVariableNameSuggestions(variableKind, project, typeName, dim, excluded, false);
  					for (int i= 0; i < names.length; i++) {
  						res.add(names[i]);
  					}
  				}
  			}
  		}
  		if (assignedExpression != null) {
  			// add at end, less important
  			String nameFromParent= getBaseNameFromLocationInParent(project, assignedExpression);
  			if (nameFromParent != null) {
  				add(getVariableNameSuggestions(variableKind, project, nameFromParent, 0, excluded, false), res); // pass 0 as dimension, base name already contains plural.
  			}
  		}
  		if (res.isEmpty()) {
  			return getDefaultVariableNameSuggestions(variableKind, excluded);
  		}
  		return (String[]) res.toArray(new String[res.size()]);
  	}
  	
  	public static String[] getVariableNameSuggestions(int variableKind, IJavaScriptProject project, Type expectedType, Expression assignedExpression, Collection excluded) {
  		LinkedHashSet res= new LinkedHashSet(); // avoid duplicates but keep order
  
  		if (assignedExpression != null) {
  			String nameFromExpression= getBaseNameFromExpression(project, assignedExpression, variableKind);
  			if (nameFromExpression != null) {
  				add(getVariableNameSuggestions(variableKind, project, nameFromExpression, 0, excluded, false), res); // pass 0 as dimension, base name already contains plural.
  			}
  		}
  		if (expectedType != null) {			
  			int dim= 0;
  			if (expectedType.isArrayType()) {
  				ArrayType arrayType= (ArrayType) expectedType;
  				dim= arrayType.getDimensions();
  				expectedType= arrayType.getElementType();
  			}
  
  			String typeName= ASTNodes.asString(expectedType);
  			
  			if (typeName.length() > 0) {
  				String[] names= getVariableNameSuggestions(variableKind, project, typeName, dim, excluded, false);
  				for (int i= 0; i < names.length; i++) {
  					res.add(names[i]);
  				}
  			}
  		}
  		if (assignedExpression != null) {
  			// add at end, less important
  			String nameFromParent= getBaseNameFromLocationInParent(project, assignedExpression);
  			if (nameFromParent != null) {
  				add(getVariableNameSuggestions(variableKind, project, nameFromParent, 0, excluded, false), res); // pass 0 as dimension, base name already contains plural.
  			}
  		}
  		if (res.isEmpty()) {
  			return getDefaultVariableNameSuggestions(variableKind, excluded);
  		}
  		return (String[]) res.toArray(new String[res.size()]);
  	}
  	
  	private static String[] getDefaultVariableNameSuggestions(int variableKind, Collection excluded) {
  		String prop= variableKind == CONSTANT_FIELD ? "X" : "x";  //$NON-NLS-1$//$NON-NLS-2$
  		String name= prop;
  		int i= 1;
  		while (excluded.contains(name)) {
  			name= prop + i++;
  		}
  		return new String[] { name };
  	}
  	
  	/**
  	 * Returns variable name suggestions for the given base name. This is a layer over the JDT.Core NamingConventions API to fix its shortcomings. JDT UI code should only use this
  	 * API.
  	 * @param variableKind Specifies what type the variable is: {@link #LOCAL}, {@link #PARAMETER}, {@link #STATIC_FIELD}, {@link #INSTANCE_FIELD} or {@link #CONSTANT_FIELD}.
   	 * @param project the current project
   	 * @param baseName the base name to make a suggestion on. the base name is expected to be a name without any pre- or suffixes in singular form. Type name are accepted as well.
   	 * @param dimensions if greater than 0, the resulting name will be in plural form
   	 * @param excluded a collection containing all excluded names or <code>null</code> if no names are excluded
   	 * @param evaluateDefault if set, the result is guaranteed to contain at least one result. If not, the result can be an empty array. 
  	 * 
  	 * @return returns the name suggestions sorted by relevance (best proposal first). If <code>evaluateDefault</code> is set to true, the returned array is never empty.
  	 * If <code>evaluateDefault</code> is set to false, an empty array is returned if there is no good suggestion for the given base name.
  	 */
  	public static String[] getVariableNameSuggestions(int variableKind, IJavaScriptProject project, String baseName, int dimensions, Collection excluded, boolean evaluateDefault) {
  		String name= workaround38111(baseName);
  		name= removeTypeArguments(name);
  		String packageName= new String(); // not used, so don't compute for now
  		String[] result= null;
  		
  		switch (variableKind) {
  			case CONSTANT_FIELD:
  				result= getConstantSuggestions(project, packageName, name, dimensions, excluded);
  				break;
  			case STATIC_FIELD:
  				result= sortByLength(NamingConventions.suggestFieldNames(project, packageName, name, dimensions, Flags.AccStatic, getExcludedArray(excluded)));
  				break;
  			case INSTANCE_FIELD:
  				result= sortByLength(NamingConventions.suggestFieldNames(project, packageName, name, dimensions, 0, getExcludedArray(excluded)));
  				break;
  			case PARAMETER:
  				result= sortByLength(NamingConventions.suggestArgumentNames(project, packageName, name, dimensions, getExcludedArray(excluded)));
  				break;
  			case LOCAL:
  				result= sortByLength(NamingConventions.suggestLocalVariableNames(project, packageName, name, dimensions, getExcludedArray(excluded)));
  				break;
  		}
  		if (evaluateDefault) {
  			if (result.length == 0) {
  				result= getDefaultVariableNameSuggestions(variableKind, excluded);
  			}
  		} else if (variableKind != CONSTANT_FIELD) {
  			 // see 166464 API DCR: specify if naming convention should return default value or not
  			String defaultValue= "NAME"; // default as chosen by jdt.core //$NON-NLS-1$
  			if (!name.toUpperCase().endsWith(defaultValue) && result[0].toUpperCase().endsWith(defaultValue)) {
  				return new String[0];
  			}
  		}
  		return result;
  	}
  	
  	private static String[] getExcludedArray(Collection excluded) {
  		if (excluded == null) {
  			return null;
  		} else if (excluded instanceof ExcludedCollection) {
  			return ((ExcludedCollection) excluded).getExcludedArray();
  		}
  		return (String[]) excluded.toArray(new String[excluded.size()]);
  	}
  	
  	
  	private static final String[] KNOWN_METHOD_NAME_PREFIXES= { "get", "is", "to"}; //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-1$
  	
  	
  	private static void add(String[] names, Set result) {
  		for (int i= 0; i < names.length; i++) {
  			result.add(names[i]);
  		}
  	}
  	
  	private static String getBaseNameFromExpression(IJavaScriptProject project, Expression assignedExpression, int variableKind) {
  		String name= null;
  
  		if (assignedExpression instanceof Name) {
  			Name simpleNode= (Name) assignedExpression;
  			IBinding binding= simpleNode.resolveBinding();
  			if (binding instanceof IVariableBinding)
  				return removePrefixAndSuffixForVariable(project, (IVariableBinding) binding);
  			
  			return ASTNodes.getSimpleNameIdentifier(simpleNode);
  		} else if (assignedExpression instanceof FunctionInvocation) {
  			SimpleName name2 = ((FunctionInvocation) assignedExpression).getName();
  			if (name2!=null)
  				name= name2.getIdentifier();
  		} else if (assignedExpression instanceof SuperMethodInvocation) {
  			name= ((SuperMethodInvocation) assignedExpression).getName().getIdentifier();
  		} else if (assignedExpression instanceof FieldAccess) {
  			return ((FieldAccess) assignedExpression).getName().getIdentifier();
  		} else if (variableKind == CONSTANT_FIELD && (assignedExpression instanceof StringLiteral || assignedExpression instanceof NumberLiteral)) {
  			String string= assignedExpression instanceof StringLiteral ? ((StringLiteral) assignedExpression).getLiteralValue() : ((NumberLiteral) assignedExpression).getToken();
  			StringBuffer res= new StringBuffer();
  			boolean needsUnderscore= false;
  			for (int i= 0; i < string.length(); i++) {
  				char ch= string.charAt(i);
  				if (Character.isJavaIdentifierPart(ch)) {
  					if (res.length() == 0 && !Character.isJavaIdentifierStart(ch) || needsUnderscore) {
  						res.append('_');
  					}
  					res.append(ch);
  					needsUnderscore= false;
  				} else {
  					needsUnderscore= res.length() > 0;
  				}
  			}
  			if (res.length() > 0) {
  				return res.toString();
  			}
  		}
  		if (name != null) {
  			for (int i= 0; i < KNOWN_METHOD_NAME_PREFIXES.length; i++) {
  				String curr= KNOWN_METHOD_NAME_PREFIXES[i];
  				if (name.startsWith(curr)) {
  					if (name.equals(curr)) {
  						return null; // don't suggest 'get' as variable name
  					} else if (Character.isUpperCase(name.charAt(curr.length()))) {
  						return name.substring(curr.length());
  					}
  				}
  			}
  		}
  		return name;
  	}
  	
  	private static String getBaseNameFromLocationInParent(IJavaScriptProject project, Expression assignedExpression) {
  		StructuralPropertyDescriptor location= assignedExpression.getLocationInParent();
  		if (location == FunctionInvocation.ARGUMENTS_PROPERTY) {
  			FunctionInvocation parent= (FunctionInvocation) assignedExpression.getParent();
  			IFunctionBinding binding= parent.resolveMethodBinding();
  			int index= parent.arguments().indexOf(assignedExpression);
  			if (binding != null && index != -1) {
  				return getParameterName(binding, index);
  			}
  		} else if (location == ClassInstanceCreation.ARGUMENTS_PROPERTY) {
  			ClassInstanceCreation parent= (ClassInstanceCreation) assignedExpression.getParent();
  			IFunctionBinding binding= parent.resolveConstructorBinding();
  			int index= parent.arguments().indexOf(assignedExpression);
  			if (binding != null && index != -1) {
  				return getParameterName(binding, index);
  			}
  		} else if (location == SuperMethodInvocation.ARGUMENTS_PROPERTY) {
  			SuperMethodInvocation parent= (SuperMethodInvocation) assignedExpression.getParent();
  			IFunctionBinding binding= parent.resolveMethodBinding();
  			int index= parent.arguments().indexOf(assignedExpression);
  			if (binding != null && index != -1) {
  				return getParameterName(binding, index);
  			}
  		} else if (location == ConstructorInvocation.ARGUMENTS_PROPERTY) {
  			ConstructorInvocation parent= (ConstructorInvocation) assignedExpression.getParent();
  			IFunctionBinding binding= parent.resolveConstructorBinding();
  			int index= parent.arguments().indexOf(assignedExpression);
  			if (binding != null && index != -1) {
  				return getParameterName(binding, index);
  			}
  		} else if (location == SuperConstructorInvocation.ARGUMENTS_PROPERTY) {
  			SuperConstructorInvocation parent= (SuperConstructorInvocation) assignedExpression.getParent();
  			IFunctionBinding binding= parent.resolveConstructorBinding();
  			int index= parent.arguments().indexOf(assignedExpression);
  			if (binding != null && index != -1) {
  				return getParameterName(binding, index);
  			}
  		}
  		return null;
  	}
  	
  	private static String getParameterName(IFunctionBinding binding, int index) {
  		try {
  			IJavaScriptElement javaElement= binding.getJavaElement();
  			if (javaElement instanceof IFunction) {
  				IFunction method= (IFunction) javaElement;
  				if (method.getOpenable().getBuffer() != null) { // avoid dummy names and lookup from Javadoc
  					String[] parameterNames= method.getParameterNames();
  					if (index < parameterNames.length) {
  						return NamingConventions.removePrefixAndSuffixForArgumentName(method.getJavaScriptProject(), parameterNames[index]);
  					}
  				}
  			}
  		} catch (JavaScriptModelException e) {
  			// ignore
  		}
  		return null;
  	}
  	
  	public static String[] getArgumentNameSuggestions(IType type,IJavaScriptUnit compUnit, String[] excluded) {
  		String baseName= (type!=null)?JavaModelUtil.getFullyQualifiedName(type) : compUnit.getElementName();
  		return getVariableNameSuggestions(PARAMETER, compUnit.getJavaScriptProject(),baseName, 0, new ExcludedCollection(excluded), true);
  	}
  	
  	public static String[] getArgumentNameSuggestions(IType type, String[] excluded) {
  		return getVariableNameSuggestions(PARAMETER, type.getJavaScriptProject(), JavaModelUtil.getFullyQualifiedName(type), 0, new ExcludedCollection(excluded), true);
  	}
  	
  	public static String[] getArgumentNameSuggestions(IJavaScriptProject project, Type type, String[] excluded) {
  		int dim= 0;
  		if (type.isArrayType()) {
  			ArrayType arrayType= (ArrayType) type;
  			dim= arrayType.getDimensions();
  			type= arrayType.getElementType();
  		}
  
  		return getVariableNameSuggestions(PARAMETER, project, ASTNodes.asString(type), dim, new ExcludedCollection(excluded), true);
  	}
  	
  	public static String[] getArgumentNameSuggestions(IJavaScriptProject project, ITypeBinding binding, String[] excluded) {
  		return getVariableNameSuggestions(PARAMETER, project, binding, null, new ExcludedCollection(excluded));
  	}
  		
  	public static String[] getArgumentNameSuggestions(IJavaScriptProject project, String baseName, int dimensions, String[] excluded) {
  		return getVariableNameSuggestions(PARAMETER, project, baseName, dimensions, new ExcludedCollection(excluded), true);
  	}
  	
  	public static String[] getFieldNameSuggestions(IType type, int fieldModifiers, String[] excluded) {		
  		return getFieldNameSuggestions(type.getJavaScriptProject(), JavaModelUtil.getFullyQualifiedName(type), 0, fieldModifiers, excluded);
  	}
  		 
  	public static String[] getFieldNameSuggestions(IJavaScriptProject project, String baseName, int dimensions, int modifiers, String[] excluded) {
  		if (Flags.isStatic(modifiers)) {
  			return getVariableNameSuggestions(STATIC_FIELD, project, baseName, dimensions, new ExcludedCollection(excluded), true);
  		}
  		return getVariableNameSuggestions(INSTANCE_FIELD, project, baseName, dimensions, new ExcludedCollection(excluded), true);
  	}
  
  	private static String[] getConstantSuggestions(IJavaScriptProject project, String packageName, String typeName, int dimensions, Collection excluded) {
  		//TODO: workaround JDT/Core bug 85946
  		
  		String string= Signature.getSimpleName(typeName);
  
  		StringBuffer buf= new StringBuffer();
  		boolean wasUpperCase= true;
  		for (int i= 0; i < string.length() ; i++) {
  			char ch= string.charAt(i);
  			if (Character.isUpperCase(ch)) {
  				if (!wasUpperCase) {
  					buf.append('_');
  				}
  				buf.append(ch);
  			} else {
  				buf.append(Character.toUpperCase(ch));
  				wasUpperCase= ch == '_'; // avoid duplicate underscores
  			}
  		}
  		ArrayList res= new ArrayList();
  		String sourceLevel= project.getOption(JavaScriptCore.COMPILER_SOURCE, true);
  		String complianceLevel= project.getOption(JavaScriptCore.COMPILER_COMPLIANCE, true);
  		
  		boolean nameStarts= true;
  		for (int i= 0; i < buf.length(); i++) {
  			if (nameStarts) {
  				String prop= buf.substring(i);
  				if (!excluded.contains(prop) && JavaScriptConventions.validateFieldName(prop, sourceLevel, complianceLevel).isOK()) {
  					res.add(prop);
  				}
  			}
  			char ch= buf.charAt(i);
  			nameStarts= ch == '_';
  		}
  		return (String[]) res.toArray(new String[res.size()]);
  	}
  	
  	private static String getCamelCaseFromUpper(String string) {
  		StringBuffer result= new StringBuffer();
  		boolean lastWasUnderscore= false;
  		for (int i= 0; i < string.length(); i++) {
  			char ch= string.charAt(i);
  			if (Character.isUpperCase(ch)) {
  				if (!lastWasUnderscore) {
  					ch= Character.toLowerCase(ch);
  				}
  				result.append(ch);
  				lastWasUnderscore= false;
  			} else if (ch == '_') {
  				lastWasUnderscore= true;
  			} else {
  				return string; // abort
  			}
  		}
  		return result.toString();
  	}
  	
  	public static String[] getLocalNameSuggestions(IJavaScriptProject project, String baseName, int dimensions, String[] excluded) {
  		return getVariableNameSuggestions(LOCAL, project, baseName, dimensions, new ExcludedCollection(excluded), true);
  	}
  	
  	private static String[] sortByLength(String[] proposals) {
  		Arrays.sort(proposals, new Comparator() {
  			public int compare(Object o1, Object o2) {
  				return ((String) o2).length() - ((String) o1).length();
  			}
  		});
  		return proposals;
  	}
  	
  	private static String workaround38111(String baseName) {
  		if (BASE_TYPES.contains(baseName))
  			return baseName;
  		return Character.toUpperCase(baseName.charAt(0)) + baseName.substring(1);
  	}
  	
  	private static final List BASE_TYPES= Arrays.asList(
  			new String[] {"boolean", "byte", "char", "double", "float", "int", "long", "short"});  //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$
  
  	public static String suggestArgumentName(IJavaScriptProject project, String baseName, String[] excluded) {
  		return suggestVariableName(PARAMETER, project, baseName, 0, excluded);
  	}
  	
  	private static String suggestVariableName(int varKind, IJavaScriptProject project, String baseName, int dimension, String[] excluded) {
  		return getVariableNameSuggestions(varKind, project, baseName, dimension, new ExcludedCollection(excluded), true)[0];
  	}
  	
  	
  	public static String[][] suggestArgumentNamesWithProposals(IJavaScriptProject project, String[] paramNames) {
  		String[][] newNames= new String[paramNames.length][];
  		ArrayList takenNames= new ArrayList();
  		
  		// Ensure that the code generation preferences are respected
  		for (int i= 0; i < paramNames.length; i++) {
  			String curr= paramNames[i];
  			String baseName= NamingConventions.removePrefixAndSuffixForArgumentName(project, curr);
  
  			String[] proposedNames= getVariableNameSuggestions(PARAMETER, project, curr, 0, takenNames, true);
  			if (!curr.equals(baseName)) {
  				// make the existing name to favourite
  				LinkedHashSet updatedNames= new LinkedHashSet();
  				updatedNames.add(curr);
  				for (int k= 0; k < proposedNames.length; k++) {
  					updatedNames.add(proposedNames[k]);
  				}
  				proposedNames= (String[]) updatedNames.toArray(new String[updatedNames.size()]);
  			}
  			newNames[i]= proposedNames;
  			takenNames.add(proposedNames[0]);
  		}
  		return newNames;
  	}
  	
  	public static String[][] suggestArgumentNamesWithProposals(IJavaScriptProject project, IFunctionBinding binding) {
  		int nParams= binding.getParameterTypes().length;
  		if (nParams > 0) {
  			try {
  				IFunction method= (IFunction) binding.getMethodDeclaration().getJavaElement();
  				if (method != null) {
  					return suggestArgumentNamesWithProposals(project, method.getParameterNames());
  				}
  			} catch (JavaScriptModelException e) {
  				// ignore
  			}
  		}
  		String[][] names= new String[nParams][];
  		for (int i= 0; i < names.length; i++) {
  			names[i]= new String[] { "arg" + i }; //$NON-NLS-1$
  		}
  		return names;
  	}
  	
  	
  	public static String[] suggestArgumentNames(IJavaScriptProject project, IFunctionBinding binding) {
  		int nParams= binding.getParameterTypes().length;
  
  		if (nParams > 0) {
  			try {
  				IFunction method= (IFunction) binding.getMethodDeclaration().getJavaElement();
  				if (method != null) {
  					String[] paramNames= method.getParameterNames();
  					String[] namesArray= new String[0];
  					ArrayList newNames= new ArrayList(paramNames.length);
  					// Ensure that the code generation preferences are respected
  					for (int i= 0; i < paramNames.length; i++) {
  						String curr= paramNames[i];
  						String baseName= NamingConventions.removePrefixAndSuffixForArgumentName(project, curr);
  						if (!curr.equals(baseName)) {
  							// make the existing name the favourite
  							newNames.add(curr);
  						} else {
  							newNames.add(suggestArgumentName(project, curr, namesArray));
  						}
  						namesArray= (String[]) newNames.toArray(new String[newNames.size()]);
  					}
  					return namesArray;
  				}
  			} catch (JavaScriptModelException e) {
  				// ignore
  			}
  		}
  		String[] names= new String[nParams];
  		for (int i= 0; i < names.length; i++) {
  			names[i]= "arg" + i; //$NON-NLS-1$
  		}
  		return names;
  	}
  	
  	public static String removePrefixAndSuffixForVariable(IJavaScriptProject project, IVariableBinding binding) {
  		if (binding.isField()) {
  			if (Modifier.isStatic(binding.getModifiers()) && Modifier.isFinal(binding.getModifiers())) {
  				return getCamelCaseFromUpper(binding.getName());
  			} else {
  				return NamingConventions.removePrefixAndSuffixForFieldName(project, binding.getName(), binding.getModifiers());
  			}
  		} else if (binding.isParameter()) {
  			return NamingConventions.removePrefixAndSuffixForArgumentName(project, binding.getName());
  		} else {
  			return NamingConventions.removePrefixAndSuffixForLocalVariableName(project, binding.getName());
  		}
  	}
  	
      private static class ExcludedCollection extends AbstractList {
  		private String[] fExcluded;
  		public ExcludedCollection(String[] excluded) {
  			fExcluded = excluded;
  		}
  		public String[] getExcludedArray() {
  			return fExcluded;
  		}
  		public int size() {
  			return fExcluded.length;
  		}
  		public Object get(int index) {
  			return fExcluded[index];
  		}
  		public int indexOf(Object o) {
  			if (o instanceof String) {
  				for (int i= 0; i < fExcluded.length; i++) {
  			         if (o.equals(fExcluded[i]))
  			             return i;
  				}
  			}
  			return -1;
  		}
  		public boolean contains(Object o) {
  			return indexOf(o) != -1;
  		}
      }
  	
  	
  	public static boolean hasFieldName(IJavaScriptProject project, String name) {
  		String prefixes= project.getOption(JavaScriptCore.CODEASSIST_FIELD_PREFIXES, true);
  		String suffixes= project.getOption(JavaScriptCore.CODEASSIST_FIELD_SUFFIXES, true);
  		String staticPrefixes= project.getOption(JavaScriptCore.CODEASSIST_STATIC_FIELD_PREFIXES, true);
  		String staticSuffixes= project.getOption(JavaScriptCore.CODEASSIST_STATIC_FIELD_SUFFIXES, true);
  		
  		
  		return hasPrefixOrSuffix(prefixes, suffixes, name) 
  			|| hasPrefixOrSuffix(staticPrefixes, staticSuffixes, name);
  	}
  	
  	public static boolean hasParameterName(IJavaScriptProject project, String name) {
  		String prefixes= project.getOption(JavaScriptCore.CODEASSIST_ARGUMENT_PREFIXES, true);
  		String suffixes= project.getOption(JavaScriptCore.CODEASSIST_ARGUMENT_SUFFIXES, true);
  		return hasPrefixOrSuffix(prefixes, suffixes, name);
  	}
  	
  	public static boolean hasLocalVariableName(IJavaScriptProject project, String name) {
  		String prefixes= project.getOption(JavaScriptCore.CODEASSIST_LOCAL_PREFIXES, true);
  		String suffixes= project.getOption(JavaScriptCore.CODEASSIST_LOCAL_SUFFIXES, true);
  		return hasPrefixOrSuffix(prefixes, suffixes, name);
  	}
  	
  	public static boolean hasConstantName(String name) {
  		return Character.isUpperCase(name.charAt(0));
  	}
  	
  	
  	private static boolean hasPrefixOrSuffix(String prefixes, String suffixes, String name) {
  		final String listSeparartor= ","; //$NON-NLS-1$
  
  		StringTokenizer tok= new StringTokenizer(prefixes, listSeparartor);
  		while (tok.hasMoreTokens()) {
  			String curr= tok.nextToken();
  			if (name.startsWith(curr)) {
  				return true;
  			}
  		}
  
  		tok= new StringTokenizer(suffixes, listSeparartor);
  		while (tok.hasMoreTokens()) {
  			String curr= tok.nextToken();
  			if (name.endsWith(curr)) {
  				return true;
  			}
  		}
  		return false;
  	}
  	
  	// -------------------- preference access -----------------------
  	
  	public static boolean useThisForFieldAccess(IJavaScriptProject project) {
  		return Boolean.valueOf(PreferenceConstants.getPreference(PreferenceConstants.CODEGEN_KEYWORD_THIS, project)).booleanValue(); 
  	}
  	
  	public static boolean useIsForBooleanGetters(IJavaScriptProject project) {
  		return Boolean.valueOf(PreferenceConstants.getPreference(PreferenceConstants.CODEGEN_IS_FOR_GETTERS, project)).booleanValue(); 
  	}
  	
  	public static String getExceptionVariableName(IJavaScriptProject project) {
  		return PreferenceConstants.getPreference(PreferenceConstants.CODEGEN_EXCEPTION_VAR_NAME, project); 
  	}
  	
  	public static boolean doAddComments(IJavaScriptProject project) {
  		return Boolean.valueOf(PreferenceConstants.getPreference(PreferenceConstants.CODEGEN_ADD_COMMENTS, project)).booleanValue(); 
  	}
  	
  	public static void setCodeTemplate(String templateId, String pattern, IJavaScriptProject project) {
  		TemplateStore codeTemplateStore= JavaScriptPlugin.getDefault().getCodeTemplateStore();
  		TemplatePersistenceData data= codeTemplateStore.getTemplateData(templateId);
  		Template orig= data.getTemplate();
  		Template copy= new Template(orig.getName(), orig.getDescription(), orig.getContextTypeId(), pattern, true);
  		data.setTemplate(copy);
  	}
  	
  	private static Template getCodeTemplate(String id, IJavaScriptProject project) {
  		if (project == null)
  			return JavaScriptPlugin.getDefault().getCodeTemplateStore().findTemplateById(id);
  		ProjectTemplateStore projectStore= new ProjectTemplateStore(project.getProject());
  		try {
  			projectStore.load();
  		} catch (IOException e) {
  			JavaScriptPlugin.log(e);
  		}
  		return projectStore.findTemplateById(id);
  	}
  	
  
  	public static ImportRewrite createImportRewrite(IJavaScriptUnit cu, boolean restoreExistingImports) throws JavaScriptModelException {
  		return CodeStyleConfiguration.createImportRewrite(cu, restoreExistingImports);
  	}
  	
  	public static ImportRewrite createImportRewrite(JavaScriptUnit astRoot, boolean restoreExistingImports) {
  		return CodeStyleConfiguration.createImportRewrite(astRoot, restoreExistingImports);
  	}
  	
  }
  