ext.org.eclipse.jdt.internal.ui.dialogs.GenerateToStringDialog.java Source code

Java tutorial

Introduction

Here is the source code for ext.org.eclipse.jdt.internal.ui.dialogs.GenerateToStringDialog.java

Source

/*******************************************************************************
 * Copyright (c) 2008, 2011 Mateusz Matela 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:
 *     Mateusz Matela <mateusz.matela@gmail.com> - [code manipulation] [dcr] toString() builder wizard - https://bugs.eclipse.org/bugs/show_bug.cgi?id=26070
 *     Mateusz Matela <mateusz.matela@gmail.com> - [toString] Template edit dialog has usability issues - https://bugs.eclipse.org/bugs/show_bug.cgi?id=267916
 *     Mateusz Matela <mateusz.matela@gmail.com> - [toString] finish toString() builder wizard - https://bugs.eclipse.org/bugs/show_bug.cgi?id=267710
 *     Mateusz Matela <mateusz.matela@gmail.com> - [toString] toString() generator: Fields in declaration order - https://bugs.eclipse.org/bugs/show_bug.cgi?id=279924
 *******************************************************************************/
package ext.org.eclipse.jdt.internal.ui.dialogs;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

import com.ibm.icu.text.Collator;

import ext.org.eclipse.jdt.internal.corext.codemanipulation.tostringgeneration.GenerateToStringOperation;
import ext.org.eclipse.jdt.internal.corext.codemanipulation.tostringgeneration.ToStringGenerationSettings;
import ext.org.eclipse.jdt.internal.corext.codemanipulation.tostringgeneration.ToStringTemplateParser;
import ext.org.eclipse.jdt.internal.corext.codemanipulation.tostringgeneration.ToStringGenerationSettings.CustomBuilderSettings;
import ext.org.eclipse.jdt.internal.corext.util.JavaConventionsUtil;
import ext.org.eclipse.jdt.internal.corext.util.JavaModelUtil;
import ext.org.eclipse.jdt.internal.corext.util.Messages;
import ext.org.eclipse.jdt.internal.ui.IJavaHelpContextIds;
import ext.org.eclipse.jdt.internal.ui.JavaPluginImages;
import ext.org.eclipse.jdt.internal.ui.JavaUIMessages;
import ext.org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitEditor;
import ext.org.eclipse.jdt.internal.ui.util.SWTUtil;
import ext.org.eclipse.jdt.internal.ui.viewsupport.BindingLabelProvider;
import ext.org.eclipse.jdt.internal.ui.viewsupport.JavaElementImageProvider;
import ext.org.eclipse.jdt.internal.ui.wizards.dialogfields.LayoutUtil;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
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.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Spinner;
import org.eclipse.swt.widgets.Text;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;

import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.dialogs.StatusDialog;
import org.eclipse.jface.fieldassist.IContentProposal;
import org.eclipse.jface.fieldassist.IContentProposalProvider;
import org.eclipse.jface.fieldassist.TextContentAdapter;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.Viewer;

import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.ISelectionStatusValidator;
import org.eclipse.ui.dialogs.SelectionDialog;
import org.eclipse.ui.fieldassist.ContentAssistCommandAdapter;

import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.ui.IJavaElementSearchConstants;
import org.eclipse.jdt.ui.JavaElementImageDescriptor;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jdt.ui.dialogs.TypeSelectionExtension;

import patch.org.eclipse.jdt.internal.ui.JavaPlugin;

/**
 * Dialog for the generate toString() action.
 * 
 * @since 3.5
 */
public class GenerateToStringDialog extends SourceActionDialog {

    private static class GenerateToStringContentProvider implements ITreeContentProvider {

        private static final Object[] EMPTY = new Object[0];

        private IVariableBinding[] fFields;

        private IMethodBinding[] fMethods;

        private IVariableBinding[] fInheritedFields;

        private IMethodBinding[] fInheritedMethods;

        private String[] mainNodes;

        private static final String fieldsNode = JavaUIMessages.GenerateToStringDialog_fields_node;

        private static final String methodsNode = JavaUIMessages.GenerateToStringDialog_methods_node;

        private static final String inheritedFieldsNode = JavaUIMessages.GenerateToStringDialog_inherited_fields_node;

        private static final String inheritedMethodsNode = JavaUIMessages.GenerateToStringDialog_inherited_methods_node;

        public GenerateToStringContentProvider(IVariableBinding[] fields, IVariableBinding[] inheritedFields,
                IMethodBinding[] methods, IMethodBinding[] inheritedMethods) {
            ArrayList<String> nodes = new ArrayList<String>();
            fFields = fields.clone();
            if (fFields.length > 0)
                nodes.add(fieldsNode);
            fInheritedFields = inheritedFields.clone();
            if (fInheritedFields.length > 0)
                nodes.add(inheritedFieldsNode);
            fMethods = methods.clone();
            if (fMethods.length > 0)
                nodes.add(methodsNode);
            fInheritedMethods = inheritedMethods.clone();
            if (fInheritedMethods.length > 0)
                nodes.add(inheritedMethodsNode);
            mainNodes = nodes.toArray(new String[nodes.size()]);
        }

        private int getElementPosition(Object element, Object[] array) {
            for (int i = 0; i < array.length; i++) {
                if (array[i].equals(element)) {
                    return i;
                }
            }
            return -1;
        }

        private Object[] getContainingArray(Object element) {
            if (element instanceof String)
                return mainNodes;
            if (element instanceof IVariableBinding) {
                if (getElementPosition(element, fFields) >= 0)
                    return fFields;
                if (getElementPosition(element, fInheritedFields) >= 0)
                    return fInheritedFields;
            }
            if (element instanceof IMethodBinding) {
                if (getElementPosition(element, fMethods) >= 0)
                    return fMethods;
                if (getElementPosition(element, fInheritedMethods) >= 0)
                    return fInheritedMethods;
            }
            return EMPTY;
        }

        public boolean canMoveDown(Object element) {
            Object[] array = getContainingArray(element);
            int position = getElementPosition(element, array);
            return position != -1 && position != array.length - 1;
        }

        public boolean canMoveUp(Object element) {
            return getElementPosition(element, getContainingArray(element)) > 0;
        }

        public void down(Object element, CheckboxTreeViewer tree) {
            move(element, tree, 1);
        }

        public void up(Object element, CheckboxTreeViewer tree) {
            move(element, tree, -1);
        }

        private void move(Object element, CheckboxTreeViewer tree, int direction) {
            Object[] array = getContainingArray(element);
            int position = getElementPosition(element, array);
            Object temp = array[position];
            array[position] = array[position + direction];
            array[position + direction] = temp;
            tree.setSelection(new StructuredSelection(element));
            tree.refresh();
        }

