content: /*******************************************************************************
   * Copyright (c) 2000, 2009 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
   *     Philippe Ombredanne - bug 149382
   *******************************************************************************/
  package org.eclipse.wst.jsdt.ui.actions;
  
  import java.lang.reflect.InvocationTargetException;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.HashSet;
  import java.util.Iterator;
  import java.util.LinkedHashMap;
  import java.util.LinkedHashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  import org.eclipse.core.runtime.CoreException;
  import org.eclipse.core.runtime.IStatus;
  import org.eclipse.jface.dialogs.IDialogConstants;
  import org.eclipse.jface.dialogs.IDialogSettings;
  import org.eclipse.jface.dialogs.MessageDialog;
  import org.eclipse.jface.operation.IRunnableContext;
  import org.eclipse.jface.resource.ImageDescriptor;
  import org.eclipse.jface.text.IRewriteTarget;
  import org.eclipse.jface.text.ITextSelection;
  import org.eclipse.jface.viewers.CheckboxTreeViewer;
  import org.eclipse.jface.viewers.ILabelProvider;
  import org.eclipse.jface.viewers.IStructuredSelection;
  import org.eclipse.jface.viewers.ITreeContentProvider;
  import org.eclipse.jface.viewers.Viewer;
  import org.eclipse.jface.viewers.ViewerFilter;
  import org.eclipse.jface.window.Window;
  import org.eclipse.swt.SWT;
  import org.eclipse.swt.events.SelectionAdapter;
  import org.eclipse.swt.events.SelectionEvent;
  import org.eclipse.swt.events.SelectionListener;
  import org.eclipse.swt.graphics.Image;
  import org.eclipse.swt.layout.GridData;
  import org.eclipse.swt.layout.GridLayout;
  import org.eclipse.swt.widgets.Button;
  import org.eclipse.swt.widgets.Combo;
  import org.eclipse.swt.widgets.Composite;
  import org.eclipse.swt.widgets.Control;
  import org.eclipse.swt.widgets.Label;
  import org.eclipse.swt.widgets.Link;
  import org.eclipse.swt.widgets.Shell;
  import org.eclipse.ui.IEditorPart;
  import org.eclipse.ui.IWorkbenchSite;
  import org.eclipse.ui.PlatformUI;
  import org.eclipse.ui.dialogs.ISelectionStatusValidator;
  import org.eclipse.wst.jsdt.core.Flags;
  import org.eclipse.wst.jsdt.core.IField;
  import org.eclipse.wst.jsdt.core.IJavaScriptElement;
  import org.eclipse.wst.jsdt.core.IJavaScriptUnit;
  import org.eclipse.wst.jsdt.core.IMember;
  import org.eclipse.wst.jsdt.core.IType;
  import org.eclipse.wst.jsdt.core.JavaScriptModelException;
  import org.eclipse.wst.jsdt.core.Signature;
  import org.eclipse.wst.jsdt.core.dom.AST;
  import org.eclipse.wst.jsdt.core.dom.JavaScriptUnit;
  import org.eclipse.wst.jsdt.internal.corext.codemanipulation.AddGetterSetterOperation;
  import org.eclipse.wst.jsdt.internal.corext.codemanipulation.CodeGenerationSettings;
  import org.eclipse.wst.jsdt.internal.corext.codemanipulation.GetterSetterUtil;
  import org.eclipse.wst.jsdt.internal.corext.codemanipulation.IRequestQuery;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.util.RefactoringASTParser;
  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.Messages;
  import org.eclipse.wst.jsdt.internal.ui.IJavaHelpContextIds;
  import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin;
  import org.eclipse.wst.jsdt.internal.ui.actions.ActionMessages;
  import org.eclipse.wst.jsdt.internal.ui.actions.ActionUtil;
  import org.eclipse.wst.jsdt.internal.ui.actions.SelectionConverter;
  import org.eclipse.wst.jsdt.internal.ui.actions.WorkbenchRunnableAdapter;
  import org.eclipse.wst.jsdt.internal.ui.dialogs.SourceActionDialog;
  import org.eclipse.wst.jsdt.internal.ui.dialogs.StatusInfo;
  import org.eclipse.wst.jsdt.internal.ui.javaeditor.CompilationUnitEditor;
  import org.eclipse.wst.jsdt.internal.ui.preferences.JavaPreferencesSettings;
  import org.eclipse.wst.jsdt.internal.ui.util.BusyIndicatorRunnableContext;
  import org.eclipse.wst.jsdt.internal.ui.util.ElementValidator;
  import org.eclipse.wst.jsdt.internal.ui.util.ExceptionHandler;
  import org.eclipse.wst.jsdt.internal.ui.viewsupport.JavaElementImageProvider;
  import org.eclipse.wst.jsdt.ui.JavaScriptElementComparator;
  import org.eclipse.wst.jsdt.ui.JavaScriptElementImageDescriptor;
  import org.eclipse.wst.jsdt.ui.JavaScriptElementLabelProvider;
  import org.eclipse.wst.jsdt.ui.JavaScriptElementLabels;
  import org.eclipse.wst.jsdt.ui.JavaScriptUI;
  
  /**
   * Creates getter and setter methods for a type's fields. Opens a dialog with a list of
   * fields for which a setter or getter can be generated. User is able to check or uncheck
   * items before setters or getters are generated.
   * <p>
   * Will open the parent compilation unit in a JavaScript editor. The result is unsaved, so the
   * user can decide if the changes are acceptable.
   * <p>
   * The action is applicable to structured selections containing elements of type
   * <code>IField</code> or <code>IType</code>.
   * 
   * <p>
   * This class may be instantiated; it is not intended to be subclassed.
   * </p>
   * 
   * 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 AddGetterSetterAction extends SelectionDispatchAction {
  
  	private boolean fSort;
  
  	//private boolean fFinal;
  
  	private int fVisibility;
  
  	private boolean fGenerateComment;
  
  	private int fNumEntries;
  
  	private CompilationUnitEditor fEditor;
  
  	private static final String DIALOG_TITLE= ActionMessages.AddGetterSetterAction_error_title; 
  
  	/**
  	 * Creates a new <code>AddGetterSetterAction</code>. The action requires that the
  	 * selection provided by the site's selection provider is of type <code>
  	 * org.eclipse.jface.viewers.IStructuredSelection</code>.
  	 * 
  	 * @param site the site providing context information for this action
  	 */
  	public AddGetterSetterAction(IWorkbenchSite site) {
  		super(site);
  		setText(ActionMessages.AddGetterSetterAction_label); 
  		setDescription(ActionMessages.AddGetterSetterAction_description); 
  		setToolTipText(ActionMessages.AddGetterSetterAction_tooltip); 
  
  		PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IJavaHelpContextIds.GETTERSETTER_ACTION);
  	}
  
  	/**
  	 * Note: This constructor is for internal use only. Clients should not call this
  	 * constructor.
  	 * 
  	 * @param editor the compilation unit editor
  	 */
  	public AddGetterSetterAction(CompilationUnitEditor editor) {
  		this(editor.getEditorSite());
  		fEditor= editor;
  		setEnabled(SelectionConverter.getInputAsCompilationUnit(editor) != null);
  		fEditor.getEditorSite();
  	}
  
  	// ---- Structured Viewer -----------------------------------------------------------
  
  	/*
  	 * (non-Javadoc) Method declared on SelectionDispatchAction
  	 */
  	public void selectionChanged(IStructuredSelection selection) {
  		try {
  			setEnabled(canEnable(selection));
  		} catch (JavaScriptModelException e) {
  			// http://bugs.eclipse.org/bugs/show_bug.cgi?id=19253
  			if (JavaModelUtil.isExceptionToBeLogged(e))
  				JavaScriptPlugin.log(e);
  			setEnabled(false);
  		}
  	}
  
  	/*
  	 * (non-Javadoc) Method declared on SelectionDispatchAction
  	 */
  	public void run(IStructuredSelection selection) {
  		try {
  			IField[] selectedFields= getSelectedFields(selection);
  			if (canRunOn(selectedFields)) {
  				run(selectedFields[0].getDeclaringType(), selectedFields, false);
  				return;
  			}
  			Object firstElement= selection.getFirstElement();
  
  			if (firstElement instanceof IType)
  				run((IType) firstElement, new IField[0], false);
  			else if (firstElement instanceof IJavaScriptUnit) {
  				// http://bugs.eclipse.org/bugs/show_bug.cgi?id=38500
  				IType type= ((IJavaScriptUnit) firstElement).findPrimaryType();
  				// type can be null if file has a bad encoding
  				if (type == null) {
  					MessageDialog.openError(getShell(), 
  						ActionMessages.AddGetterSetterAction_no_primary_type_title, 
  						ActionMessages.AddGetterSetterAction_no_primary_type_message);
  					notifyResult(false);
  					return;
  				}
  				
  				run(((IJavaScriptUnit) firstElement).findPrimaryType(), new IField[0], false);
  			}
  		} catch (CoreException e) {
  			ExceptionHandler.handle(e, getShell(), DIALOG_TITLE, ActionMessages.AddGetterSetterAction_error_actionfailed); 
  		}
  
  	}
  
  	private boolean canEnable(IStructuredSelection selection) throws JavaScriptModelException {
  		if (getSelectedFields(selection) != null)
  			return true;
  
  		if ((selection.size() == 1) && (selection.getFirstElement() instanceof IType)) {
  			IType type= (IType) selection.getFirstElement();
  			return type.getJavaScriptUnit() != null && !type.isLocal();
  		}
  
  		if ((selection.size() == 1) && (selection.getFirstElement() instanceof IJavaScriptUnit))
  			return true;
  
  		return false;
  	}
  
  	private boolean canRunOn(IField[] fields) throws JavaScriptModelException {
  		if (fields == null || fields.length == 0)
  			return false;
  		int count= 0;
  		for (int index= 0; index < fields.length; index++) {
  			count++;
  		}
  		if (count == 0)
  			MessageDialog.openInformation(getShell(), DIALOG_TITLE, ActionMessages.AddGetterSetterAction_not_applicable); 
  		return (count > 0);
  	}
  
  	private void resetNumEntries() {
  		fNumEntries= 0;
  	}
  
  	private void incNumEntries() {
  		fNumEntries++;
  	}
  
  	private void run(IType type, IField[] preselected, boolean editor) throws CoreException {
  		if (type.getJavaScriptUnit() == null) {
  			MessageDialog.openInformation(getShell(), DIALOG_TITLE, ActionMessages.AddGetterSetterAction_error_not_in_source_file);
  			notifyResult(false);
  			return;
  		}
  		if (!ElementValidator.check(type, getShell(), DIALOG_TITLE, editor)) {
  			notifyResult(false);
  			return;
  		}
  		if (!ActionUtil.isEditable(getShell(), type)) {
  			notifyResult(false);
  			return;
  		}
  
  		ILabelProvider lp= new AddGetterSetterLabelProvider();
  		resetNumEntries();
  		Map entries= createGetterSetterMapping(type);
  		if (entries.isEmpty()) {
  			MessageDialog.openInformation(getShell(), DIALOG_TITLE, ActionMessages.AddGettSetterAction_typeContainsNoFields_message);
  			notifyResult(false);
  			return;
  		}
  		AddGetterSetterContentProvider cp= new AddGetterSetterContentProvider(entries);
  		GetterSetterTreeSelectionDialog dialog= new GetterSetterTreeSelectionDialog(getShell(), lp, cp, fEditor, type);
  		dialog.setComparator(new JavaScriptElementComparator());
  		dialog.setTitle(DIALOG_TITLE);
  		String message= ActionMessages.AddGetterSetterAction_dialog_label;
  		dialog.setMessage(message);
  		dialog.setValidator(createValidator(fNumEntries));
  		dialog.setContainerMode(true);
  		dialog.setSize(60, 18);
  		dialog.setInput(type);
  
  		if (preselected.length > 0) {
  			dialog.setInitialSelections(preselected);
  			dialog.setExpandedElements(preselected);
  		}
  		final Set keySet= new LinkedHashSet(entries.keySet());
  		int dialogResult= dialog.open();
  		if (dialogResult == Window.OK) {
  			Object[] result= dialog.getResult();
  			if (result == null) {
  				notifyResult(false);
  				return;
  			}
  			fSort= dialog.getSortOrder();
  			//fFinal= dialog.getFinal();
  			fVisibility= dialog.getVisibilityModifier();
  			fGenerateComment= dialog.getGenerateComment();
  			IField[] getterFields, setterFields, getterSetterFields;
  			if (fSort) {
  				getterFields= getGetterFields(result, keySet);
  				setterFields= getSetterFields(result, keySet);
  				getterSetterFields= new IField[0];
  			} else {
  				getterFields= getGetterOnlyFields(result, keySet);
  				setterFields= getSetterOnlyFields(result, keySet);
  				getterSetterFields= getGetterSetterFields(result, keySet);
  			}
  			generate(type, getterFields, setterFields, getterSetterFields, new RefactoringASTParser(AST.JLS3).parse(type.getJavaScriptUnit(), true), dialog.getElementPosition());
  		}
  		notifyResult(dialogResult == Window.OK);
  	}
  
  	private static class AddGetterSetterSelectionStatusValidator implements ISelectionStatusValidator {
  
  		private static int fEntries;
  
  		AddGetterSetterSelectionStatusValidator(int entries) {
  			fEntries= entries;
  		}
  
  		public IStatus validate(Object[] selection) {
  			// https://bugs.eclipse.org/bugs/show_bug.cgi?id=38478
  			HashSet map= null;
  			if ((selection != null) && (selection.length > 1)) {
  				map= new HashSet(selection.length);
  			}
  
  			int count= 0;
  			for (int i= 0; i < selection.length; i++) {
  				try {
  					if (selection[i] instanceof GetterSetterEntry) {
  						Object key= selection[i];
  						IField getsetField= ((GetterSetterEntry) selection[i]).field;
  						if (((GetterSetterEntry) selection[i]).isGetter) {
  							if (!map.add(GetterSetterUtil.getGetterName(getsetField, null)))
  								return new StatusInfo(IStatus.WARNING, ActionMessages.AddGetterSetterAction_error_duplicate_methods); 
  						} else {
  							key= createSignatureKey(GetterSetterUtil.getSetterName(getsetField, null), getsetField);
  							if (!map.add(key))
  								return new StatusInfo(IStatus.WARNING, ActionMessages.AddGetterSetterAction_error_duplicate_methods); 
  						}
  						count++;
  					}
  				} catch (JavaScriptModelException e) {
  				}
  			}
  
  			if (count == 0)
  				return new StatusInfo(IStatus.ERROR, ""); //$NON-NLS-1$
  			String message= Messages.format(ActionMessages.AddGetterSetterAction_methods_selected, 
  					new Object[] { String.valueOf(count), String.valueOf(fEntries)});
  			return new StatusInfo(IStatus.INFO, message);
  		}
  	}
  
  	/**
  	 * Creates a key used in hash maps for a method signature
  	 * (gettersettername+arguments(fqn)).
  	 */
  	private static String createSignatureKey(String methodName, IField field) throws JavaScriptModelException {
  		StringBuffer buffer= new StringBuffer();
  		buffer.append(methodName);
  		String fieldType= field.getTypeSignature();
  		String signature= Signature.getSimpleName(Signature.toString(fieldType));
  		buffer.append("#"); //$NON-NLS-1$
  		buffer.append(signature);
  
  		return buffer.toString();
  	}
  
  	private static ISelectionStatusValidator createValidator(int entries) {
  		AddGetterSetterSelectionStatusValidator validator= new AddGetterSetterSelectionStatusValidator(entries);
  		return validator;
  	}
  
  	// returns a list of fields with setter entries checked
  	private static IField[] getSetterFields(Object[] result, Set set) {
  		List list= new ArrayList(0);
  		Object each= null;
  		GetterSetterEntry entry= null;
  		for (int i= 0; i < result.length; i++) {
  			each= result[i];
  			if ((each instanceof GetterSetterEntry)) {
  				entry= (GetterSetterEntry) each;
  				if (!entry.isGetter) {
  					list.add(entry.field);
  				}
  			}
  		}
  		list= reorderFields(list, set);
  		return (IField[]) list.toArray(new IField[list.size()]);
  	}
  
  	// returns a list of fields with getter entries checked
  	private static IField[] getGetterFields(Object[] result, Set set) {
  		List list= new ArrayList(0);
  		Object each= null;
  		GetterSetterEntry entry= null;
  		for (int i= 0; i < result.length; i++) {
  			each= result[i];
  			if ((each instanceof GetterSetterEntry)) {
  				entry= (GetterSetterEntry) each;
  				if (entry.isGetter) {
  					list.add(entry.field);
  				}
  			}
  		}
  		list= reorderFields(list, set);
  		return (IField[]) list.toArray(new IField[list.size()]);
  	}
  
  	// returns a list of fields with only getter entries checked
  	private static IField[] getGetterOnlyFields(Object[] result, Set set) {
  		List list= new ArrayList(0);
  		Object each= null;
  		GetterSetterEntry entry= null;
  		boolean getterSet= false;
  		for (int i= 0; i < result.length; i++) {
  			each= result[i];
  			if ((each instanceof GetterSetterEntry)) {
  				entry= (GetterSetterEntry) each;
  				if (entry.isGetter) {
  					list.add(entry.field);
  					getterSet= true;
  				}
  				if ((!entry.isGetter) && (getterSet == true)) {
  					list.remove(entry.field);
  					getterSet= false;
  				}
  			} else
  				getterSet= false;
  		}
  		list= reorderFields(list, set);
  		return (IField[]) list.toArray(new IField[list.size()]);
  	}
  
  	// returns a list of fields with only setter entries checked
  	private static IField[] getSetterOnlyFields(Object[] result, Set set) {
  		List list= new ArrayList(0);
  		Object each= null;
  		GetterSetterEntry entry= null;
  		boolean getterSet= false;
  		for (int i= 0; i < result.length; i++) {
  			each= result[i];
  			if ((each instanceof GetterSetterEntry)) {
  				entry= (GetterSetterEntry) each;
  				if (entry.isGetter) {
  					getterSet= true;
  				}
  				if ((!entry.isGetter) && (getterSet != true)) {
  					list.add(entry.field);
  					getterSet= false;
  				}
  			} else
  				getterSet= false;
  		}
  		list= reorderFields(list, set);
  		return (IField[]) list.toArray(new IField[list.size()]);
  	}
  
  	// returns a list of fields with both entries checked
  	private static IField[] getGetterSetterFields(Object[] result, Set set) {
  		List list= new ArrayList(0);
  		Object each= null;
  		GetterSetterEntry entry= null;
  		boolean getterSet= false;
  		for (int i= 0; i < result.length; i++) {
  			each= result[i];
  			if ((each instanceof GetterSetterEntry)) {
  				entry= (GetterSetterEntry) each;
  				if (entry.isGetter) {
  					getterSet= true;
  				}
  				if ((!entry.isGetter) && (getterSet == true)) {
  					list.add(entry.field);
  					getterSet= false;
  				}
  			} else
  				getterSet= false;
  		}
  		list= reorderFields(list, set);
  		return (IField[]) list.toArray(new IField[list.size()]);
  	}
  
      private static List reorderFields(List collection, Set set) {
      	final List list= new ArrayList(collection.size());
      	for (final Iterator iterator= set.iterator(); iterator.hasNext();) {
  	        final IField field= (IField) iterator.next();
  	        if (collection.contains(field))
  	        	list.add(field);
          }
      	return list;
      }
  
  	private void generate(IType type, IField[] getterFields, IField[] setterFields, IField[] getterSetterFields, JavaScriptUnit unit, IJavaScriptElement elementPosition) throws CoreException {
  		if (getterFields.length == 0 && setterFields.length == 0 && getterSetterFields.length == 0)
  			return;
  
  		IJavaScriptUnit cu= null;
  		if (getterFields.length != 0)
  			cu= getterFields[0].getJavaScriptUnit();
  		else if (setterFields.length != 0)
  			cu= setterFields[0].getJavaScriptUnit();
  		else
  			cu= getterSetterFields[0].getJavaScriptUnit();
  		// open the editor, forces the creation of a working copy
  		run(cu, type, getterFields, setterFields, getterSetterFields, JavaScriptUI.openInEditor(cu), unit, elementPosition);
  	}
  
  	// ---- JavaScript Editor --------------------------------------------------------------
  
  	/*
  	 * (non-Javadoc) Method declared on SelectionDispatchAction
  	 */
  	public void selectionChanged(ITextSelection selection) {
  	}
  
  	/*
  	 * (non-Javadoc) Method declared on SelectionDispatchAction
  	 */
  	public void run(ITextSelection selection) {
  		try {
  			if (!ActionUtil.isProcessable(fEditor)) {
  				notifyResult(false);
  				return;
  			}
  
  			IJavaScriptElement[] elements= SelectionConverter.codeResolveForked(fEditor, true);
  			if (elements.length == 1 && (elements[0] instanceof IField)) {
  				IField field= (IField) elements[0];
  				run(field.getDeclaringType(), new IField[] { field}, true);
  				return;
  			}
  			IJavaScriptElement element= SelectionConverter.getElementAtOffset(fEditor);
  
  			if (element != null) {
  				IType type= (IType) element.getAncestor(IJavaScriptElement.TYPE);
  				if (type != null) {
  					if (type.getFields().length > 0) {
  						run(type, new IField[0], true);
  						return;
  					}
  				}
  			}
  			MessageDialog.openInformation(getShell(), DIALOG_TITLE, ActionMessages.AddGetterSetterAction_not_applicable); 
  		} catch (CoreException e) {
  			ExceptionHandler.handle(e, getShell(), DIALOG_TITLE, ActionMessages.AddGetterSetterAction_error_actionfailed); 
  		} catch (InvocationTargetException e) {
  			ExceptionHandler.handle(e, getShell(), DIALOG_TITLE, ActionMessages.AddGetterSetterAction_error_actionfailed); 
  		} catch (InterruptedException e) {
  			// cancelled
  		}
  	}
  
  	// ---- Helpers -------------------------------------------------------------------
  
  	private void run(IJavaScriptUnit cu, IType type, IField[] getterFields, IField[] setterFields, IField[] getterSetterFields, IEditorPart editor, JavaScriptUnit unit, IJavaScriptElement elementPosition) {
  		IRewriteTarget target= (IRewriteTarget) editor.getAdapter(IRewriteTarget.class);
  		if (target != null) {
  			target.beginCompoundChange();
  		}
  		try {
  			CodeGenerationSettings settings= JavaPreferencesSettings.getCodeGenerationSettings(cu.getJavaScriptProject());
  			settings.createComments= fGenerateComment;
  
  			AddGetterSetterOperation op= new AddGetterSetterOperation(type, getterFields, setterFields, getterSetterFields, unit, skipReplaceQuery(), elementPosition, settings, true, false);
  			setOperationStatusFields(op);
  
  			IRunnableContext context= JavaScriptPlugin.getActiveWorkbenchWindow();
  			if (context == null) {
  				context= new BusyIndicatorRunnableContext();
  			}
  
  			PlatformUI.getWorkbench().getProgressService().runInUI(context, new WorkbenchRunnableAdapter(op, op.getSchedulingRule()), op.getSchedulingRule());
  
  		} catch (InvocationTargetException e) {
  			String message= ActionMessages.AddGetterSetterAction_error_actionfailed; 
  			ExceptionHandler.handle(e, getShell(), DIALOG_TITLE, message);
  		} catch (InterruptedException e) {
  			// operation canceled
  		} finally {
  			if (target != null) {
  				target.endCompoundChange();
  			}
  		}
  	}
  
  	private void setOperationStatusFields(AddGetterSetterOperation op) {
  		// Set the status fields corresponding to the visibility and modifiers set
  		int flags= fVisibility;
  
  		op.setSort(fSort);
  		op.setVisibility(flags);
  	}
  
  	private IRequestQuery skipReplaceQuery() {
  		return new IRequestQuery() {
  
  			public int doQuery(IMember method) {
  				int[] returnCodes= { IRequestQuery.YES, IRequestQuery.NO, IRequestQuery.YES_ALL, IRequestQuery.CANCEL};
  				String skipLabel= ActionMessages.AddGetterSetterAction_SkipExistingDialog_skip_label; 
  				String replaceLabel= ActionMessages.AddGetterSetterAction_SkipExistingDialog_replace_label; 
  				String skipAllLabel= ActionMessages.AddGetterSetterAction_SkipExistingDialog_skipAll_label; 
  				String[] options= { skipLabel, replaceLabel, skipAllLabel, IDialogConstants.CANCEL_LABEL};
  				String methodName= JavaScriptElementLabels.getElementLabel(method, JavaScriptElementLabels.M_PARAMETER_TYPES);
  				String formattedMessage= Messages.format(ActionMessages.AddGetterSetterAction_SkipExistingDialog_message, methodName); 
  				return showQueryDialog(formattedMessage, options, returnCodes);
  			}
  		};
  	}
  
  	private int showQueryDialog(final String message, final String[] buttonLabels, int[] returnCodes) {
  		final Shell shell= getShell();
  		if (shell == null) {
  			JavaScriptPlugin.logErrorMessage("AddGetterSetterAction.showQueryDialog: No active shell found"); //$NON-NLS-1$
  			return IRequestQuery.CANCEL;
  		}
  		final int[] result= { Window.CANCEL};
  		shell.getDisplay().syncExec(new Runnable() {
  
  			public void run() {
  				String title= ActionMessages.AddGetterSetterAction_QueryDialog_title; 
  				MessageDialog dialog= new MessageDialog(shell, title, null, message, MessageDialog.QUESTION, buttonLabels, 0);
  				result[0]= dialog.open();
  			}
  		});
  		int returnVal= result[0];
  		return returnVal < 0 ? IRequestQuery.CANCEL : returnCodes[returnVal];
  	}
  
  	/*
  	 * Returns fields in the selection or <code>null</code> if the selection is empty or
  	 * not valid.
  	 */
  	private IField[] getSelectedFields(IStructuredSelection selection) {
  		List elements= selection.toList();
  		int nElements= elements.size();
  		if (nElements > 0) {
  			IField[] res= new IField[nElements];
  			IJavaScriptUnit cu= null;
  			for (int i= 0; i < nElements; i++) {
  				Object curr= elements.get(i);
  				if (curr instanceof IField) {
  					IField fld= (IField) curr;
  
  					if (i == 0) {
  						// remember the cu of the first element
  						cu= fld.getJavaScriptUnit();
  						if (cu == null) {
  							return null;
  						}
  					} else if (!cu.equals(fld.getJavaScriptUnit())) {
  						// all fields must be in the same CU
  						return null;
  					}
  					
  					final IType declaringType= fld.getDeclaringType();
  					if (declaringType==null)
  						return null;
  					
  					res[i]= fld;
  				} else {
  					return null;
  				}
  			}
  			return res;
  		}
  		return null;
  	}
  
  	private static class AddGetterSetterLabelProvider extends JavaScriptElementLabelProvider {
  
  		AddGetterSetterLabelProvider() {
  		}
  
  		/*
  		 * @see ILabelProvider#getText(Object)
  		 */
  		public String getText(Object element) {
  			if (element instanceof GetterSetterEntry) {
  				GetterSetterEntry entry= (GetterSetterEntry) element;
  				try {
  					if (entry.isGetter) {
  						return GetterSetterUtil.getGetterName(entry.field, null) + "()"; //$NON-NLS-1$ 
  					} else {
  						return GetterSetterUtil.getSetterName(entry.field, null) + '(' + Signature.getSimpleName(Signature.toString(entry.field.getTypeSignature())) + ')';
  					}
  				} catch (JavaScriptModelException e) {
  					return ""; //$NON-NLS-1$
  				}
  			}
  			return super.getText(element);
  		}
  
  		/*
  		 * @see ILabelProvider#getImage(Object)
  		 */
  		public Image getImage(Object element) {
  			if (element instanceof GetterSetterEntry) {
  				int flags= 0;
  				try {
  					flags= ((GetterSetterEntry) element).field.getFlags();
  				} catch (JavaScriptModelException e) {
  					JavaScriptPlugin.log(e);
  				}
  				ImageDescriptor desc= JavaElementImageProvider.getFieldImageDescriptor(false, Flags.AccPublic);
  				int adornmentFlags= Flags.isStatic(flags) ? JavaScriptElementImageDescriptor.STATIC : 0;
  				desc= new JavaScriptElementImageDescriptor(desc, adornmentFlags, JavaElementImageProvider.BIG_SIZE);
  				return JavaScriptPlugin.getImageDescriptorRegistry().get(desc);
  			}
  			return super.getImage(element);
  		}
  	}
  
  	/**
  	 * @return map IField -> GetterSetterEntry[]
  	 */
  	private Map createGetterSetterMapping(IType type) throws JavaScriptModelException {
  		IField[] fields= type.getFields();
  		Map result= new LinkedHashMap();
  		for (int i= 0; i < fields.length; i++) {
  			IField field= fields[i];
  			
  			List l= new ArrayList(2);
  			if (GetterSetterUtil.getGetter(field) == null) {
  				l.add(new GetterSetterEntry(field, true, false));
  				incNumEntries();
  			}
  
  			if (GetterSetterUtil.getSetter(field) == null) {
  				l.add(new GetterSetterEntry(field, false, false));
  				incNumEntries();
  			}
  
  			if (!l.isEmpty())
  				result.put(field, l.toArray(new GetterSetterEntry[l.size()]));
  		
  		}
  		return result;
  	}
  
  	private static class AddGetterSetterContentProvider implements ITreeContentProvider {
  
  		private static final Object[] EMPTY= new Object[0];
  
  		//private Viewer fViewer;
  
  		private Map fGetterSetterEntries;
  
  		public AddGetterSetterContentProvider(Map entries) {
  			fGetterSetterEntries= entries;
  		}
  
  		/*
  		 * @see IContentProvider#inputChanged(Viewer, Object, Object)
  		 */
  		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
  			//fViewer= viewer;
  		}
  
  //		public Viewer getViewer() {
  //			return fViewer;
  //		}
  			
  		/*
  		 * @see ITreeContentProvider#getChildren(Object)
  		 */
  		public Object[] getChildren(Object parentElement) {
  			if (parentElement instanceof IField)
  				return (Object[]) fGetterSetterEntries.get(parentElement);
  			return EMPTY;
  		}
  
  		/*
  		 * @see ITreeContentProvider#getParent(Object)
  		 */
  		public Object getParent(Object element) {
  			if (element instanceof IMember)
  			{ IMember member=(IMember) element;
  				return  member.getDeclaringType()!=null ? 
  						member.getDeclaringType() :(Object)member.getJavaScriptUnit();
  			}
  			if (element instanceof GetterSetterEntry)
  				return ((GetterSetterEntry) element).field;
  			return null;
  		}
  
  		/*
  		 * @see ITreeContentProvider#hasChildren(Object)
  		 */
  		public boolean hasChildren(Object element) {
  			return getChildren(element).length > 0;
  		}
  
  		/*
  		 * @see IStructuredContentProvider#getElements(Object)
  		 */
  		public Object[] getElements(Object inputElement) {
  			return fGetterSetterEntries.keySet().toArray();
  		}
  
  		/*
  		 * @see IContentProvider#dispose()
  		 */
  		public void dispose() {
  			fGetterSetterEntries.clear();
  			fGetterSetterEntries= null;
  		}
  	}
  	
  	private static class SettersForFinalFieldsFilter extends ViewerFilter {
  		
  		private final AddGetterSetterContentProvider fContentProvider;
  
  		public SettersForFinalFieldsFilter(AddGetterSetterContentProvider contentProvider) {
  			fContentProvider= contentProvider;
  		}
  
  		public boolean select(Viewer viewer, Object parentElement, Object element) {
  			if (element instanceof GetterSetterEntry) {
  				GetterSetterEntry getterSetterEntry= (GetterSetterEntry) element;
  				return getterSetterEntry.isGetter || !getterSetterEntry.isFinal;
  			} else if (element instanceof IField) {
  				Object[] children= fContentProvider.getChildren(element);
  				for (int i= 0; i < children.length; i++) {
  					GetterSetterEntry curr= (GetterSetterEntry) children[i];
  					if (curr.isGetter || !curr.isFinal) {
  						return true;
  					}
  				}
  				return false;
  			}
  			return true;
  		}
  	}
  	
  
  	private static class GetterSetterTreeSelectionDialog extends SourceActionDialog {
  
  		private AddGetterSetterContentProvider fContentProvider;
  
  		private static final int SELECT_GETTERS_ID= IDialogConstants.CLIENT_ID + 1;
  		private static final int SELECT_SETTERS_ID= IDialogConstants.CLIENT_ID + 2;
  		private final String SETTINGS_SECTION= "AddGetterSetterDialog"; //$NON-NLS-1$
  		private final String SORT_ORDER= "SortOrdering"; //$NON-NLS-1$
  		private final String ALLOW_SETTERS_FOR_FINALS= "RemoveFinal"; //$NON-NLS-1$
  		
  		private IDialogSettings fSettings;
  		private SettersForFinalFieldsFilter fSettersForFinalFieldsFilter;
  
  		private boolean fSortOrder;
  		private boolean fAllowSettersForFinals;
  		
  		private ArrayList fPreviousSelectedFinals;
  
  
  		public GetterSetterTreeSelectionDialog(Shell parent, ILabelProvider labelProvider, AddGetterSetterContentProvider contentProvider, CompilationUnitEditor editor, IType type) throws JavaScriptModelException {
  			super(parent, labelProvider, contentProvider, editor, type, false);
  			fContentProvider= contentProvider;
  			fPreviousSelectedFinals= new ArrayList();
  
  			// http://bugs.eclipse.org/bugs/show_bug.cgi?id=19253
  			IDialogSettings dialogSettings= JavaScriptPlugin.getDefault().getDialogSettings();
  			fSettings= dialogSettings.getSection(SETTINGS_SECTION);
  			if (fSettings == null) {
  				fSettings= dialogSettings.addNewSection(SETTINGS_SECTION);
  				fSettings.put(SORT_ORDER, false); 
  				fSettings.put(ALLOW_SETTERS_FOR_FINALS, false); 
  			}
  
  			fSortOrder= fSettings.getBoolean(SORT_ORDER);
  			fAllowSettersForFinals= fSettings.getBoolean(ALLOW_SETTERS_FOR_FINALS);
  			
  			fSettersForFinalFieldsFilter= new SettersForFinalFieldsFilter(contentProvider);
  		}
  
  		public boolean getSortOrder() {
  			return fSortOrder;
  		}
  
  		public void setSortOrder(boolean sort) {
  			if (fSortOrder != sort) {
  				fSortOrder= sort;
  				fSettings.put(SORT_ORDER, sort);
  				if (getTreeViewer() != null) {
  					getTreeViewer().refresh();
  				}
  			}
  		}
  		
  		private boolean allowSettersForFinals() {
  			return fAllowSettersForFinals;
  		}
  		
  		public void allowSettersForFinals(boolean allowSettersForFinals) {
  			if (fAllowSettersForFinals != allowSettersForFinals) {
  				fAllowSettersForFinals= allowSettersForFinals;
  				fSettings.put(ALLOW_SETTERS_FOR_FINALS, allowSettersForFinals);
  				CheckboxTreeViewer treeViewer= getTreeViewer();
  				if (treeViewer != null) {
  					ArrayList newChecked= new ArrayList();
  					if (allowSettersForFinals) {
  						newChecked.addAll(fPreviousSelectedFinals);
  					}
  					fPreviousSelectedFinals.clear();
  					Object[] checkedElements= treeViewer.getCheckedElements();
  					for (int i= 0; i < checkedElements.length; i++) {
  						if (checkedElements[i] instanceof GetterSetterEntry) {
  							GetterSetterEntry entry= (GetterSetterEntry) checkedElements[i];
  							if (allowSettersForFinals || entry.isGetter || !entry.isFinal) {
  								newChecked.add(entry);
  							} else {
  								fPreviousSelectedFinals.add(entry);
  							}
  						}
  					}
  					if (allowSettersForFinals) {
  						treeViewer.removeFilter(fSettersForFinalFieldsFilter);
  					} else {
  						treeViewer.addFilter(fSettersForFinalFieldsFilter);
  					}
  					treeViewer.setCheckedElements(newChecked.toArray());
  				}
  				updateOKStatus();
  			}
  		}
  		
  		/* (non-Javadoc)
  		 * @see org.eclipse.ui.dialogs.CheckedTreeSelectionDialog#createTreeViewer(org.eclipse.swt.widgets.Composite)
  		 */
  		protected CheckboxTreeViewer createTreeViewer(Composite parent) {
  			 CheckboxTreeViewer treeViewer= super.createTreeViewer(parent);
  			 if (!fAllowSettersForFinals) {
  				 treeViewer.addFilter(fSettersForFinalFieldsFilter);
  			 }
  			return treeViewer;
  		}
  
  		protected void configureShell(Shell shell) {
  			super.configureShell(shell);
  			PlatformUI.getWorkbench().getHelpSystem().setHelp(shell, IJavaHelpContextIds.ADD_GETTER_SETTER_SELECTION_DIALOG);
  		}
  		
  		private void createGetterSetterButtons(Composite buttonComposite) {
  			createButton(buttonComposite, SELECT_GETTERS_ID, ActionMessages.GetterSetterTreeSelectionDialog_select_getters, false); 
  			createButton(buttonComposite, SELECT_SETTERS_ID, ActionMessages.GetterSetterTreeSelectionDialog_select_setters, false); 
  		}
  
  		protected void buttonPressed(int buttonId) {
  			super.buttonPressed(buttonId);
  			switch (buttonId) {
  				case SELECT_GETTERS_ID: {
  					getTreeViewer().setCheckedElements(getGetterSetterElements(true));
  					updateOKStatus();
  					break;
  				}
  				case SELECT_SETTERS_ID: {
  					getTreeViewer().setCheckedElements(getGetterSetterElements(false));
  					updateOKStatus();
  					break;
  				}
  			}
  		}
  
  		protected Composite createInsertPositionCombo(Composite composite) {
  			Button addRemoveFinalCheckbox= addAllowSettersForFinalslCheckbox(composite);
  			addRemoveFinalCheckbox.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
  			
  			Composite entryComposite= super.createInsertPositionCombo(composite);
  			addSortOrder(entryComposite);
  			addVisibilityAndModifiersChoices(entryComposite);
  			return entryComposite;
  		}
  
  		private Button addAllowSettersForFinalslCheckbox(Composite entryComposite) {
  			Button allowSettersForFinalsButton= new Button(entryComposite, SWT.CHECK);
  			allowSettersForFinalsButton.setText(ActionMessages.AddGetterSetterAction_allow_setters_for_finals_description); 
  
  			allowSettersForFinalsButton.addSelectionListener(new SelectionListener() {
  				public void widgetSelected(SelectionEvent e) {
  					boolean isSelected= (((Button) e.widget).getSelection());
  					allowSettersForFinals(isSelected);
  				}
  
  				public void widgetDefaultSelected(SelectionEvent e) {
  					widgetSelected(e);
  				}
  			});
  			allowSettersForFinalsButton.setSelection(allowSettersForFinals());
  			return allowSettersForFinalsButton;
  		}
  
  		private Composite addSortOrder(Composite composite) {
  			Label label= new Label(composite, SWT.NONE);
  			label.setText(ActionMessages.GetterSetterTreeSelectionDialog_sort_label); 
  			GridData gd= new GridData(GridData.FILL_BOTH);
  			label.setLayoutData(gd);
  
  			final Combo combo= new Combo(composite, SWT.READ_ONLY);
  			combo.setItems(new String[] { ActionMessages.GetterSetterTreeSelectionDialog_alpha_pair_sort, 
  					ActionMessages.GetterSetterTreeSelectionDialog_alpha_method_sort}); 
  			final int methodIndex= 1; // Hard-coded. Change this if the
  														// list gets more complicated.
  			// http://bugs.eclipse.org/bugs/show_bug.cgi?id=38400
  			int sort= getSortOrder() ? 1 : 0;
  			combo.setText(combo.getItem(sort));
  			gd= new GridData(GridData.FILL_BOTH);
  			combo.setLayoutData(gd);
  			combo.addSelectionListener(new SelectionAdapter() {
  
  				public void widgetSelected(SelectionEvent e) {
  					setSortOrder(combo.getSelectionIndex() == methodIndex);
  				}
  			});
  			return composite;
  		}
  
  		private Object[] getGetterSetterElements(boolean isGetter) {
  			Object[] allFields= fContentProvider.getElements(null);
  			Set result= new HashSet();
  			for (int i= 0; i < allFields.length; i++) {
  				IField field= (IField) allFields[i];
  				GetterSetterEntry[] entries= getEntries(field);
  				for (int j= 0; j < entries.length; j++) {
  					AddGetterSetterAction.GetterSetterEntry entry= entries[j];
  					if (entry.isGetter == isGetter)
  						result.add(entry);
  				}
  			}
  			return result.toArray();
  		}
  
  		private GetterSetterEntry[] getEntries(IField field) {
  			List result= Arrays.asList(fContentProvider.getChildren(field));
  			return (GetterSetterEntry[]) result.toArray(new GetterSetterEntry[result.size()]);
  		}
  
  		protected Composite createSelectionButtons(Composite composite) {
  			Composite buttonComposite= super.createSelectionButtons(composite);
  
  			GridLayout layout= new GridLayout();
  			buttonComposite.setLayout(layout);
  
  			createGetterSetterButtons(buttonComposite);
  
  			layout.marginHeight= 0;
  			layout.marginWidth= 0;
  			layout.numColumns= 1;
  
  			return buttonComposite;
  		}
  
  		/*
  		 * @see org.eclipse.wst.jsdt.internal.ui.dialogs.SourceActionDialog#createLinkControl(org.eclipse.swt.widgets.Composite)
  		 */
  		protected Control createLinkControl(Composite composite) {
  			Link link= new Link(composite, SWT.WRAP);
  			link.setText(ActionMessages.AddGetterSetterAction_template_link_description); 
  			link.addSelectionListener(new SelectionAdapter() {
  				public void widgetSelected(SelectionEvent e) {
  					openCodeTempatePage(CodeTemplateContextType.GETTERCOMMENT_ID);
  				}
  			});
  			link.setToolTipText(ActionMessages.AddGetterSetterAction_template_link_tooltip); 
  			
  			GridData gridData= new GridData(SWT.FILL, SWT.BEGINNING, true, false);
  			gridData.widthHint= convertWidthInCharsToPixels(40); // only expand further if anyone else requires it
  			link.setLayoutData(gridData);
  			return link;
  		}
  	}
  
  	private static class GetterSetterEntry {
  		public final IField field;
  		public final boolean isGetter;
  		public final boolean isFinal;
  
  		GetterSetterEntry(IField field, boolean isGetterEntry, boolean isFinal) {
  			this.field= field;
  			this.isGetter= isGetterEntry;
  			this.isFinal= isFinal;
  		}
  	}
  }
  