org.eclipse.jdt.ui.actions.AddGetterSetterAction.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jdt.ui.actions.AddGetterSetterAction.java

Source

/*******************************************************************************
 * Copyright (c) 2000, 2011 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.jdt.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.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.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.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.jface.text.IRewriteTarget;
import org.eclipse.jface.text.ITextSelection;

import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchSite;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.ISelectionStatusValidator;

import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.dom.CompilationUnit;

import org.eclipse.jdt.internal.corext.codemanipulation.AddGetterSetterOperation;
import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings;
import org.eclipse.jdt.internal.corext.codemanipulation.GetterSetterUtil;
import org.eclipse.jdt.internal.corext.codemanipulation.IRequestQuery;
import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser;
import org.eclipse.jdt.internal.corext.template.java.CodeTemplateContextType;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
import org.eclipse.jdt.internal.corext.util.JdtFlags;
import org.eclipse.jdt.internal.corext.util.Messages;

import org.eclipse.jdt.ui.JavaElementComparator;
import org.eclipse.jdt.ui.JavaElementImageDescriptor;
import org.eclipse.jdt.ui.JavaElementLabelProvider;
import org.eclipse.jdt.ui.JavaElementLabels;
import org.eclipse.jdt.ui.JavaUI;

import org.eclipse.jdt.internal.ui.IJavaHelpContextIds;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.actions.ActionMessages;
import org.eclipse.jdt.internal.ui.actions.ActionUtil;
import org.eclipse.jdt.internal.ui.actions.SelectionConverter;
import org.eclipse.jdt.internal.ui.actions.WorkbenchRunnableAdapter;
import org.eclipse.jdt.internal.ui.dialogs.SourceActionDialog;
import org.eclipse.jdt.internal.ui.dialogs.StatusInfo;
import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider;
import org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitEditor;
import org.eclipse.jdt.internal.ui.preferences.JavaPreferencesSettings;
import org.eclipse.jdt.internal.ui.util.BusyIndicatorRunnableContext;
import org.eclipse.jdt.internal.ui.util.ElementValidator;
import org.eclipse.jdt.internal.ui.util.ExceptionHandler;
import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
import org.eclipse.jdt.internal.ui.viewsupport.JavaElementImageProvider;

/**
 * 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 Java 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>
 *
 * @since 2.0
 *
 * @noextend This class is not intended to be subclassed by clients.
 */
public class AddGetterSetterAction extends SelectionDispatchAction {

    private boolean fSort;

    private boolean fSynchronized;

    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
     *
     * @noreference This constructor is not intended to be referenced by clients.
     */
    public AddGetterSetterAction(CompilationUnitEditor editor) {
        this(editor.getEditorSite());
        fEditor = editor;
        setEnabled(SelectionConverter.getInputAsCompilationUnit(editor) != null);
        fEditor.getEditorSite();
    }

    // ---- Structured Viewer -----------------------------------------------------------

    /*
     * (non-Javadoc) Method declared on SelectionDispatchAction
     */
    @Override
    public void selectionChanged(IStructuredSelection selection) {
        try {
            setEnabled(canEnable(selection));
        } catch (JavaModelException e) {
            // http://bugs.eclipse.org/bugs/show_bug.cgi?id=19253
            if (JavaModelUtil.isExceptionToBeLogged(e))
                JavaPlugin.log(e);
            setEnabled(false);
        }
    }