        public void sort() {
            Comparator<IBinding> comparator = new Comparator<IBinding>() {
                Collator collator = Collator.getInstance();

                public int compare(IBinding b1, IBinding b2) {
                    return collator.compare(b1.getName(), b2.getName());
                }
            };
            Arrays.sort(fFields, comparator);
            Arrays.sort(fMethods, comparator);
            Arrays.sort(fInheritedFields, comparator);
            Arrays.sort(fInheritedMethods, comparator);
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
         */
        public Object[] getChildren(Object parentElement) {
            if (parentElement == fieldsNode)
                return fFields;
            if (parentElement == inheritedFieldsNode)
                return fInheritedFields;
            if (parentElement == methodsNode)
                return fMethods;
            if (parentElement == inheritedMethodsNode)
                return fInheritedMethods;
            return EMPTY;
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
         */
        public Object getParent(Object element) {
            Object[] array = getContainingArray(element);
            if (array == fFields)
                return fieldsNode;
            if (array == fInheritedFields)
                return inheritedFieldsNode;
            if (array == fMethods)
                return methodsNode;
            if (array == fInheritedMethods)
                return inheritedMethodsNode;
            return null;
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
         */
        public boolean hasChildren(Object element) {
            if (element instanceof String)
                return true;
            return false;
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
         */
        public Object[] getElements(Object inputElement) {
            return mainNodes;
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.eclipse.jface.viewers.IContentProvider#dispose()
         */
        public void dispose() {
        }

        /*
         * (non-Javadoc)
         * 
         * @see
         * org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
         * java.lang.Object, java.lang.Object)
         */
        public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
        }
    }

    private static class GenerateToStringLabelProvider extends BindingLabelProvider {
        @Override
        public Image getImage(Object element) {
            ImageDescriptor descriptor = null;
            if (element == GenerateToStringContentProvider.fieldsNode
                    || element == GenerateToStringContentProvider.inheritedFieldsNode)
                descriptor = JavaPluginImages.DESC_FIELD_PUBLIC;
            if (element == GenerateToStringContentProvider.methodsNode
                    || element == GenerateToStringContentProvider.inheritedMethodsNode)
                descriptor = JavaPluginImages.DESC_MISC_PUBLIC;
            if (descriptor != null) {
                descriptor = new JavaElementImageDescriptor(descriptor, 0, JavaElementImageProvider.BIG_SIZE);
                return JavaPlugin.getImageDescriptorRegistry().get(descriptor);
            }
            return super.getImage(element);
        }
    }

    private class GenerateToStringValidator implements ISelectionStatusValidator {

        private int fNumFields;

        private int fNumMethods;

        private CustomBuilderValidator fValidator;

        public GenerateToStringValidator(int fields, int methods) {
            fNumFields = fields;
            fNumMethods = methods;
        }

        public IStatus validate(Object[] selection) {
            if (getGenerationSettings().toStringStyle == GenerateToStringOperation.CUSTOM_BUILDER) {
                if (fValidator == null)
                    fValidator = new CustomBuilderValidator(getType().getJavaProject());
                IStatus status = fValidator.revalidateAll(getGenerationSettings().getCustomBuilderSettings());
                if (!status.isOK())
                    return new StatusInfo(IStatus.ERROR,
                            JavaUIMessages.GenerateToStringDialog_selectioninfo_customBuilderConfigError);
            }

            int countFields = 0, countMethods = 0;
            for (int index = 0; index < selection.length; index++) {
                if (selection[index] instanceof IVariableBinding)
                    countFields++;
                else if (selection[index] instanceof IMethodBinding)
                    countMethods++;
            }

            return new StatusInfo(IStatus.INFO,
                    Messages.format(JavaUIMessages.GenerateToStringDialog_selectioninfo_more,
                            new String[] { String.valueOf(countFields), String.valueOf(fNumFields),
                                    String.valueOf(countMethods), String.valueOf(fNumMethods) }));
        }
    }

    private class ToStringTemplatesDialog extends StatusDialog {

        private class TemplateEditionDialog extends StatusDialog {
            /**
             * Template number, -1 for new template.
             */
            private final int templateNumber;

            /**
             * Initial template name, can be <code>null</code>.
             */
            private final String fInitialTemplateName;

            private Text templateName;

            private Text template;

            private String resultTemplateName;

            private String resultTemplate;

            private StatusInfo nameValidationStatus = new StatusInfo();

            public TemplateEditionDialog(Shell parent, int templateNumber) {
                super(parent);
                this.templateNumber = templateNumber;
                fInitialTemplateName = templateNumber < 0 ? null : templateNames.get(templateNumber);
                setHelpAvailable(false);
            }

            @Override
            protected boolean isResizable() {
                return true;
            }

            @Override
            protected Control createDialogArea(Composite parent) {
                getShell().setText(
                        templateNumber >= 0 ? JavaUIMessages.GenerateToStringDialog_templateEdition_WindowTitle
                                : JavaUIMessages.GenerateToStringDialog_templateEdition_NewWindowTitle);

                Composite composite = (Composite) super.createDialogArea(parent);

                GridLayout layout = (GridLayout) composite.getLayout();
                layout.numColumns = 2;
                layout.horizontalSpacing = 8;

                Label label = new Label(composite, SWT.LEFT);
                label.setText(JavaUIMessages.GenerateToStringDialog_template_name);
                label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));

                templateName = new Text(composite, SWT.BORDER | SWT.SINGLE);
                templateName.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));

                label = new Label(composite, SWT.LEFT);
                label.setText(JavaUIMessages.GenerateToStringDialog_template_content);
                label.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false));

                template = new Text(composite, SWT.BORDER | SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
                GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
                gridData.heightHint = 80;
                gridData.widthHint = 480;
                template.setLayoutData(gridData);
                new ContentAssistCommandAdapter(template, new TextContentAdapter(),
                        new ToStringTemplateProposalProvider(), null, new char[] { '$' }, true)
                                .setPropagateKeys(false);

                if (templateNumber >= 0) {
                    templateName.setText(fInitialTemplateName);
                    template.setText(templates.get(templateNumber));
                } else {
                    templateName.setText(createNewTemplateName());
                    template.setText(ToStringTemplateParser.DEFAULT_TEMPLATE);
                }
                templateName.setSelection(0, templateName.getText().length());

                templateName.addModifyListener(new ModifyListener() {
                    public void modifyText(ModifyEvent e) {
                        validate(templateName.getText());
                    }
                });

                //Ctrl+Enter should execute the default button, workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=145959
                template.addTraverseListener(new TraverseListener() {
                    public void keyTraversed(TraverseEvent e) {
                        if (e.detail == SWT.TRAVERSE_RETURN && (e.stateMask & SWT.MODIFIER_MASK) != 0) {
                            buttonPressed(((Integer) getShell().getDefaultButton().getData()).intValue());
                        }
                    }
                });

                return composite;
            }

            private void validate(String newName) {
                if (newName.length() == 0) {
                    nameValidationStatus.setError(
                            JavaUIMessages.GenerateToStringDialog_templateEdition_TemplateNameEmptyErrorMessage);
                } else if (!newName.equals(fInitialTemplateName) && templateNames.contains(newName)) {
                    nameValidationStatus.setError(
                            JavaUIMessages.GenerateToStringDialog_templateEdition_TemplateNameDuplicateErrorMessage);
                } else {
                    nameValidationStatus.setOK();
                }
                updateStatus(nameValidationStatus);
            }

            private String createNewTemplateName() {
                if (!templateNames.contains(JavaUIMessages.GenerateToStringDialog_newTemplateName))
                    return JavaUIMessages.GenerateToStringDialog_newTemplateName;

                int copyCount = 2;
                String newName;
                do {
                    newName = Messages.format(JavaUIMessages.GenerateToStringDialog_newTemplateNameArg,
                            new Integer(copyCount));
                    copyCount++;
                } while (templateNames.contains(newName));
                return newName;
            }

            @Override
            public boolean close() {
                resultTemplateName = templateName.getText();
                resultTemplate = fixLineBreaks(template.getText());
                return super.close();
            }

            public String getTemplateName() {
                return resultTemplateName;
            }

            public String getTemplate() {
                return resultTemplate;
            }

            private String fixLineBreaks(String input) {
                String systemLineDelimiter = Text.DELIMITER;
                final String javaLineDelimiter = "\n"; //$NON-NLS-1$
                if (!systemLineDelimiter.equals(javaLineDelimiter)) {
                    StringBuffer outputBuffer = new StringBuffer(input);
                    int pos = outputBuffer.indexOf(systemLineDelimiter);
                    while (pos >= 0) {
                        outputBuffer.delete(pos, pos + systemLineDelimiter.length());
                        outputBuffer.insert(pos, javaLineDelimiter);
                        pos = outputBuffer.indexOf(systemLineDelimiter, pos + javaLineDelimiter.length());
                    }
                    return outputBuffer.toString();
                }
                return input;
            }

        }

        private class ToStringTemplateProposalProvider implements IContentProposalProvider {
            private class Proposal implements IContentProposal {
                final private String proposal;

                private int position;

                public Proposal(String proposal) {
                    this.proposal = proposal;
                    this.position = proposal.length();
                }

                public String getContent() {
                    int overlap = stringOverlap(latestContents.substring(0, latestPosition), proposal);
                    position = proposal.length() - overlap;
                    return proposal.substring(overlap);
                }

                public int getCursorPosition() {
                    return position;
                }

                public String getDescription() {
                    return parser.getVariableDescriptions().get(proposal);
                }

                public String getLabel() {
                    return proposal;
                }
            }

            private String latestContents;

            private int latestPosition;

            public IContentProposal[] getProposals(String contents, int position) {
                List<Proposal> primaryProposals = new ArrayList<Proposal>();
                List<Proposal> secondaryProposals = new ArrayList<Proposal>();
                String[] proposalStrings = parser.getVariables();
                String contentToCursor = contents.substring(0, position);
                for (int i = 0; i < proposalStrings.length; i++) {
                    if (stringOverlap(contentToCursor, proposalStrings[i]) > 0)
                        primaryProposals.add(new Proposal(proposalStrings[i]));
                    else
                        secondaryProposals.add(new Proposal(proposalStrings[i]));
                }

                this.latestContents = contents;
                this.latestPosition = position;

                primaryProposals.addAll(secondaryProposals);
                return primaryProposals.toArray(new IContentProposal[0]);
            }

            /**
             * Checks if the end of the first string is equal to the beginning of of the second
             * string.
             * 
             * @param s1 first String
             * @param s2 second String
             * @return length of overlapping segment (0 if strings don't overlap)
             */
            private int stringOverlap(String s1, String s2) {
                int l1 = s1.length();
                for (int l = 1; l <= Math.min(s1.length(), s2.length()); l++) {
                    boolean ok = true;
                    for (int i = 0; i < l; i++) {
                        if (s1.charAt(l1 - l + i) != s2.charAt(i)) {
                            ok = false;
                            break;
                        }
                    }
                    if (ok)
                        return l;
                }
                return 0;
            }
        }

        private final int ADD_BUTTON = IDialogConstants.CLIENT_ID + 1;

        private final int REMOVE_BUTTON = IDialogConstants.CLIENT_ID + 2;

        private final int APPLY_BUTTON = IDialogConstants.CLIENT_ID + 3;

        private final int EDIT_BUTTON = IDialogConstants.CLIENT_ID + 4;

        private Text templateTextControl;

        private org.eclipse.swt.widgets.List templateNameControl;

        private ToStringTemplateParser parser;

        private List<String> templateNames;

        private List<String> templates;

        private int selectedTemplateNumber;

        private boolean somethingChanged = false;

        private StatusInfo validationStatus = new StatusInfo();

        protected ToStringTemplatesDialog(Shell parentShell, ToStringTemplateParser parser) {
            super(parentShell);
            this.parser = parser;
            this.setShellStyle(this.getShellStyle() | SWT.RESIZE);
            this.create();
            PlatformUI.getWorkbench().getHelpSystem().setHelp(getShell(),
                    IJavaHelpContextIds.GENERATE_TOSTRING_MANAGE_TEMPLATES_DIALOG);
        }