    /*
     * (non-Javadoc) Method declared on SelectionDispatchAction
     */
    @Override
    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 ICompilationUnit) {
                // http://bugs.eclipse.org/bugs/show_bug.cgi?id=38500
                IType type = ((ICompilationUnit) 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;
                }
                if (type.isAnnotation()) {
                    MessageDialog.openInformation(getShell(), DIALOG_TITLE,
                            ActionMessages.AddGetterSetterAction_annotation_not_applicable);
                    notifyResult(false);
                    return;
                } else if (type.isInterface()) {
                    MessageDialog.openInformation(getShell(), DIALOG_TITLE,
                            ActionMessages.AddGetterSetterAction_interface_not_applicable);
                    notifyResult(false);
                    return;
                } else
                    run(((ICompilationUnit) 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 JavaModelException {
        if (getSelectedFields(selection) != null)
            return true;

        if ((selection.size() == 1) && (selection.getFirstElement() instanceof IType)) {
            IType type = (IType) selection.getFirstElement();
            return type.getCompilationUnit() != null && !type.isInterface() && !type.isAnonymous();
        }

        if ((selection.size() == 1) && (selection.getFirstElement() instanceof ICompilationUnit))
            return true;

        return false;
    }

    private boolean canRunOn(IField[] fields) throws JavaModelException {
        if (fields == null || fields.length == 0)
            return false;
        int count = 0;
        for (int index = 0; index < fields.length; index++) {
            if (!JdtFlags.isEnum(fields[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.isAnnotation()) {
            MessageDialog.openInformation(getShell(), DIALOG_TITLE,
                    ActionMessages.AddGetterSetterAction_annotation_not_applicable);
            notifyResult(false);
            return;
        } else if (type.isInterface()) {
            MessageDialog.openInformation(getShell(), DIALOG_TITLE,
                    ActionMessages.AddGetterSetterAction_interface_not_applicable);
            notifyResult(false);
            return;
        } else if (type.getCompilationUnit() == 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<IField, GetterSetterEntry[]> 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 JavaElementComparator());
        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<IField> keySet = new LinkedHashSet<IField>(entries.keySet());
        int dialogResult = dialog.open();
        if (dialogResult == Window.OK) {
            Object[] result = dialog.getResult();
            if (result == null) {
                notifyResult(false);
                return;
            }
            fSort = dialog.getSortOrder();
            fSynchronized = dialog.getSynchronized();
            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(ASTProvider.SHARED_AST_LEVEL).parse(type.getCompilationUnit(), 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<Object> map = null;
            if ((selection != null) && (selection.length > 1)) {
                map = new HashSet<Object>(selection.length);
            }

            int selectedCount = 0;
            int possibleDuplicateCount = 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)))
                                possibleDuplicateCount++;
                        } else {
                            key = createSignatureKey(GetterSetterUtil.getSetterName(getsetField, null),
                                    getsetField);
                            if (!map.add(key))
                                possibleDuplicateCount++;
                        }
                        selectedCount++;
                    }
                } catch (JavaModelException e) {
                }
            }

            if (possibleDuplicateCount > 0) {
                return new StatusInfo(IStatus.WARNING,
                        possibleDuplicateCount == 1
                                ? ActionMessages.AddGetterSetterAction_error_duplicate_methods_singular
                                : Messages.format(
                                        ActionMessages.AddGetterSetterAction_error_duplicate_methods_plural,
                                        String.valueOf(possibleDuplicateCount)));
            }
            if (selectedCount == 0)
                return new StatusInfo(IStatus.ERROR, ""); //$NON-NLS-1$
            String message = Messages.format(ActionMessages.AddGetterSetterAction_methods_selected,
                    new Object[] { String.valueOf(selectedCount), String.valueOf(fEntries) });
            return new StatusInfo(IStatus.INFO, message);
        }
    }

    /**
     * Creates a key used in hash maps for a method signature (gettersettername+arguments(fqn)).
     * 
     * @param methodName the method name
     * @param field the filed
     * @return the signature
     * @throws JavaModelException if getting the field's type signature fails
     */
    private static String createSignatureKey(String methodName, IField field) throws JavaModelException {
        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<IField> set) {
        List<IField> list = new ArrayList<IField>(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 list.toArray(new IField[list.size()]);
    }

    // returns a list of fields with getter entries checked
    private static IField[] getGetterFields(Object[] result, Set<IField> set) {
        List<IField> list = new ArrayList<IField>(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 list.toArray(new IField[list.size()]);
    }

    // returns a list of fields with only getter entries checked
    private static IField[] getGetterOnlyFields(Object[] result, Set<IField> set) {
        List<IField> list = new ArrayList<IField>(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 list.toArray(new IField[list.size()]);
    }

    // returns a list of fields with only setter entries checked
    private static IField[] getSetterOnlyFields(Object[] result, Set<IField> set) {
        List<IField> list = new ArrayList<IField>(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 list.toArray(new IField[list.size()]);
    }

    // returns a list of fields with both entries checked
    private static IField[] getGetterSetterFields(Object[] result, Set<IField> set) {
        List<IField> list = new ArrayList<IField>(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 list.toArray(new IField[list.size()]);
    }

    private static List<IField> reorderFields(List<IField> collection, Set<IField> set) {
        final List<IField> list = new ArrayList<IField>(collection.size());
        for (final Iterator<IField> iterator = set.iterator(); iterator.hasNext();) {
            final IField field = iterator.next();
            if (collection.contains(field))
                list.add(field);
        }
        return list;
    }

    private void generate(IType type, IField[] getterFields, IField[] setterFields, IField[] getterSetterFields,
            CompilationUnit unit, IJavaElement elementPosition) throws CoreException {
        if (getterFields.length == 0 && setterFields.length == 0 && getterSetterFields.length == 0)
            return;

        ICompilationUnit cu = null;
        if (getterFields.length != 0)
            cu = getterFields[0].getCompilationUnit();
        else if (setterFields.length != 0)
            cu = setterFields[0].getCompilationUnit();
        else
            cu = getterSetterFields[0].getCompilationUnit();
        // open the editor, forces the creation of a working copy
        run(cu, type, getterFields, setterFields, getterSetterFields, JavaUI.openInEditor(cu), unit,
                elementPosition);
    }

    // ---- Java Editor --------------------------------------------------------------

    /*
     * (non-Javadoc) Method declared on SelectionDispatchAction
     */
    @Override
    public void selectionChanged(ITextSelection selection) {
    }

    /*
     * (non-Javadoc) Method declared on SelectionDispatchAction
     */
    @Override
    public void run(ITextSelection selection) {
        try {
            if (!ActionUtil.isProcessable(fEditor)) {
                notifyResult(false);
                return;
            }

            IJavaElement[] 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;
            }
            IJavaElement element = SelectionConverter.getElementAtOffset(fEditor);

            if (element != null) {
                IType type = (IType) element.getAncestor(IJavaElement.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(ICompilationUnit cu, IType type, IField[] getterFields, IField[] setterFields,
            IField[] getterSetterFields, IEditorPart editor, CompilationUnit unit, IJavaElement elementPosition) {
        IRewriteTarget target = (IRewriteTarget) editor.getAdapter(IRewriteTarget.class);
        if (target != null) {
            target.beginCompoundChange();
        }
        try {
            CodeGenerationSettings settings = JavaPreferencesSettings
                    .getCodeGenerationSettings(cu.getJavaProject());
            settings.createComments = fGenerateComment;

            AddGetterSetterOperation op = new AddGetterSetterOperation(type, getterFields, setterFields,
                    getterSetterFields, unit, skipReplaceQuery(), elementPosition, settings, true, false);
            setOperationStatusFields(op);

            IRunnableContext context = JavaPlugin.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;
        if (fSynchronized) {
            flags |= Flags.AccSynchronized;
        }
        if (fFinal) {
            flags |= Flags.AccFinal;
        }
        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 = JavaElementLabels.getElementLabel(method, JavaElementLabels.M_PARAMETER_TYPES);
                String formattedMessage = Messages.format(
                        ActionMessages.AddGetterSetterAction_SkipExistingDialog_message,
                        BasicElementLabels.getJavaElementName(methodName));
                return showQueryDialog(formattedMessage, options, returnCodes);
            }
        };
    }

    private int showQueryDialog(final String message, final String[] buttonLabels, int[] returnCodes) {
        final Shell shell = getShell();
        if (shell == null) {
            JavaPlugin.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];
            ICompilationUnit 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.getCompilationUnit();
                        if (cu == null) {
                            return null;
                        }
                    } else if (!cu.equals(fld.getCompilationUnit())) {
                        // all fields must be in the same CU
                        return null;
                    }
                    try {
                        final IType declaringType = fld.getDeclaringType();
                        if (declaringType.isInterface() || declaringType.isAnonymous())
                            return null;
                    } catch (JavaModelException e) {
                        JavaPlugin.log(e);
                        return null;
                    }

                    res[i] = fld;
                } else {
                    return null;
                }
            }
            return res;
        }
        return null;
    }

    private static class AddGetterSetterLabelProvider extends JavaElementLabelProvider {

        AddGetterSetterLabelProvider() {
        }

        /*
         * @see ILabelProvider#getText(Object)
         */
        @Override
        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 (JavaModelException e) {
                    return ""; //$NON-NLS-1$
                }
            }
            return super.getText(element);
        }

        /*
         * @see ILabelProvider#getImage(Object)
         */
        @Override
        public Image getImage(Object element) {
            if (element instanceof GetterSetterEntry) {
                int flags = 0;
                try {
                    flags = ((GetterSetterEntry) element).field.getFlags();
                } catch (JavaModelException e) {
                    JavaPlugin.log(e);
                }
                ImageDescriptor desc = JavaElementImageProvider.getFieldImageDescriptor(false, Flags.AccPublic);
                int adornmentFlags = Flags.isStatic(flags) ? JavaElementImageDescriptor.STATIC : 0;
                desc = new JavaElementImageDescriptor(desc, adornmentFlags, JavaElementImageProvider.BIG_SIZE);
                return JavaPlugin.getImageDescriptorRegistry().get(desc);
            }
            return super.getImage(element);
        }
    }

    /**
     * @param type the type
     * @return map IField -> GetterSetterEntry[]
     * @throws JavaModelException if the type does not exist or if an exception occurs while
     *             accessing its corresponding resource
     */
    private Map<IField, GetterSetterEntry[]> createGetterSetterMapping(IType type) throws JavaModelException {
        IField[] fields = type.getFields();
        Map<IField, GetterSetterEntry[]> result = new LinkedHashMap<IField, GetterSetterEntry[]>();
        for (int i = 0; i < fields.length; i++) {
            IField field = fields[i];
            int flags = field.getFlags();
            if (!Flags.isEnum(flags)) {
                List<GetterSetterEntry> l = new ArrayList<GetterSetterEntry>(2);
                if (GetterSetterUtil.getGetter(field) == null) {
                    l.add(new GetterSetterEntry(field, true, Flags.isFinal(flags)));
                    incNumEntries();
                }

                if (GetterSetterUtil.getSetter(field) == null) {
                    l.add(new GetterSetterEntry(field, false, Flags.isFinal(flags)));
                    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 Map<IField, GetterSetterEntry[]> fGetterSetterEntries;

        public AddGetterSetterContentProvider(Map<IField, GetterSetterEntry[]> entries) {
            fGetterSetterEntries = entries;
        }

        /*
         * @see IContentProvider#inputChanged(Viewer, Object, Object)
         */
        public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
        }

        /*
         * @see ITreeContentProvider#getChildren(Object)
         */
        public Object[] getChildren(Object parentElement) {
            if (parentElement instanceof IField)
                return fGetterSetterEntries.get(parentElement);
            return EMPTY;
        }

        /*
         * @see ITreeContentProvider#getParent(Object)
         */
        public Object getParent(Object element) {
            if (element instanceof IMember)
                return ((IMember) element).getDeclaringType();
            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;
        }

        @Override
        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<GetterSetterEntry> fPreviousSelectedFinals;

        public GetterSetterTreeSelectionDialog(Shell parent, ILabelProvider labelProvider,
                AddGetterSetterContentProvider contentProvider, CompilationUnitEditor editor, IType type)
                throws JavaModelException {
            super(parent, labelProvider, contentProvider, editor, type, false);
            fContentProvider = contentProvider;
            fPreviousSelectedFinals = new ArrayList<GetterSetterEntry>();

            // http://bugs.eclipse.org/bugs/show_bug.cgi?id=19253
            IDialogSettings dialogSettings = JavaPlugin.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<GetterSetterEntry> newChecked = new ArrayList<GetterSetterEntry>();
                    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)
         */
        @Override
        protected CheckboxTreeViewer createTreeViewer(Composite parent) {
            CheckboxTreeViewer treeViewer = super.createTreeViewer(parent);
            if (!fAllowSettersForFinals) {
                treeViewer.addFilter(fSettersForFinalFieldsFilter);
            }
            return treeViewer;
        }

        @Override
        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);
        }

        @Override
        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;
            }
            }
        }

        @Override
        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() {

                @Override
                public void widgetSelected(SelectionEvent e) {
                    setSortOrder(combo.getSelectionIndex() == methodIndex);
                }
            });
            return composite;
        }

        private Object[] getGetterSetterElements(boolean isGetter) {
            Object[] allFields = fContentProvider.getElements(null);
            Set<GetterSetterEntry> result = new HashSet<GetterSetterEntry>();
            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<Object> result = Arrays.asList(fContentProvider.getChildren(field));
            return result.toArray(new GetterSetterEntry[result.size()]);
        }

        @Override
        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.jdt.internal.ui.dialogs.SourceActionDialog#createLinkControl(org.eclipse.swt.widgets.Composite)
         */
        @Override
        protected Control createLinkControl(Composite composite) {
            Link link = new Link(composite, SWT.WRAP);
            link.setText(ActionMessages.AddGetterSetterAction_template_link_description);
            link.addSelectionListener(new SelectionAdapter() {
                @Override
                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;
        }
    }
}