        @Override
        protected Control createDialogArea(Composite parent) {
            getShell().setText(JavaUIMessages.GenerateToStringDialog_templatesManagerTitle);

            Composite composite = (Composite) super.createDialogArea(parent);

            Label label = new Label(composite, SWT.LEFT);
            label.setText(JavaUIMessages.GenerateToStringDialog_templatesManagerTemplatesList);

            Composite templatesComposite = new Composite(composite, SWT.NONE);
            templatesComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
            GridLayout gl = new GridLayout(2, false);
            gl.marginWidth = gl.marginHeight = 0;
            templatesComposite.setLayout(gl);

            templateNameControl = new org.eclipse.swt.widgets.List(templatesComposite,
                    SWT.BORDER | SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL);
            templateNameControl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

            Composite rightComposite = new Composite(templatesComposite, SWT.NONE);
            gl = new GridLayout();
            gl.marginWidth = gl.marginHeight = 0;
            rightComposite.setLayout(gl);
            rightComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false));
            createButton(rightComposite, ADD_BUTTON,
                    JavaUIMessages.GenerateToStringDialog_templatesManagerNewButton, false);
            createButton(rightComposite, REMOVE_BUTTON,
                    JavaUIMessages.GenerateToStringDialog_templatesManagerRemoveButton, false);
            createButton(rightComposite, EDIT_BUTTON,
                    JavaUIMessages.GenerateToStringDialog_teplatesManagerEditButton, false);
            ((GridLayout) rightComposite.getLayout()).numColumns = 1;

            label = new Label(composite, SWT.LEFT);
            label.setText(JavaUIMessages.GenerateToStringDialog_templatesManagerPreview);

            templateTextControl = new Text(composite,
                    SWT.BORDER | SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.READ_ONLY);
            GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true);
            gd.heightHint = 80;
            gd.widthHint = 450;
            templateTextControl.setLayoutData(gd);

            templateNames = new ArrayList<String>(Arrays.asList(getTemplateNames()));
            templates = new ArrayList<String>(Arrays.asList(getTemplates(getDialogSettings())));
            selectedTemplateNumber = getGenerationSettings().stringFormatTemplateNumber;
            refreshControls();

            templateNameControl.addSelectionListener(new SelectionListener() {
                public void widgetDefaultSelected(SelectionEvent e) {
                    onEdit();
                }

                public void widgetSelected(SelectionEvent e) {
                    if (templateNameControl.getSelectionIndex() >= 0) {
                        selectedTemplateNumber = templateNameControl.getSelectionIndex();
                        templateTextControl.setText(templates.get(selectedTemplateNumber));
                    }
                }
            });

            applyDialogFont(composite);

            return composite;
        }

        @Override
        protected void createButtonsForButtonBar(Composite parent) {
            super.createButtonsForButtonBar(parent);
            createButton(parent, APPLY_BUTTON, JavaUIMessages.GenerateToStringDialog_templateManagerApplyButton,
                    false).setEnabled(false);
        }

        private void applyChanges() {
            getDialogSettings().put(ToStringGenerationSettings.SETTINGS_TEMPLATE_NAMES,
                    templateNames.toArray(new String[0]));
            getDialogSettings().put(ToStringGenerationSettings.SETTINGS_TEMPLATES,
                    templates.toArray(new String[0]));
            getGenerationSettings().stringFormatTemplateNumber = Math.max(selectedTemplateNumber, 0);
            somethingChanged = false;
            getButton(APPLY_BUTTON).setEnabled(false);
        }

        @Override
        protected void buttonPressed(int buttonId) {
            switch (buttonId) {
            case APPLY_BUTTON:
                applyChanges();
                break;
            case IDialogConstants.OK_ID:
                applyChanges();
                close();
                break;
            case IDialogConstants.CANCEL_ID:
                close();
                break;
            case ADD_BUTTON:
                TemplateEditionDialog dialog = new TemplateEditionDialog(getShell(), -1);
                dialog.open();
                if (dialog.getReturnCode() == IDialogConstants.OK_ID) {
                    templateNames.add(dialog.getTemplateName());
                    templates.add(dialog.getTemplate());
                    selectedTemplateNumber = templateNames.size() - 1;
                    somethingChanged = true;
                    refreshControls();
                }
                break;
            case REMOVE_BUTTON:
                if (templateNames.size() > 0) {
                    templateNames.remove(selectedTemplateNumber);
                    templates.remove(selectedTemplateNumber);
                }
                if (selectedTemplateNumber >= templateNames.size())
                    selectedTemplateNumber = templateNames.size() - 1;
                somethingChanged = true;
                refreshControls();
                break;
            case EDIT_BUTTON:
                onEdit();
            }
        }

        private void onEdit() {
            TemplateEditionDialog dialog;
            dialog = new TemplateEditionDialog(getShell(), selectedTemplateNumber);
            dialog.open();
            if (dialog.getReturnCode() == IDialogConstants.OK_ID) {
                templateNames.set(selectedTemplateNumber, dialog.getTemplateName());
                templates.set(selectedTemplateNumber, dialog.getTemplate());
                somethingChanged = true;
                refreshControls();
            }
        }

        public void refreshControls() {
            templateNameControl.setItems(templateNames.toArray(new String[0]));
            if (templateNames.size() > 0) {
                templateNameControl.select(selectedTemplateNumber);
                templateTextControl.setText(templates.get(selectedTemplateNumber));
            } else {
                templateTextControl.setText(""); //$NON-NLS-1$
            }
            revalidate();
            if (getButton(APPLY_BUTTON) != null)
                getButton(APPLY_BUTTON)
                        .setEnabled(somethingChanged && getButton(IDialogConstants.OK_ID).getEnabled());
            if (getButton(REMOVE_BUTTON) != null)
                getButton(REMOVE_BUTTON).setEnabled(templateNames.size() > 0);
            if (getButton(EDIT_BUTTON) != null)
                getButton(EDIT_BUTTON).setEnabled(templateNames.size() > 0);
        }

        private void revalidate() {
            if (templateNames.size() > 0)
                validationStatus.setOK();
            else
                validationStatus
                        .setError(JavaUIMessages.GenerateToStringDialog_templateManagerNoTemplateErrorMessage);
            updateStatus(validationStatus);
        }
    }

    private static class CustomBuilderValidator implements ISelectionStatusValidator {

        private final IJavaProject fJavaProject;

        private IType fLastValidBuilderType;

        private List<String> fLastValidAppendMethodSuggestions;

        private List<String> fLastValidResultMethodSuggestions;

        public CustomBuilderValidator(IJavaProject javaProject) {
            fJavaProject = javaProject;
        }

        public IStatus validateBuilderType(IType type) {
            if (fLastValidBuilderType != null && fLastValidBuilderType.equals(type)) {
                return new StatusInfo();
            }

            try {
                IMethod[] methods = type.getMethods();
                boolean foundConstructor = false;
                for (int i = 0; i < methods.length; i++) {
                    if (methods[i].isConstructor() && Flags.isPublic(methods[i].getFlags())) {
                        String[] parameterTypes = methods[i].getParameterTypes();
                        if (parameterTypes.length == 1 && "java.lang.Object" //$NON-NLS-1$
                                .equals(JavaModelUtil.getResolvedTypeName(parameterTypes[0], type))) {
                            foundConstructor = true;
                            break;
                        }
                    }
                }
                if (!foundConstructor)
                    return new StatusInfo(IStatus.ERROR,
                            JavaUIMessages.GenerateToStringDialog_customBuilderConfig_noConstructorError);

                List<String> appendMethodSuggestions = getAppendMethodSuggestions(type);
                if (appendMethodSuggestions.isEmpty())
                    return new StatusInfo(IStatus.ERROR,
                            JavaUIMessages.GenerateToStringDialog_customBuilderConfig_noAppendMethodError);

                List<String> resultMethodSuggestions = getResultMethodSuggestions(type);
                if (resultMethodSuggestions.isEmpty())
                    return new StatusInfo(IStatus.ERROR,
                            JavaUIMessages.GenerateToStringDialog_customBuilderConfig_noResultMethodError);

                fLastValidBuilderType = type;
                fLastValidAppendMethodSuggestions = appendMethodSuggestions;
                fLastValidResultMethodSuggestions = resultMethodSuggestions;
                return new StatusInfo();
            } catch (JavaModelException e1) {
                return new StatusInfo(IStatus.WARNING,
                        JavaUIMessages.GenerateToStringDialog_customBuilderConfig_typeValidationError);
            }
        }

        public IStatus validate(Object[] selection) {
            return validateBuilderType(((IType) selection[0]));
        }

        public IStatus revalidateAll(CustomBuilderSettings builderSettings) {
            try {
                if (builderSettings.className.length() == 0) {
                    return new StatusInfo(IStatus.ERROR,
                            JavaUIMessages.GenerateToStringDialog_customBuilderConfig_noBuilderClassError);
                }

                IType type = findType(builderSettings.className);

                if (type == null || !type.exists()) {
                    return new StatusInfo(IStatus.ERROR,
                            MessageFormat.format(
                                    JavaUIMessages.GenerateToStringDialog_customBuilderConfig_invalidClassError,
                                    new Object[] { builderSettings.className }));
                }

                IStatus typeValidation = validateBuilderType(type);
                if (!typeValidation.isOK())
                    return typeValidation;

                if (!getAppendMethodSuggestions(type).contains(builderSettings.appendMethod))
                    return new StatusInfo(IStatus.ERROR, MessageFormat.format(
                            JavaUIMessages.GenerateToStringDialog_customBuilderConfig_invalidAppendMethodError,
                            new Object[] { builderSettings.appendMethod }));

                if (!getResultMethodSuggestions(type).contains(builderSettings.resultMethod))
                    return new StatusInfo(IStatus.ERROR, MessageFormat.format(
                            JavaUIMessages.GenerateToStringDialog_customBuilderConfig_invalidResultMethodError,
                            new Object[] { builderSettings.resultMethod }));

                if (!isValidJavaIdentifier(builderSettings.variableName))
                    return new StatusInfo(IStatus.ERROR, MessageFormat.format(
                            JavaUIMessages.GenerateToStringDialog_customBuilderConfig_invalidVariableNameError,
                            new Object[] { builderSettings.variableName }));

            } catch (JavaModelException e) {
                return new StatusInfo(IStatus.WARNING,
                        JavaUIMessages.GenerateToStringDialog_customBuilderConfig_dataValidationError);
            }
            return new StatusInfo();
        }

        public IType findType(String builderClassName) throws JavaModelException {
            if (fLastValidBuilderType != null
                    && builderClassName.equals(fLastValidBuilderType.getFullyQualifiedParameterizedName())) {
                return fLastValidBuilderType;
            }

            return fJavaProject.findType(builderClassName, (IProgressMonitor) null);
        }

        public List<String> getAppendMethodSuggestions(final IType type) throws JavaModelException {
            if (fLastValidBuilderType != null && fLastValidBuilderType.equals(type)) {
                return fLastValidAppendMethodSuggestions;
            }
            return getMethodSuggestions(type, new MethodChecker() {
                public boolean isMethodOK(IMethod method) throws JavaModelException {
                    if (!Flags.isPublic(method.getFlags()) || method.isConstructor())
                        return false;
                    /* To be an append method, it must take exactly one
                     * Object parameter, and optionally one String parameter. */
                    String[] parameterTypes = method.getParameterTypes();
                    if (parameterTypes.length == 0 || parameterTypes.length > 2) {
                        return false;
                    }
                    int countObjects = 0, countStrings = 0;
                    for (int i = 0; i < parameterTypes.length; i++) {
                        String resolvedParameterTypeName = JavaModelUtil.getResolvedTypeName(parameterTypes[i],
                                type);
                        if ("java.lang.Object".equals(resolvedParameterTypeName))//$NON-NLS-1$
                            countObjects++;
                        if ("java.lang.String".equals(resolvedParameterTypeName))//$NON-NLS-1$
                            countStrings++;
                    }
                    return countObjects == 1 && countObjects + countStrings == parameterTypes.length;

                }
            });
        }

        public List<String> getResultMethodSuggestions(final IType type) throws JavaModelException {
            if (fLastValidBuilderType != null && fLastValidBuilderType.equals(type)) {
                return fLastValidResultMethodSuggestions;
            }
            return getMethodSuggestions(type, new MethodChecker() {
                public boolean isMethodOK(IMethod method) throws JavaModelException {
                    return Flags.isPublic(method.getFlags()) && method.getParameterTypes().length == 0
                            && "java.lang.String" //$NON-NLS-1$
                                    .equals(JavaModelUtil.getResolvedTypeName(method.getReturnType(), type));
                }
            });
        }

        private interface MethodChecker {
            boolean isMethodOK(IMethod method) throws JavaModelException;
        }

        private List<String> getMethodSuggestions(IType type, MethodChecker checker) throws JavaModelException {
            ArrayList<String> result = new ArrayList<String>();
            IType[] classes = type.newSupertypeHierarchy(null).getAllClasses();
            for (int i = 0; i < classes.length; i++) {
                IMethod[] methods = classes[i].getMethods();
                for (int j = 0; j < methods.length; j++) {
                    if (checker.isMethodOK(methods[j])) {
                        String name = methods[j].getElementName();
                        if (!result.contains(name))
                            result.add(name);
                    }
                }
            }
            return result;
        }

        private boolean isValidJavaIdentifier(String identifier) {
            return JavaConventionsUtil.validateIdentifier(identifier, fJavaProject).isOK();
        }
    }

    private class CustomBuilderConfigurationDialog extends StatusDialog {

        private final int APPLY_BUTTON = IDialogConstants.CLIENT_ID + 1;

        /**
         * Extension for class selection dialog - validates selected type
         */
        private final TypeSelectionExtension fExtension = new TypeSelectionExtension() {
            @Override
            public ISelectionStatusValidator getSelectionValidator() {
                return getValidator();
            }
        };

        /**
         * Listener for text fields - updates combos and validates entered data
         */
        private final ModifyListener modifyListener = new ModifyListener() {

            public void modifyText(ModifyEvent e) {
                if (e.widget == fBuilderClassName) {
                    fBuilderSettings.className = fBuilderClassName.getText();
                    updateCombos();
                } else if (e.widget == fBuilderVariableName)
                    fBuilderSettings.variableName = fBuilderVariableName.getText();

                IStatus status = getValidator().revalidateAll(fBuilderSettings);
                updateStatus(status);

                enableApplyButton();
            }
        };

        private final CustomBuilderValidator fValidator = new CustomBuilderValidator(getType().getJavaProject());

        private Text fBuilderClassName;

        private Text fBuilderVariableName;

        private Combo fAppendMethodName;

        private Combo fResultMethodName;

        private Button fChainInvocations;

        private ToStringGenerationSettings.CustomBuilderSettings fBuilderSettings;

        private boolean somethingChanged = false;

        public CustomBuilderConfigurationDialog(Shell parent) {
            super(parent);
            this.setShellStyle(this.getShellStyle() | SWT.RESIZE);
            fBuilderSettings = getGenerationSettings().getCustomBuilderSettings();
        }

        public CustomBuilderValidator getValidator() {
            return fValidator;
        }

        @Override
        protected Control createDialogArea(Composite parent) {
            getShell().setText(JavaUIMessages.GenerateToStringDialog_customBuilderConfig_windowTitle);

            Composite composite = (Composite) super.createDialogArea(parent);
            ((GridLayout) composite.getLayout()).numColumns = 3;
            LayoutUtil.setWidthHint(composite, convertWidthInCharsToPixels(100));

            Label label = new Label(composite, SWT.LEFT);
            label.setText(JavaUIMessages.GenerateToStringDialog_customBuilderConfig_builderClassField);
            fBuilderClassName = createTextField(composite, 1, fBuilderSettings.className);

            Button button = new Button(composite, SWT.NONE);
            button.setText(JavaUIMessages.GenerateToStringDialog_customBuilderConfig_browseButton);
            setButtonLayoutData(button);
            button.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(SelectionEvent e) {
                    browseForBuilderClass();
                }
            });

            label = new Label(composite, SWT.LEFT);
            label.setText(JavaUIMessages.GenerateToStringDialog_customBuilderConfig_varNameField);
            fBuilderVariableName = createTextField(composite, 2, fBuilderSettings.variableName);

            label = new Label(composite, SWT.LEFT);
            label.setText(JavaUIMessages.GenerateToStringDialog_customBuilderConfig_appendMethodField);
            fAppendMethodName = new Combo(composite, SWT.READ_ONLY);
            fAppendMethodName.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));

            label = new Label(composite, SWT.LEFT);
            label.setText(JavaUIMessages.GenerateToStringDialog_customBuilderConfig_resultMethodField);
            fResultMethodName = new Combo(composite, SWT.READ_ONLY);
            fResultMethodName.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));

            updateCombos();
            ModifyListener comboListener = new ModifyListener() {
                public void modifyText(ModifyEvent e) {
                    Combo c = (Combo) e.widget;
                    if (c.getText().length() > 0) {
                        if (c == fAppendMethodName)
                            fBuilderSettings.appendMethod = c.getText();
                        if (c == fResultMethodName)
                            fBuilderSettings.resultMethod = c.getText();
                    }
                    updateStatus(fValidator.revalidateAll(fBuilderSettings));
                    enableApplyButton();
                }
            };
            fAppendMethodName.addModifyListener(comboListener);
            fResultMethodName.addModifyListener(comboListener);
            if (!select(fAppendMethodName, fBuilderSettings.appendMethod)) {
                fAppendMethodName.select(0);
            }
            if (!select(fResultMethodName, fBuilderSettings.resultMethod)) {
                fResultMethodName.select(0);
            }

            fChainInvocations = new Button(composite, SWT.CHECK);
            fChainInvocations
                    .setText(JavaUIMessages.GenerateToStringDialog_customBuilderConfig_chainedCallsCheckbox);
            fChainInvocations.setSelection(fBuilderSettings.chainCalls);
            fChainInvocations.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1));
            fChainInvocations.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(SelectionEvent e) {
                    fBuilderSettings.chainCalls = fChainInvocations.getSelection();
                    enableApplyButton();
                }
            });

            PlatformUI.getWorkbench().getHelpSystem().setHelp(getShell(),
                    IJavaHelpContextIds.GENERATE_TOSTRING_CONFIGURE_CUSTOM_BUILDER_DIALOG);

            return composite;
        }

        @Override
        public void create() {
            super.create();
            IStatus status = getValidator().revalidateAll(fBuilderSettings);
            updateStatus(status);
        }

        @Override
        protected void createButtonsForButtonBar(Composite parent) {
            super.createButtonsForButtonBar(parent);
            createButton(parent, APPLY_BUTTON,
                    JavaUIMessages.GenerateToStringDialog_customBuilderConfig_applyButton, false).setEnabled(false);
        }

        private void enableApplyButton() {
            somethingChanged = true;
            getButton(APPLY_BUTTON).setEnabled(!getStatus().matches(IStatus.ERROR));
        }

        @Override
        protected void updateButtonsEnableState(IStatus status) {
            super.updateButtonsEnableState(status);
            getButton(APPLY_BUTTON).setEnabled(!status.matches(IStatus.ERROR) && somethingChanged);
        }

        @Override
        protected void buttonPressed(int buttonId) {
            switch (buttonId) {
            case APPLY_BUTTON:
                getButton(APPLY_BUTTON).setEnabled(false);
                somethingChanged = false;
                //$FALL-THROUGH$
            case OK:
                applyChanges();
            }
            super.buttonPressed(buttonId);
        }

        private boolean select(Combo combo, String item) {
            int index = Arrays.asList(combo.getItems()).indexOf(item);
            if (index >= 0) {
                combo.select(index);
                return true;
            }
            return false;
        }

        private void updateCombos() {
            final String[] empty = new String[0];
            try {
                IType type = fValidator.findType(fBuilderSettings.className);
                if (type == null) {
                    fAppendMethodName.setItems(empty);
                    fResultMethodName.setItems(empty);
                } else {
                    fAppendMethodName.setItems(fValidator.getAppendMethodSuggestions(type).toArray(empty));
                    select(fAppendMethodName, fBuilderSettings.appendMethod);
                    fResultMethodName.setItems(fValidator.getResultMethodSuggestions(type).toArray(empty));
                    select(fResultMethodName, fBuilderSettings.resultMethod);
                }
            } catch (JavaModelException e1) {
                fAppendMethodName.setItems(empty);
                fResultMethodName.setItems(empty);
            }
        }

        private void applyChanges() {
            fBuilderSettings.appendMethod = fAppendMethodName.getText();
            fBuilderSettings.resultMethod = fResultMethodName.getText();
            getGenerationSettings().writeCustomBuilderSettings(fBuilderSettings);
        }

        private Text createTextField(Composite composite, int gridHSpan, String text) {
            Text result = new Text(composite, SWT.BORDER);
            result.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, gridHSpan, 1));
            result.setText(text);
            result.addModifyListener(modifyListener);
            TextFieldNavigationHandler.install(result);
            return result;
        }

        private void browseForBuilderClass() {
            try {
                IJavaSearchScope scope = SearchEngine
                        .createJavaSearchScope(new IJavaElement[] { getType().getJavaProject() });
                SelectionDialog dialog = JavaUI.createTypeDialog(getShell(),
                        PlatformUI.getWorkbench().getProgressService(), scope,
                        IJavaElementSearchConstants.CONSIDER_CLASSES, false, "*ToString", fExtension); //$NON-NLS-1$
                dialog.setTitle(
                        JavaUIMessages.GenerateToStringDialog_customBuilderConfig_classSelection_windowTitle);
                dialog.setMessage(JavaUIMessages.GenerateToStringDialog_customBuilderConfig_classSelection_message);
                dialog.open();
                if (dialog.getReturnCode() == OK) {
                    IType type = (IType) dialog.getResult()[0];
                    fBuilderClassName.setText(type.getFullyQualifiedParameterizedName());
                    List<String> suggestions = fValidator.getAppendMethodSuggestions(type);
                    if (!suggestions.contains(fAppendMethodName.getText()))
                        fAppendMethodName.setText(suggestions.get(0));
                    suggestions = fValidator.getResultMethodSuggestions(type);
                    if (!suggestions.contains(fResultMethodName.getText()))
                        fResultMethodName.setText(suggestions.get(0));
                }
            } catch (JavaModelException e) {
                JavaPlugin.log(e);
            }
        }
    }

    private ToStringGenerationSettings fGenerationSettings;

    private static final int DOWN_BUTTON = IDialogConstants.CLIENT_ID + 2;

    private static final int UP_BUTTON = IDialogConstants.CLIENT_ID + 1;

    private static final int SORT_BUTTON = IDialogConstants.CLIENT_ID + 3;

    protected Button[] fButtonControls;

    boolean[] fButtonsEnabled;

    private static final int DOWN_INDEX = 1;

    private static final int UP_INDEX = 0;

    public ToStringGenerationSettings getGenerationSettings() {
        return fGenerationSettings;
    }

    public static String[] getTemplates(IDialogSettings dialogSettings) {
        String[] result = dialogSettings.getArray(ToStringGenerationSettings.SETTINGS_TEMPLATES);
        if (result != null && result.length > 0)
            return result;
        return new String[] { ToStringTemplateParser.DEFAULT_TEMPLATE };
    }

    public String[] getTemplateNames() {
        String[] result = getDialogSettings().getArray(ToStringGenerationSettings.SETTINGS_TEMPLATE_NAMES);
        if (result != null && result.length > 0)
            return result;
        return new String[] { JavaUIMessages.GenerateToStringDialog_defaultTemplateName };
    }

    public int getSelectedTemplate() {
        try {
            int result = getDialogSettings().getInt(ToStringGenerationSettings.SETTINGS_SELECTED_TEMPLATE);
            if (result < 0)
                return 0;
            return result;
        } catch (NumberFormatException e) {
            return 0;
        }
    }

    public void setSelectedTemplate(int templateNumber) {
        getDialogSettings().put(ToStringGenerationSettings.SETTINGS_SELECTED_TEMPLATE, templateNumber);
    }

    public GenerateToStringDialog(Shell shell, CompilationUnitEditor editor, IType type, IVariableBinding[] fields,
            IVariableBinding[] inheritedFields, IVariableBinding[] selectedFields, IMethodBinding[] methods,
            IMethodBinding[] inheritededMethods) throws JavaModelException {
        super(shell, new BindingLabelProvider(),
                new GenerateToStringContentProvider(fields, inheritedFields, methods, inheritededMethods), editor,
                type, false);
        setEmptyListMessage(JavaUIMessages.GenerateHashCodeEqualsDialog_no_entries);

        List<Object> selected = new ArrayList<Object>(Arrays.asList(selectedFields));
        if (selectedFields.length == fields.length && selectedFields.length > 0)
            selected.add(getContentProvider().getParent(selectedFields[0]));
        setInitialElementSelections(selected);

        setTitle(JavaUIMessages.GenerateToStringDialog_dialog_title);
        setMessage(JavaUIMessages.GenerateToStringDialog_select_fields_to_include);
        setValidator(new GenerateToStringValidator(fields.length + inheritedFields.length,
                methods.length + inheritededMethods.length));
        setSize(60, 18);
        setInput(new Object());

        fGenerationSettings = new ToStringGenerationSettings(getDialogSettings());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean close() {
        fGenerationSettings.writeDialogSettings();

        fGenerationSettings.stringFormatTemplate = getTemplates(
                getDialogSettings())[fGenerationSettings.stringFormatTemplateNumber];

        fGenerationSettings.createComments = getGenerateComment();

        return super.close();
    }

    @Override
    public Object[] getResult() {
        Object[] oldResult = super.getResult();
        List<Object> newResult = new ArrayList<Object>();
        for (int i = 0; i < oldResult.length; i++) {
            if (!(oldResult[i] instanceof String)) {
                newResult.add(oldResult[i]);
            }
        }
        return newResult.toArray();
    }

    @Override
    protected void configureShell(Shell shell) {
        super.configureShell(shell);
        PlatformUI.getWorkbench().getHelpSystem().setHelp(shell,
                IJavaHelpContextIds.GENERATE_TOSTRING_SELECTION_DIALOG);
    }

    @Override
    protected CheckboxTreeViewer createTreeViewer(Composite parent) {
        CheckboxTreeViewer treeViewer = super.createTreeViewer(parent);

        treeViewer.setLabelProvider(new GenerateToStringLabelProvider());

        //expandAll because setSubtreeChecked() used in CheckStateListener below assumes that elements have been expanded
        treeViewer.expandAll();
        //but actually we only need one branch expanded
        treeViewer.collapseAll();
        treeViewer.expandToLevel(GenerateToStringContentProvider.fieldsNode, 1);

        treeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
            public void selectionChanged(SelectionChangedEvent event) {
                IStructuredSelection selection = (IStructuredSelection) getTreeViewer().getSelection();

                Object selected = selection.size() > 0 ? selection.toList().get(0) : null;
                GenerateToStringContentProvider cp = (GenerateToStringContentProvider) getContentProvider();

                fButtonControls[UP_INDEX].setEnabled(cp.canMoveUp(selected));
                fButtonControls[DOWN_INDEX].setEnabled(cp.canMoveDown(selected));
            }

        });
        treeViewer.addCheckStateListener(new ICheckStateListener() {
            public void checkStateChanged(CheckStateChangedEvent event) {
                getTreeViewer().setSubtreeChecked(event.getElement(), event.getChecked());
                getTreeViewer().setGrayed(event.getElement(), false);
                Object parentElement = ((ITreeContentProvider) (getTreeViewer().getContentProvider()))
                        .getParent(event.getElement());
                if (parentElement != null) {
                    Object[] siblings = ((ITreeContentProvider) (getTreeViewer().getContentProvider()))
                            .getChildren(parentElement);
                    int count = 0;
                    for (int i = 0; i < siblings.length; i++) {
                        if (getTreeViewer().getChecked(siblings[i]))
                            count++;
                    }
                    if (count == 0)
                        getTreeViewer().setGrayChecked(parentElement, false);
                    else if (count == siblings.length) {
                        getTreeViewer().setChecked(parentElement, true);
                        getTreeViewer().setGrayed(parentElement, false);
                    } else
                        getTreeViewer().setGrayChecked(parentElement, true);
                }
                updateOKStatus();
            }

        });
        return treeViewer;
    }

    @Override
    protected Composite createSelectionButtons(Composite composite) {
        Composite buttonComposite = super.createSelectionButtons(composite);

        GridLayout layout = new GridLayout();
        buttonComposite.setLayout(layout);

        createUpDownButtons(buttonComposite);

        createButton(buttonComposite, SORT_BUTTON, JavaUIMessages.GenerateToStringDialog_sort_button, false);

        layout.marginHeight = 0;
        layout.marginWidth = 0;
        layout.numColumns = 1;

        return buttonComposite;
    }

    @Override
    protected void buttonPressed(int buttonId) {
        super.buttonPressed(buttonId);
        switch (buttonId) {
        case UP_BUTTON: {
            GenerateToStringContentProvider contentProvider = (GenerateToStringContentProvider) getTreeViewer()
                    .getContentProvider();
            List<?> selection = ((IStructuredSelection) getTreeViewer().getSelection()).toList();
            if (selection.size() > 0)
                contentProvider.up(selection.get(0), getTreeViewer());
            updateOKStatus();
            break;
        }
        case DOWN_BUTTON: {
            GenerateToStringContentProvider contentProvider = (GenerateToStringContentProvider) getTreeViewer()
                    .getContentProvider();
            List<?> selection = ((IStructuredSelection) getTreeViewer().getSelection()).toList();
            if (selection.size() > 0)
                contentProvider.down(selection.get(0), getTreeViewer());
            updateOKStatus();
            break;
        }
        case SORT_BUTTON: {
            GenerateToStringContentProvider contentProvider = (GenerateToStringContentProvider) getTreeViewer()
                    .getContentProvider();
            contentProvider.sort();
            getTreeViewer().refresh();
            updateOKStatus();
            break;
        }
        }
    }

    protected void createUpDownButtons(Composite buttonComposite) {
        int numButtons = 2; // up, down
        fButtonControls = new Button[numButtons];
        fButtonsEnabled = new boolean[numButtons];
        fButtonControls[UP_INDEX] = createButton(buttonComposite, UP_BUTTON,
                JavaUIMessages.GenerateToStringDialog_up_button, false);
        fButtonControls[DOWN_INDEX] = createButton(buttonComposite, DOWN_BUTTON,
                JavaUIMessages.GenerateToStringDialog_down_button, false);
        boolean defaultState = false;
        fButtonControls[UP_INDEX].setEnabled(defaultState);
        fButtonControls[DOWN_INDEX].setEnabled(defaultState);
        fButtonsEnabled[UP_INDEX] = defaultState;
        fButtonsEnabled[DOWN_INDEX] = defaultState;
    }

    private Label formatLabel;

    private Combo formatCombo;

    private Button skipNullsButton;

    private Button styleButton;

    @Override
    protected Composite createCommentSelection(Composite parentComposite) {
        Composite composite = super.createCommentSelection(parentComposite);

        Group group = new Group(parentComposite, SWT.NONE);
        group.setText(JavaUIMessages.GenerateToStringDialog_generated_code_group);
        GridLayout groupLayout = new GridLayout();
        group.setLayout(groupLayout);
        group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));

        Composite composite2 = new Composite(group, SWT.NONE);
        GridLayout layout = new GridLayout(3, false);
        layout.marginWidth = 0;
        layout.marginHeight = 0;
        composite2.setLayout(layout);
        composite2.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));

        formatLabel = new Label(composite2, SWT.NONE);
        formatLabel.setText(JavaUIMessages.GenerateToStringDialog_string_format_combo);
        GridData gridData = new GridData(SWT.FILL, SWT.CENTER, false, false);
        formatLabel.setLayoutData(gridData);

        formatCombo = new Combo(composite2, SWT.READ_ONLY);
        formatCombo.setItems(getTemplateNames());
        formatCombo.select(fGenerationSettings.stringFormatTemplateNumber);
        formatCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
        SWTUtil.setDefaultVisibleItemCount(formatCombo);
        formatCombo.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                fGenerationSettings.stringFormatTemplateNumber = ((Combo) e.widget).getSelectionIndex();
            }
        });

        final Button formatButton = new Button(composite2, SWT.NONE);
        formatButton.setText(JavaUIMessages.GenerateToStringDialog_manage_templates_button);
        setButtonLayoutData(formatButton);
        formatButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                manageTemplatesButtonSelected();
            }
        });

        final Label styleLabel = new Label(composite2, SWT.NONE);
        styleLabel.setText(JavaUIMessages.GenerateToStringDialog_code_style_combo);
        gridData = new GridData(SWT.FILL, SWT.CENTER, false, false);
        styleLabel.setLayoutData(gridData);

        final Combo styleCombo = new Combo(composite2, SWT.READ_ONLY);
        styleCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
        styleCombo.setItems(GenerateToStringOperation.getStyleNames());
        styleCombo.select(Math.min(fGenerationSettings.toStringStyle, styleCombo.getItemCount() - 1));
        SWTUtil.setDefaultVisibleItemCount(styleCombo);
        styleCombo.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                changeToStringStyle(((Combo) e.widget).getSelectionIndex());
            }
        });

        styleButton = new Button(composite2, SWT.NONE);
        styleButton.setText(JavaUIMessages.GenerateToStringDialog_codeStyleConfigureButton);
        setButtonLayoutData(styleButton);
        styleButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                configureStyleButtonSelected();
            }
        });

        skipNullsButton = new Button(group, SWT.CHECK);
        skipNullsButton.setText(JavaUIMessages.GenerateToStringDialog_skip_null_button);
        skipNullsButton.setSelection(fGenerationSettings.skipNulls);
        skipNullsButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent event) {
                fGenerationSettings.skipNulls = ((Button) event.widget).getSelection();
            }
        });

        final Button arrayButton = new Button(group, SWT.CHECK);
        arrayButton.setText(JavaUIMessages.GenerateToStringDialog_ignore_default_button);
        arrayButton.setSelection(fGenerationSettings.customArrayToString);
        arrayButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                fGenerationSettings.customArrayToString = ((Button) e.widget).getSelection();
            }
        });

        Composite limitRow = new Composite(group, SWT.NONE);
        RowLayout rowLayout = new RowLayout();
        rowLayout.center = true;
        rowLayout.marginLeft = rowLayout.marginRight = rowLayout.marginTop = rowLayout.marginBottom = 0;
        limitRow.setLayout(rowLayout);

        final Button limitButton = new Button(limitRow, SWT.CHECK);
        limitButton.setText(JavaUIMessages.GenerateToStringDialog_limit_elements_button);
        limitButton.setSelection(fGenerationSettings.limitElements);
        limitButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                fGenerationSettings.limitElements = ((Button) e.widget).getSelection();
            }
        });

        final Spinner limitSpinner = new Spinner(limitRow, SWT.BORDER);
        limitSpinner.setMinimum(0);
        limitSpinner.setSelection(fGenerationSettings.limitValue);
        limitSpinner.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                fGenerationSettings.limitValue = ((Spinner) e.widget).getSelection();
            }
        });

        //invoked to change initial enable state of controls
        changeToStringStyle(styleCombo.getSelectionIndex());

        return composite;
    }

    private void manageTemplatesButtonSelected() {
        ToStringTemplatesDialog dialog = new ToStringTemplatesDialog(getShell(),
                GenerateToStringOperation.createTemplateParser(fGenerationSettings.toStringStyle));
        dialog.open();
        formatCombo.setItems(getTemplateNames());
        formatCombo
                .select(Math.min(fGenerationSettings.stringFormatTemplateNumber, formatCombo.getItemCount() - 1));
    }

    private void configureStyleButtonSelected() {
        CustomBuilderConfigurationDialog dialog = new CustomBuilderConfigurationDialog(getShell());
        dialog.open();
        updateOKStatus();
    }

    private void changeToStringStyle(int style) {
        fGenerationSettings.toStringStyle = style;
        skipNullsButton.setEnabled(style != GenerateToStringOperation.STRING_FORMAT);
        boolean enableFormat = (style != GenerateToStringOperation.CUSTOM_BUILDER);
        formatLabel.setEnabled(enableFormat);
        formatCombo.setEnabled(enableFormat);
        styleButton.setEnabled(style == GenerateToStringOperation.CUSTOM_BUILDER);
        updateOKStatus();
    }

}