package org.bndtools.core.ui.wizards.ds;

import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IBuffer;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaConventions;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.ToolFactory;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.compiler.IScanner;
import org.eclipse.jdt.core.compiler.ITerminalSymbols;
import org.eclipse.jdt.core.compiler.InvalidInputException;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.ImportDeclaration;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
import org.eclipse.jdt.core.formatter.CodeFormatter;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.internal.corext.CorextMessages;
import org.eclipse.jdt.internal.corext.ValidateEditException;
import org.eclipse.jdt.internal.corext.codemanipulation.AddUnimplementedConstructorsOperation;
import org.eclipse.jdt.internal.corext.codemanipulation.AddUnimplementedMethodsOperation;
import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings;
import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.jdt.internal.corext.dom.TokenScanner;
import org.eclipse.jdt.internal.corext.refactoring.StubTypeContext;
import org.eclipse.jdt.internal.corext.refactoring.TypeContextChecker;
import org.eclipse.jdt.internal.corext.template.java.JavaContext;
import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil;
import org.eclipse.jdt.internal.corext.util.JavaConventionsUtil;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
import org.eclipse.jdt.internal.corext.util.Messages;
import org.eclipse.jdt.internal.corext.util.Resources;
import org.eclipse.jdt.internal.corext.util.Strings;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.JavaPluginImages;
import org.eclipse.jdt.internal.ui.dialogs.FilteredTypesSelectionDialog;
import org.eclipse.jdt.internal.ui.dialogs.StatusInfo;
import org.eclipse.jdt.internal.ui.dialogs.TableTextCellEditor;
import org.eclipse.jdt.internal.ui.dialogs.TextFieldNavigationHandler;
import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider;
import org.eclipse.jdt.internal.ui.preferences.CodeTemplatePreferencePage;
import org.eclipse.jdt.internal.ui.preferences.JavaPreferencesSettings;
import org.eclipse.jdt.internal.ui.refactoring.contentassist.CompletionContextRequestor;
import org.eclipse.jdt.internal.ui.refactoring.contentassist.ControlContentAssistHelper;
import org.eclipse.jdt.internal.ui.refactoring.contentassist.JavaPackageCompletionProcessor;
import org.eclipse.jdt.internal.ui.refactoring.contentassist.JavaTypeCompletionProcessor;
import org.eclipse.jdt.internal.ui.util.SWTUtil;
import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
import org.eclipse.jdt.internal.ui.wizards.NewWizardMessages;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.DialogField;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.IDialogFieldListener;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.IListAdapter;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.IStringButtonAdapter;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.LayoutUtil;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.ListDialogField;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.SelectionButtonDialogField;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.SelectionButtonDialogFieldGroup;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.Separator;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.StringButtonDialogField;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.StringButtonStatusDialogField;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.StringDialogField;
import org.eclipse.jdt.ui.CodeGeneration;
import org.eclipse.jdt.ui.JavaElementLabelProvider;
import org.eclipse.jdt.ui.wizards.NewContainerWizardPage;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.preference.PreferenceDialog;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.templates.Template;
import org.eclipse.jface.text.templates.TemplateException;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
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.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Text;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.ui.dialogs.ElementListSelectionDialog;
import org.eclipse.ui.dialogs.PreferencesUtil;

/**
 * This is copied from {@link org.eclipse.jdt.ui.wizards.NewTypeWizardPage} but with one or two methods opened up for
 * extensibility.
 *
 * @see org.eclipse.jdt.ui.wizards.NewTypeWizardPage
 */
public abstract class NewTypeWizardPage extends NewContainerWizardPage {

    /**
     * Class used in stub creation routines to add needed imports to a compilation unit.
     */
    public static class ImportsManager {

        private final ImportRewrite fImportsRewrite;

        /* package */ ImportsManager(CompilationUnit astRoot) {
            fImportsRewrite = StubUtility.createImportRewrite(astRoot, true);
        }

        /* package */ICompilationUnit getCompilationUnit() {
            return fImportsRewrite.getCompilationUnit();
        }

        /**
         * Adds a new import declaration that is sorted in the existing imports. If an import already exists or the
         * import would conflict with an import of an other type with the same simple name, the import is not added.
         *
         * @param qualifiedTypeName The fully qualified name of the type to import (dot separated).
         * @return Returns the simple type name that can be used in the code or the fully qualified type name if an
         *         import conflict prevented the import.
         */
        public String addImport(String qualifiedTypeName) {
            return fImportsRewrite.addImport(qualifiedTypeName);
        }

        /**
         * Adds a new import declaration that is sorted in the existing imports. If an import already exists or the
         * import would conflict with an import of an other type with the same simple name, the import is not added.
         *
         * @param typeBinding the binding of the type to import
         * @return Returns the simple type name that can be used in the code or the fully qualified type name if an
         *         import conflict prevented the import.
         */
        public String addImport(ITypeBinding typeBinding) {
            return fImportsRewrite.addImport(typeBinding);
        }

        /**
         * Adds a new import declaration for a static type that is sorted in the existing imports. If an import already
         * exists or the import would conflict with an import of an other static import with the same simple name, the
         * import is not added.
         *
         * @param declaringTypeName The qualified name of the static's member declaring type
         * @param simpleName the simple name of the member; either a field or a method name.
         * @param isField <code>true</code> specifies that the member is a field, <code>false</code> if it is a method.
         * @return returns either the simple member name if the import was successful or else the qualified name if an
         *         import conflict prevented the import.
         * @since 3.2
         */
        public String addStaticImport(String declaringTypeName, String simpleName, boolean isField) {
            return fImportsRewrite.addStaticImport(declaringTypeName, simpleName, isField);
        }

        /* package */void create(boolean needsSave, IProgressMonitor monitor) throws CoreException {
            TextEdit edit = fImportsRewrite.rewriteImports(monitor);
            ICompilationUnit cu = fImportsRewrite.getCompilationUnit();
            SubMonitor subMonitor = SubMonitor.convert(null, CorextMessages.JavaModelUtil_applyedit_operation, 2);
            IFile file = (IFile) cu.getResource();
            if (!needsSave || !file.exists()) {
                cu.applyTextEdit(edit, subMonitor.split(2));
            } else {
                IStatus status = Resources.makeCommittable(file, null);
                if (!status.isOK()) {
                    throw new ValidateEditException(status);
                }
                cu.applyTextEdit(edit, subMonitor.split(1));
                cu.save(subMonitor.split(1), true);
            }
        }

        /* package */void removeImport(String qualifiedName) {
            fImportsRewrite.removeImport(qualifiedName);
        }

        /* package */void removeStaticImport(String qualifiedName) {
            fImportsRewrite.removeStaticImport(qualifiedName);
        }
    }

    /** Public access flag. See The Java Virtual Machine Specification for more details. */
    public int F_PUBLIC = Flags.AccPublic;
    /** Private access flag. See The Java Virtual Machine Specification for more details. */
    public int F_PRIVATE = Flags.AccPrivate;
    /** Protected access flag. See The Java Virtual Machine Specification for more details. */
    public int F_PROTECTED = Flags.AccProtected;
    /** Static access flag. See The Java Virtual Machine Specification for more details. */
    public int F_STATIC = Flags.AccStatic;
    /** Final access flag. See The Java Virtual Machine Specification for more details. */
    public int F_FINAL = Flags.AccFinal;
    /** Abstract property flag. See The Java Virtual Machine Specification for more details. */
    public int F_ABSTRACT = Flags.AccAbstract;

    private final static String PAGE_NAME = "NewTypeWizardPage"; //$NON-NLS-1$

    /** Field ID of the package input field. */
    protected final static String PACKAGE = PAGE_NAME + ".package"; //$NON-NLS-1$
    /** Field ID of the enclosing type input field. */
    protected final static String ENCLOSING = PAGE_NAME + ".enclosing"; //$NON-NLS-1$
    /** Field ID of the enclosing type checkbox. */
    protected final static String ENCLOSINGSELECTION = ENCLOSING + ".selection"; //$NON-NLS-1$
    /** Field ID of the type name input field. */
    protected final static String TYPENAME = PAGE_NAME + ".typename"; //$NON-NLS-1$
    /** Field ID of the super type input field. */
    protected final static String SUPER = PAGE_NAME + ".superclass"; //$NON-NLS-1$
    /** Field ID of the super interfaces input field. */
    protected final static String INTERFACES = PAGE_NAME + ".interfaces"; //$NON-NLS-1$
    /** Field ID of the modifier check boxes. */
    protected final static String MODIFIERS = PAGE_NAME + ".modifiers"; //$NON-NLS-1$
    /** Field ID of the method stubs check boxes. */
    protected final static String METHODS = PAGE_NAME + ".methods"; //$NON-NLS-1$

    private static class InterfaceWrapper {
        public String interfaceName;

        public InterfaceWrapper(String interfaceName) {
            this.interfaceName = interfaceName;
        }

        @Override
        public int hashCode() {
            return interfaceName.hashCode();
        }

        @Override
        public boolean equals(Object obj) {
            return obj != null && getClass().equals(obj.getClass()) && ((InterfaceWrapper) obj).interfaceName.equals(interfaceName);
        }
    }

    private static class InterfacesListLabelProvider extends LabelProvider {
        private final Image fInterfaceImage;

        public InterfacesListLabelProvider() {
            fInterfaceImage = JavaPluginImages.get(JavaPluginImages.IMG_OBJS_INTERFACE);
        }

        @Override
        public String getText(Object element) {
            return BasicElementLabels.getJavaElementName(((InterfaceWrapper) element).interfaceName);
        }

        @Override
        public Image getImage(Object element) {
            return fInterfaceImage;
        }
    }

    private final StringButtonStatusDialogField fPackageDialogField;

    private final SelectionButtonDialogField fEnclosingTypeSelection;
    private final StringButtonDialogField fEnclosingTypeDialogField;

    private boolean fCanModifyPackage;
    private boolean fCanModifyEnclosingType;

    private IPackageFragment fCurrPackage;

    private IType fCurrEnclosingType;
    /**
     * a handle to the type to be created (does usually not exist, can be null)
     */
    private IType fCurrType;
    private final StringDialogField fTypeNameDialogField;

    private final StringButtonDialogField fSuperClassDialogField;
    private final ListDialogField<InterfaceWrapper> fSuperInterfacesDialogField;

    private final SelectionButtonDialogFieldGroup fAccMdfButtons;
    private final SelectionButtonDialogFieldGroup fOtherMdfButtons;

    private final SelectionButtonDialogField fAddCommentButton;
    private boolean fUseAddCommentButtonValue; // used for compatibility: Wizards that don't show the comment button
                                               // control
    // will use the preferences settings

    private IType fCreatedType;

    private final JavaPackageCompletionProcessor fCurrPackageCompletionProcessor;
    private final JavaTypeCompletionProcessor fEnclosingTypeCompletionProcessor;
    private StubTypeContext fSuperClassStubTypeContext;
    private StubTypeContext fSuperInterfaceStubTypeContext;

    protected IStatus fEnclosingTypeStatus;
    protected IStatus fPackageStatus;
    protected IStatus fTypeNameStatus;
    protected IStatus fSuperClassStatus;
    protected IStatus fModifierStatus;
    protected IStatus fSuperInterfacesStatus;

    private static final int PUBLIC_INDEX = 0, DEFAULT_INDEX = 1, PRIVATE_INDEX = 2, PROTECTED_INDEX = 3;
    private static final int ABSTRACT_INDEX = 0, FINAL_INDEX = 1, STATIC_INDEX = 2, ENUM_ANNOT_STATIC_INDEX = 1;

    private final int fTypeKind;

    /**
     * Constant to signal that the created type is a class.
     *
     * @since 3.1
     */
    public static final int CLASS_TYPE = 1;

    /**
     * Constant to signal that the created type is a interface.
     *
     * @since 3.1
     */
    public static final int INTERFACE_TYPE = 2;

    /**
     * Constant to signal that the created type is an enum.
     *
     * @since 3.1
     */
    public static final int ENUM_TYPE = 3;

    /**
     * Constant to signal that the created type is an annotation.
     *
     * @since 3.1
     */
    public static final int ANNOTATION_TYPE = 4;

    /**
     * Creates a new <code>NewTypeWizardPage</code>.
     *
     * @param isClass <code>true</code> if a new class is to be created; otherwise an interface is to be created
     * @param pageName the wizard page's name
     */
    public NewTypeWizardPage(boolean isClass, String pageName) {
        this(isClass ? CLASS_TYPE : INTERFACE_TYPE, pageName);
    }

    /**
     * Creates a new <code>NewTypeWizardPage</code>.
     *
     * @param typeKind Signals the kind of the type to be created. Valid kinds are {@link #CLASS_TYPE},
     *            {@link #INTERFACE_TYPE}, {@link #ENUM_TYPE} and {@link #ANNOTATION_TYPE}
     * @param pageName the wizard page's name
     * @since 3.1
     */
    public NewTypeWizardPage(int typeKind, String pageName) {
        super(pageName);
        fTypeKind = typeKind;

        fCreatedType = null;

        TypeFieldsAdapter adapter = new TypeFieldsAdapter();

        fPackageDialogField = new StringButtonStatusDialogField(adapter);
        fPackageDialogField.setDialogFieldListener(adapter);
        fPackageDialogField.setLabelText(getPackageLabel());
        fPackageDialogField.setButtonLabel(NewWizardMessages.NewTypeWizardPage_package_button);
        fPackageDialogField.setStatusWidthHint(NewWizardMessages.NewTypeWizardPage_default);

        fEnclosingTypeSelection = new SelectionButtonDialogField(SWT.CHECK);
        fEnclosingTypeSelection.setDialogFieldListener(adapter);
        fEnclosingTypeSelection.setLabelText(getEnclosingTypeLabel());

        fEnclosingTypeDialogField = new StringButtonDialogField(adapter);
        fEnclosingTypeDialogField.setDialogFieldListener(adapter);
        fEnclosingTypeDialogField.setButtonLabel(NewWizardMessages.NewTypeWizardPage_enclosing_button);

        fTypeNameDialogField = new StringDialogField();
        fTypeNameDialogField.setDialogFieldListener(adapter);
        fTypeNameDialogField.setLabelText(getTypeNameLabel());

        fSuperClassDialogField = new StringButtonDialogField(adapter);
        fSuperClassDialogField.setDialogFieldListener(adapter);
        fSuperClassDialogField.setLabelText(getSuperClassLabel());
        fSuperClassDialogField.setButtonLabel(NewWizardMessages.NewTypeWizardPage_superclass_button);

        String[] addButtons = new String[] {
            NewWizardMessages.NewTypeWizardPage_interfaces_add, /* 1 */null, NewWizardMessages.NewTypeWizardPage_interfaces_remove
        };
        fSuperInterfacesDialogField = new ListDialogField<InterfaceWrapper>(adapter, addButtons, new InterfacesListLabelProvider());
        fSuperInterfacesDialogField.setDialogFieldListener(adapter);
        fSuperInterfacesDialogField.setTableColumns(new ListDialogField.ColumnsDescription(1, false));
        fSuperInterfacesDialogField.setLabelText(getSuperInterfacesLabel());
        fSuperInterfacesDialogField.setRemoveButtonIndex(2);

        String[] buttonNames1 = new String[] {
            NewWizardMessages.NewTypeWizardPage_modifiers_public, NewWizardMessages.NewTypeWizardPage_modifiers_default, NewWizardMessages.NewTypeWizardPage_modifiers_private, NewWizardMessages.NewTypeWizardPage_modifiers_protected
        };
        fAccMdfButtons = new SelectionButtonDialogFieldGroup(SWT.RADIO, buttonNames1, 4);
        fAccMdfButtons.setDialogFieldListener(adapter);
        fAccMdfButtons.setLabelText(getModifiersLabel());
        fAccMdfButtons.setSelection(0, true);

        String[] buttonNames2;
        if (fTypeKind == CLASS_TYPE) {
            buttonNames2 = new String[] {
                NewWizardMessages.NewTypeWizardPage_modifiers_abstract, NewWizardMessages.NewTypeWizardPage_modifiers_final, NewWizardMessages.NewTypeWizardPage_modifiers_static
            };
        } else {
            if (fTypeKind == ENUM_TYPE || fTypeKind == ANNOTATION_TYPE) {
                buttonNames2 = new String[] {
                    NewWizardMessages.NewTypeWizardPage_modifiers_abstract, NewWizardMessages.NewTypeWizardPage_modifiers_static
                };
            } else
                buttonNames2 = new String[] {};
        }

        fOtherMdfButtons = new SelectionButtonDialogFieldGroup(SWT.CHECK, buttonNames2, 4);
        fOtherMdfButtons.setDialogFieldListener(adapter);

        fAccMdfButtons.enableSelectionButton(PRIVATE_INDEX, false);
        fAccMdfButtons.enableSelectionButton(PROTECTED_INDEX, false);
        fOtherMdfButtons.enableSelectionButton(STATIC_INDEX, false);

        if (fTypeKind == ENUM_TYPE || fTypeKind == ANNOTATION_TYPE) {
            fOtherMdfButtons.enableSelectionButton(ABSTRACT_INDEX, false);
            fOtherMdfButtons.enableSelectionButton(ENUM_ANNOT_STATIC_INDEX, false);
        }

        fAddCommentButton = new SelectionButtonDialogField(SWT.CHECK);
        fAddCommentButton.setLabelText(NewWizardMessages.NewTypeWizardPage_addcomment_label);

        fUseAddCommentButtonValue = false; // only used when enabled

        fCurrPackageCompletionProcessor = new JavaPackageCompletionProcessor();
        fEnclosingTypeCompletionProcessor = new JavaTypeCompletionProcessor(false, false, true);

        fPackageStatus = new StatusInfo();
        fEnclosingTypeStatus = new StatusInfo();

        fCanModifyPackage = true;
        fCanModifyEnclosingType = true;
        updateEnableState();

        fTypeNameStatus = new StatusInfo();
        fSuperClassStatus = new StatusInfo();
        fSuperInterfacesStatus = new StatusInfo();
        fModifierStatus = new StatusInfo();
    }

    /**
     * Initializes all fields provided by the page with a given selection.
     *
     * @param elem the selection used to initialize this page or <code>
     * null</code> if no selection was available
     */
    protected void initTypePage(IJavaElement elem) {
        String initSuperclass = "java.lang.Object"; //$NON-NLS-1$
        ArrayList<String> initSuperinterfaces = new ArrayList<String>(5);

        IJavaProject project = null;
        IPackageFragment pack = null;
        IType enclosingType = null;

        if (elem != null) {
            // evaluate the enclosing type
            project = elem.getJavaProject();
            pack = (IPackageFragment) elem.getAncestor(IJavaElement.PACKAGE_FRAGMENT);
            IType typeInCU = (IType) elem.getAncestor(IJavaElement.TYPE);
            if (typeInCU != null) {
                if (typeInCU.getCompilationUnit() != null) {
                    enclosingType = typeInCU;
                }
            } else {
                ITypeRoot cu = (ITypeRoot) elem.getAncestor(IJavaElement.COMPILATION_UNIT);
                if (cu != null) {
                    enclosingType = cu.findPrimaryType();
                }
            }

            try {
                IType type = null;
                if (elem.getElementType() == IJavaElement.TYPE) {
                    type = (IType) elem;
                    if (type.exists()) {
                        String superName = SuperInterfaceSelectionDialog.getNameWithTypeParameters(type);
                        if (type.isInterface()) {
                            initSuperinterfaces.add(superName);
                        } else {
                            initSuperclass = superName;
                        }
                    }
                }
            } catch (JavaModelException e) {
                JavaPlugin.log(e);
                // ignore this exception now
            }
        }

        String typeName = ""; //$NON-NLS-1$

        ITextSelection selection = getCurrentTextSelection();
        if (selection != null) {
            String text = selection.getText();
            if (text != null && validateJavaTypeName(text, project).isOK()) {
                typeName = text;
            }
        }

        setPackageFragment(pack, true);
        setEnclosingType(enclosingType, true);
        setEnclosingTypeSelection(false, true);

        setTypeName(typeName, true);
        setSuperClass(initSuperclass, true);
        setSuperInterfaces(initSuperinterfaces, true);

        setAddComments(StubUtility.doAddComments(project), true); // from project or workspace
    }

    private static IStatus validateJavaTypeName(String text, IJavaProject project) {
        if (project == null || !project.exists()) {
            return JavaConventions.validateJavaTypeName(text, JavaCore.VERSION_1_3, JavaCore.VERSION_1_3);
        }
        return JavaConventionsUtil.validateJavaTypeName(text, project);
    }

    private static IStatus validatePackageName(String text, IJavaProject project) {
        if (project == null || !project.exists()) {
            return JavaConventions.validatePackageName(text, JavaCore.VERSION_1_3, JavaCore.VERSION_1_3);
        }
        return JavaConventionsUtil.validatePackageName(text, project);
    }

    // -------- UI Creation ---------

    /**
     * Returns the label that is used for the package input field.
     *
     * @return the label that is used for the package input field.
     * @since 3.2
     */
    protected String getPackageLabel() {
        return NewWizardMessages.NewTypeWizardPage_package_label;
    }

    /**
     * Returns the label that is used for the enclosing type input field.
     *
     * @return the label that is used for the enclosing type input field.
     * @since 3.2
     */
    protected String getEnclosingTypeLabel() {
        return NewWizardMessages.NewTypeWizardPage_enclosing_selection_label;
    }

    /**
     * Returns the label that is used for the type name input field.
     *
     * @return the label that is used for the type name input field.
     * @since 3.2
     */
    protected String getTypeNameLabel() {
        return NewWizardMessages.NewTypeWizardPage_typename_label;
    }

    /**
     * Returns the label that is used for the modifiers input field.
     *
     * @return the label that is used for the modifiers input field
     * @since 3.2
     */
    protected String getModifiersLabel() {
        return NewWizardMessages.NewTypeWizardPage_modifiers_acc_label;
    }

    /**
     * Returns the label that is used for the super class input field.
     *
     * @return the label that is used for the super class input field.
     * @since 3.2
     */
    protected String getSuperClassLabel() {
        return NewWizardMessages.NewTypeWizardPage_superclass_label;
    }

    /**
     * Returns the label that is used for the super interfaces input field.
     *
     * @return the label that is used for the super interfaces input field.
     * @since 3.2
     */
    protected String getSuperInterfacesLabel() {
        if (fTypeKind != INTERFACE_TYPE)
            return NewWizardMessages.NewTypeWizardPage_interfaces_class_label;
        return NewWizardMessages.NewTypeWizardPage_interfaces_ifc_label;
    }

    /**
     * Creates a separator line. Expects a <code>GridLayout</code> with at least 1 column.
     *
     * @param composite the parent composite
     * @param nColumns number of columns to span
     */
    protected void createSeparator(Composite composite, int nColumns) {
        (new Separator(SWT.SEPARATOR | SWT.HORIZONTAL)).doFillIntoGrid(composite, nColumns, convertHeightInCharsToPixels(1));
    }

    /**
     * Creates the controls for the package name field. Expects a <code>GridLayout</code> with at least 4 columns.
     *
     * @param composite the parent composite
     * @param nColumns number of columns to span
     */
    protected void createPackageControls(Composite composite, int nColumns) {
        fPackageDialogField.doFillIntoGrid(composite, nColumns);
        Text text = fPackageDialogField.getTextControl(null);
        LayoutUtil.setWidthHint(text, getMaxFieldWidth());
        LayoutUtil.setHorizontalGrabbing(text);
        ControlContentAssistHelper.createTextContentAssistant(text, fCurrPackageCompletionProcessor);
        TextFieldNavigationHandler.install(text);
    }

    /**
     * Creates the controls for the enclosing type name field. Expects a <code>GridLayout</code> with at least 4
     * columns.
     *
     * @param composite the parent composite
     * @param nColumns number of columns to span
     */
    @SuppressWarnings("unused")
    protected void createEnclosingTypeControls(Composite composite, int nColumns) {
        // #6891
        Composite tabGroup = new Composite(composite, SWT.NONE);
        GridLayout layout = new GridLayout();
        layout.marginWidth = 0;
        layout.marginHeight = 0;
        tabGroup.setLayout(layout);

        fEnclosingTypeSelection.doFillIntoGrid(tabGroup, 1);

        Text text = fEnclosingTypeDialogField.getTextControl(composite);
        SWTUtil.setAccessibilityText(text, NewWizardMessages.NewTypeWizardPage_enclosing_field_description);

        GridData gd = new GridData(GridData.FILL_HORIZONTAL);
        gd.widthHint = getMaxFieldWidth();
        gd.horizontalSpan = 2;
        text.setLayoutData(gd);

        Button button = fEnclosingTypeDialogField.getChangeControl(composite);
        gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
        gd.widthHint = SWTUtil.getButtonWidthHint(button);
        button.setLayoutData(gd);
        ControlContentAssistHelper.createTextContentAssistant(text, fEnclosingTypeCompletionProcessor);
        TextFieldNavigationHandler.install(text);
    }

    /**
     * Creates the controls for the type name field. Expects a <code>GridLayout</code> with at least 2 columns.
     *
     * @param composite the parent composite
     * @param nColumns number of columns to span
     */
    protected void createTypeNameControls(Composite composite, int nColumns) {
        fTypeNameDialogField.doFillIntoGrid(composite, nColumns - 1);
        DialogField.createEmptySpace(composite);

        Text text = fTypeNameDialogField.getTextControl(null);
        LayoutUtil.setWidthHint(text, getMaxFieldWidth());
        TextFieldNavigationHandler.install(text);
    }

    /**
     * Creates the controls for the modifiers radio/checkbox buttons. Expects a <code>GridLayout</code> with at least 3
     * columns.
     *
     * @param composite the parent composite
     * @param nColumns number of columns to span
     */
    protected void createModifierControls(Composite composite, int nColumns) {
        LayoutUtil.setHorizontalSpan(fAccMdfButtons.getLabelControl(composite), 1);

        Control control = fAccMdfButtons.getSelectionButtonsGroup(composite);
        GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
        gd.horizontalSpan = nColumns - 2;
        control.setLayoutData(gd);

        DialogField.createEmptySpace(composite);

        if (fTypeKind == CLASS_TYPE) {
            DialogField.createEmptySpace(composite);

            control = fOtherMdfButtons.getSelectionButtonsGroup(composite);
            gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
            gd.horizontalSpan = nColumns - 2;
            control.setLayoutData(gd);

            DialogField.createEmptySpace(composite);
        }
    }

    /**
     * Creates the controls for the superclass name field. Expects a <code>GridLayout</code> with at least 3 columns.
     *
     * @param composite the parent composite
     * @param nColumns number of columns to span
     */
    protected void createSuperClassControls(Composite composite, int nColumns) {
        fSuperClassDialogField.doFillIntoGrid(composite, nColumns);
        Text text = fSuperClassDialogField.getTextControl(null);
        LayoutUtil.setWidthHint(text, getMaxFieldWidth());

        JavaTypeCompletionProcessor superClassCompletionProcessor = new JavaTypeCompletionProcessor(false, false, true);
        superClassCompletionProcessor.setCompletionContextRequestor(new CompletionContextRequestor() {
            @Override
            public StubTypeContext getStubTypeContext() {
                return getSuperClassStubTypeContext();
            }
        });

        ControlContentAssistHelper.createTextContentAssistant(text, superClassCompletionProcessor);
        TextFieldNavigationHandler.install(text);
    }

    /**
     * Creates the controls for the superclass name field. Expects a <code>GridLayout</code> with at least 3 columns.
     *
     * @param composite the parent composite
     * @param nColumns number of columns to span
     */
    protected void createSuperInterfacesControls(Composite composite, int nColumns) {
        final String INTERFACE = "interface"; //$NON-NLS-1$
        fSuperInterfacesDialogField.doFillIntoGrid(composite, nColumns);
        final TableViewer tableViewer = fSuperInterfacesDialogField.getTableViewer();
        tableViewer.setColumnProperties(new String[] {
            INTERFACE
        });

        TableTextCellEditor cellEditor = new TableTextCellEditor(tableViewer, 0) {
            @Override
            protected void doSetFocus() {
                if (text != null) {
                    text.setFocus();
                    text.setSelection(text.getText()
                        .length());
                    checkSelection();
                    checkDeleteable();
                    checkSelectable();
                }
            }
        };
        JavaTypeCompletionProcessor superInterfaceCompletionProcessor = new JavaTypeCompletionProcessor(false, false, true);
        superInterfaceCompletionProcessor.setCompletionContextRequestor(new CompletionContextRequestor() {
            @Override
            public StubTypeContext getStubTypeContext() {
                return getSuperInterfacesStubTypeContext();
            }
        });
        @SuppressWarnings("deprecation")
        org.eclipse.jface.contentassist.SubjectControlContentAssistant contentAssistant = ControlContentAssistHelper.createJavaContentAssistant(superInterfaceCompletionProcessor);
        Text cellEditorText = cellEditor.getText();
        @SuppressWarnings({
            "deprecation", "unused"
        })
        org.eclipse.ui.contentassist.ContentAssistHandler contentAssistantHandler = org.eclipse.ui.contentassist.ContentAssistHandler.createHandlerForText(cellEditorText, contentAssistant);
        TextFieldNavigationHandler.install(cellEditorText);
        cellEditor.setContentAssistant(contentAssistant);

        tableViewer.setCellEditors(new CellEditor[] {
            cellEditor
        });
        tableViewer.setCellModifier(new ICellModifier() {
            @Override
            public void modify(Object element, String property, Object value) {
                Object el = element;
                if (el instanceof Item)
                    el = ((Item) el).getData();

                ((InterfaceWrapper) el).interfaceName = (String) value;
                fSuperInterfacesDialogField.elementChanged((InterfaceWrapper) el);
            }

            @Override
            public Object getValue(Object element, String property) {
                return ((InterfaceWrapper) element).interfaceName;
            }

            @Override
            public boolean canModify(Object element, String property) {
                return true;
            }
        });
        tableViewer.getTable()
            .addKeyListener(new KeyAdapter() {
                @Override
                public void keyPressed(KeyEvent event) {
                    if (event.keyCode == SWT.F2 && event.stateMask == 0) {
                        ISelection selection = tableViewer.getSelection();
                        if (!(selection instanceof IStructuredSelection))
                            return;
                        IStructuredSelection structuredSelection = (IStructuredSelection) selection;
                        tableViewer.editElement(structuredSelection.getFirstElement(), 0);
                    }
                }
            });
        GridData gd = (GridData) fSuperInterfacesDialogField.getListControl(null)
            .getLayoutData();
        if (fTypeKind == CLASS_TYPE) {
            gd.heightHint = convertHeightInCharsToPixels(3);
        } else {
            gd.heightHint = convertHeightInCharsToPixels(6);
        }
        gd.grabExcessVerticalSpace = false;
        gd.widthHint = getMaxFieldWidth();
    }

    /**
     * Creates the controls for the preference page links. Expects a <code>GridLayout</code> with at least 3 columns.
     *
     * @param composite the parent composite
     * @param nColumns number of columns to span
     * @since 3.1
     */
    protected void createCommentControls(Composite composite, int nColumns) {
        Link link = new Link(composite, SWT.NONE);
        link.setText(NewWizardMessages.NewTypeWizardPage_addcomment_description);
        link.addSelectionListener(new TypeFieldsAdapter());
        link.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, false, false, nColumns, 1));
        DialogField.createEmptySpace(composite);
        fAddCommentButton.doFillIntoGrid(composite, nColumns - 1);
    }

    /**
     * Sets the focus on the type name input field.
     */
    protected void setFocus() {
        if (fTypeNameDialogField.isEnabled()) {
            fTypeNameDialogField.setFocus();
        } else {
            setFocusOnContainer();
        }
    }

    // -------- TypeFieldsAdapter --------

    private class TypeFieldsAdapter implements IStringButtonAdapter, IDialogFieldListener, IListAdapter<InterfaceWrapper>, SelectionListener {

        // -------- IStringButtonAdapter
        @Override
        public void changeControlPressed(DialogField field) {
            typePageChangeControlPressed(field);
        }

        // -------- IListAdapter
        @Override
        public void customButtonPressed(ListDialogField<InterfaceWrapper> field, int index) {
            typePageCustomButtonPressed(field, index);
        }

        @Override
        public void selectionChanged(ListDialogField<InterfaceWrapper> field) {
        }

        // -------- IDialogFieldListener
        @Override
        public void dialogFieldChanged(DialogField field) {
            typePageDialogFieldChanged(field);
        }

        @Override
        public void doubleClicked(ListDialogField<InterfaceWrapper> field) {
        }

        @Override
        public void widgetSelected(SelectionEvent e) {
            typePageLinkActivated();
        }

        @Override
        public void widgetDefaultSelected(SelectionEvent e) {
            typePageLinkActivated();
        }
    }

    private void typePageLinkActivated() {
        IJavaProject project = getJavaProject();
        if (project != null) {
            PreferenceDialog dialog = PreferencesUtil.createPropertyDialogOn(getShell(), project.getProject(), CodeTemplatePreferencePage.PROP_ID, null, null);
            dialog.open();
        } else {
            String title = NewWizardMessages.NewTypeWizardPage_configure_templates_title;
            String message = NewWizardMessages.NewTypeWizardPage_configure_templates_message;
            MessageDialog.openInformation(getShell(), title, message);
        }
    }

    private void typePageChangeControlPressed(DialogField field) {
        if (field == fPackageDialogField) {
            IPackageFragment pack = choosePackage();
            if (pack != null) {
                fPackageDialogField.setText(pack.getElementName());
            }
        } else if (field == fEnclosingTypeDialogField) {
            IType type = chooseEnclosingType();
            if (type != null) {
                fEnclosingTypeDialogField.setText(type.getFullyQualifiedName('.'));
            }
        } else if (field == fSuperClassDialogField) {
            IType type = chooseSuperClass();
            if (type != null) {
                fSuperClassDialogField.setText(SuperInterfaceSelectionDialog.getNameWithTypeParameters(type));
            }
        }
    }

    private void typePageCustomButtonPressed(DialogField field, int index) {
        if (field == fSuperInterfacesDialogField && index == 0) {
            chooseSuperInterfaces();
            List<InterfaceWrapper> interfaces = fSuperInterfacesDialogField.getElements();
            if (!interfaces.isEmpty()) {
                Object element = interfaces.get(interfaces.size() - 1);
                fSuperInterfacesDialogField.editElement(element);
            }
        }
    }

    /*
     * A field on the type has changed. The fields' status and all dependent status are updated.
     */
    private void typePageDialogFieldChanged(DialogField field) {
        String fieldName = null;
        if (field == fPackageDialogField) {
            fPackageStatus = packageChanged();
            updatePackageStatusLabel();
            fTypeNameStatus = typeNameChanged();
            fSuperClassStatus = superClassChanged();
            fieldName = PACKAGE;
        } else if (field == fEnclosingTypeDialogField) {
            fEnclosingTypeStatus = enclosingTypeChanged();
            fTypeNameStatus = typeNameChanged();
            fSuperClassStatus = superClassChanged();
            fieldName = ENCLOSING;
        } else if (field == fEnclosingTypeSelection) {
            updateEnableState();
            boolean isEnclosedType = isEnclosingTypeSelected();
            if (!isEnclosedType) {
                if (fAccMdfButtons.isSelected(PRIVATE_INDEX) || fAccMdfButtons.isSelected(PROTECTED_INDEX)) {
                    fAccMdfButtons.setSelection(PRIVATE_INDEX, false);
                    fAccMdfButtons.setSelection(PROTECTED_INDEX, false);
                    fAccMdfButtons.setSelection(PUBLIC_INDEX, true);
                }
                if (fOtherMdfButtons.isSelected(STATIC_INDEX)) {
                    fOtherMdfButtons.setSelection(STATIC_INDEX, false);
                }
            }
            fAccMdfButtons.enableSelectionButton(PRIVATE_INDEX, isEnclosedType);
            fAccMdfButtons.enableSelectionButton(PROTECTED_INDEX, isEnclosedType);
            fOtherMdfButtons.enableSelectionButton(STATIC_INDEX, isEnclosedType);
            fTypeNameStatus = typeNameChanged();
            fSuperClassStatus = superClassChanged();
            fieldName = ENCLOSINGSELECTION;
        } else if (field == fTypeNameDialogField) {
            fTypeNameStatus = typeNameChanged();
            fieldName = TYPENAME;
        } else if (field == fSuperClassDialogField) {
            fSuperClassStatus = superClassChanged();
            fieldName = SUPER;
        } else if (field == fSuperInterfacesDialogField) {
            fSuperInterfacesStatus = superInterfacesChanged();
            fieldName = INTERFACES;
        } else if (field == fOtherMdfButtons || field == fAccMdfButtons) {
            fModifierStatus = modifiersChanged();
            fieldName = MODIFIERS;
        } else {
            fieldName = METHODS;
        }
        // tell all others
        handleFieldChanged(fieldName);
    }

    // -------- update message ----------------

    /*
     * @see org.eclipse.jdt.ui.wizards.NewContainerWizardPage#handleFieldChanged(String)
     */
    @Override
    protected void handleFieldChanged(String fieldName) {
        super.handleFieldChanged(fieldName);
        if (CONTAINER.equals(fieldName)) {
            fPackageStatus = packageChanged();
            fEnclosingTypeStatus = enclosingTypeChanged();
            fTypeNameStatus = typeNameChanged();
            fSuperClassStatus = superClassChanged();
            fSuperInterfacesStatus = superInterfacesChanged();
        }
    }

    // ---- set / get ----------------

    /**
     * Returns the text of the package input field.
     *
     * @return the text of the package input field
     */
    public String getPackageText() {
        return fPackageDialogField.getText();
    }

    /**
     * Returns the text of the enclosing type input field.
     *
     * @return the text of the enclosing type input field
     */
    public String getEnclosingTypeText() {
        return fEnclosingTypeDialogField.getText();
    }

    /**
     * Returns the package fragment corresponding to the current input.
     *
     * @return a package fragment or <code>null</code> if the input could not be resolved.
     */
    public IPackageFragment getPackageFragment() {
        if (!isEnclosingTypeSelected()) {
            return fCurrPackage;
        }

        if (fCurrEnclosingType != null) {
            return fCurrEnclosingType.getPackageFragment();
        }
        return null;
    }

    /**
     * Sets the package fragment to the given value. The method updates the model and the text of the control.
     *
     * @param pack the package fragment to be set
     * @param canBeModified if <code>true</code> the package fragment is editable; otherwise it is read-only.
     */
    public void setPackageFragment(IPackageFragment pack, boolean canBeModified) {
        fCurrPackage = pack;
        fCanModifyPackage = canBeModified;
        String str = (pack == null) ? "" : pack.getElementName(); //$NON-NLS-1$
        fPackageDialogField.setText(str);
        updateEnableState();
    }

    /**
     * Returns the enclosing type corresponding to the current input.
     *
     * @return the enclosing type or <code>null</code> if the enclosing type is not selected or the input could not be
     *         resolved
     */
    public IType getEnclosingType() {
        if (isEnclosingTypeSelected()) {
            return fCurrEnclosingType;
        }
        return null;
    }

    /**
     * Sets the enclosing type. The method updates the underlying model and the text of the control.
     *
     * @param type the enclosing type
     * @param canBeModified if <code>true</code> the enclosing type field is editable; otherwise it is read-only.
     */
    public void setEnclosingType(IType type, boolean canBeModified) {
        fCurrEnclosingType = type;
        fCanModifyEnclosingType = canBeModified;
        String str = (type == null) ? "" : type.getFullyQualifiedName('.'); //$NON-NLS-1$
        fEnclosingTypeDialogField.setText(str);
        updateEnableState();
    }

    /**
     * Returns the selection state of the enclosing type checkbox.
     *
     * @return the selection state of the enclosing type checkbox
     */
    public boolean isEnclosingTypeSelected() {
        return fEnclosingTypeSelection.isSelected();
    }

    /**
     * Sets the enclosing type checkbox's selection state.
     *
     * @param isSelected the checkbox's selection state
     * @param canBeModified if <code>true</code> the enclosing type checkbox is modifiable; otherwise it is read-only.
     */
    public void setEnclosingTypeSelection(boolean isSelected, boolean canBeModified) {
        fEnclosingTypeSelection.setSelection(isSelected);
        fEnclosingTypeSelection.setEnabled(canBeModified);
        updateEnableState();
    }

    /**
     * Returns the type name entered into the type input field.
     *
     * @return the type name
     */
    public String getTypeName() {
        return fTypeNameDialogField.getText();
    }

    /**
     * Sets the type name input field's text to the given value. Method doesn't update the model.
     *
     * @param name the new type name
     * @param canBeModified if <code>true</code> the type name field is editable; otherwise it is read-only.
     */
    public void setTypeName(String name, boolean canBeModified) {
        fTypeNameDialogField.setText(name);
        fTypeNameDialogField.setEnabled(canBeModified);
    }

    /**
     * Returns the selected modifiers.
     *
     * @return the selected modifiers
     * @see Flags
     */
    public int getModifiers() {
        int mdf = 0;
        if (fAccMdfButtons.isSelected(PUBLIC_INDEX)) {
            mdf += F_PUBLIC;
        } else if (fAccMdfButtons.isSelected(PRIVATE_INDEX)) {
            mdf += F_PRIVATE;
        } else if (fAccMdfButtons.isSelected(PROTECTED_INDEX)) {
            mdf += F_PROTECTED;
        }
        if (fOtherMdfButtons.isSelected(ABSTRACT_INDEX)) {
            mdf += F_ABSTRACT;
        }
        if (fOtherMdfButtons.isSelected(FINAL_INDEX)) {
            mdf += F_FINAL;
        }
        if (fOtherMdfButtons.isSelected(STATIC_INDEX)) {
            mdf += F_STATIC;
        }
        return mdf;
    }

    /**
     * Sets the modifiers.
     *
     * @param modifiers <code>F_PUBLIC</code>, <code>F_PRIVATE</code>, <code>F_PROTECTED</code>,
     *            <code>F_ABSTRACT</code>, <code>F_FINAL</code> or <code>F_STATIC</code> or a valid combination.
     * @param canBeModified if <code>true</code> the modifier fields are editable; otherwise they are read-only
     * @see Flags
     */
    public void setModifiers(int modifiers, boolean canBeModified) {
        if (Flags.isPublic(modifiers)) {
            fAccMdfButtons.setSelection(PUBLIC_INDEX, true);
        } else if (Flags.isPrivate(modifiers)) {
            fAccMdfButtons.setSelection(PRIVATE_INDEX, true);
        } else if (Flags.isProtected(modifiers)) {
            fAccMdfButtons.setSelection(PROTECTED_INDEX, true);
        } else {
            fAccMdfButtons.setSelection(DEFAULT_INDEX, true);
        }
        if (Flags.isAbstract(modifiers)) {
            fOtherMdfButtons.setSelection(ABSTRACT_INDEX, true);
        }
        if (Flags.isFinal(modifiers)) {
            fOtherMdfButtons.setSelection(FINAL_INDEX, true);
        }
        if (Flags.isStatic(modifiers)) {
            fOtherMdfButtons.setSelection(STATIC_INDEX, true);
        }

        fAccMdfButtons.setEnabled(canBeModified);
        fOtherMdfButtons.setEnabled(canBeModified);
    }

    /**
     * Returns the content of the superclass input field.
     *
     * @return the superclass name
     */
    public String getSuperClass() {
        return fSuperClassDialogField.getText();
    }

    /**
     * Sets the super class name.
     *
     * @param name the new superclass name
     * @param canBeModified if <code>true</code> the superclass name field is editable; otherwise it is read-only.
     */
    public void setSuperClass(String name, boolean canBeModified) {
        fSuperClassDialogField.setText(name);
        fSuperClassDialogField.setEnabled(canBeModified);
    }

    /**
     * Returns the chosen super interfaces.
     *
     * @return a list of chosen super interfaces. The list's elements are of type <code>String</code>
     */
    public List<String> getSuperInterfaces() {
        List<InterfaceWrapper> interfaces = fSuperInterfacesDialogField.getElements();
        ArrayList<String> result = new ArrayList<String>(interfaces.size());
        for (Iterator<InterfaceWrapper> iter = interfaces.iterator(); iter.hasNext();) {
            InterfaceWrapper wrapper = iter.next();
            result.add(wrapper.interfaceName);
        }
        return result;
    }

    /**
     * Sets the super interfaces.
     *
     * @param interfacesNames a list of super interface. The method requires that the list's elements are of type
     *            <code>String</code>
     * @param canBeModified if <code>true</code> the super interface field is editable; otherwise it is read-only.
     */
    public void setSuperInterfaces(List<String> interfacesNames, boolean canBeModified) {
        ArrayList<InterfaceWrapper> interfaces = new ArrayList<InterfaceWrapper>(interfacesNames.size());
        for (Iterator<String> iter = interfacesNames.iterator(); iter.hasNext();) {
            interfaces.add(new InterfaceWrapper(iter.next()));
        }
        fSuperInterfacesDialogField.setElements(interfaces);
        fSuperInterfacesDialogField.setEnabled(canBeModified);
    }

    /**
     * Adds a super interface to the end of the list and selects it if it is not in the list yet.
     *
     * @param superInterface the fully qualified type name of the interface.
     * @return returns <code>true</code>if the interfaces has been added, <code>false</code> if the interface already is
     *         in the list.
     * @since 3.2
     */
    public boolean addSuperInterface(String superInterface) {
        return fSuperInterfacesDialogField.addElement(new InterfaceWrapper(superInterface));
    }

    /**
     * Sets 'Add comment' checkbox. The value set will only be used when creating source when the comment control is
     * enabled (see {@link #enableCommentControl(boolean)}
     *
     * @param doAddComments if <code>true</code>, comments are added.
     * @param canBeModified if <code>true</code> check box is editable; otherwise it is read-only.
     * @since 3.1
     */
    public void setAddComments(boolean doAddComments, boolean canBeModified) {
        fAddCommentButton.setSelection(doAddComments);
        fAddCommentButton.setEnabled(canBeModified);
    }

    /**
     * Sets to use the 'Add comment' checkbox value. Clients that use the 'Add comment' checkbox additionally have to
     * enable the control. This has been added for backwards compatibility.
     *
     * @param useAddCommentValue if <code>true</code>,
     * @since 3.1
     */
    public void enableCommentControl(boolean useAddCommentValue) {
        fUseAddCommentButtonValue = useAddCommentValue;
    }

    /**
     * Returns if comments are added. This method can be overridden by clients. The selection of the comment control is
     * taken if enabled (see {@link #enableCommentControl(boolean)}, otherwise the settings as specified in the
     * preferences is used.
     *
     * @return Returns <code>true</code> if comments can be added
     * @since 3.1
     */
    public boolean isAddComments() {
        if (fUseAddCommentButtonValue) {
            return fAddCommentButton.isSelected();
        }
        return StubUtility.doAddComments(getJavaProject());
    }

    /**
     * Returns the resource handle that corresponds to the compilation unit to was or will be created or modified.
     *
     * @return A resource or null if the page contains illegal values.
     * @since 3.0
     */
    public IResource getModifiedResource() {
        IType enclosing = getEnclosingType();
        if (enclosing != null) {
            return enclosing.getResource();
        }
        IPackageFragment pack = getPackageFragment();
        if (pack != null) {
            String cuName = getCompilationUnitName(getTypeNameWithoutParameters());
            return pack.getCompilationUnit(cuName)
                .getResource();
        }
        return null;
    }

    // ----------- validation ----------

    /*
     * @see org.eclipse.jdt.ui.wizards.NewContainerWizardPage#containerChanged()
     */
    @Override
    protected IStatus containerChanged() {
        IStatus status = super.containerChanged();
        IPackageFragmentRoot root = getPackageFragmentRoot();
        if (root == null) {
            return status;
        }

        IJavaProject project = root.getJavaProject();

        if ((fTypeKind == ANNOTATION_TYPE || fTypeKind == ENUM_TYPE) && !status.matches(IStatus.ERROR)) {
            if (!JavaModelUtil.is50OrHigher(project)) {
                // error as createType will fail otherwise (bug 96928)
                return new StatusInfo(IStatus.ERROR, Messages.format(NewWizardMessages.NewTypeWizardPage_warning_NotJDKCompliant, BasicElementLabels.getJavaElementName(project.getElementName())));
            }
            if (fTypeKind == ENUM_TYPE) {
                try {
                    // if findType(...) == null then Enum is unavailable
                    if (findType(project, "java.lang.Enum") == null) //$NON-NLS-1$
                        return new StatusInfo(IStatus.WARNING, NewWizardMessages.NewTypeWizardPage_warning_EnumClassNotFound);
                } catch (JavaModelException e) {
                    JavaPlugin.log(e);
                }
            }
        }

        fCurrPackageCompletionProcessor.setPackageFragmentRoot(root);
        fEnclosingTypeCompletionProcessor.setPackageFragment(root.getPackageFragment("")); //$NON-NLS-1$
        return status;
    }

    /**
     * A hook method that gets called when the package field has changed. The method validates the package name and
     * returns the status of the validation. The validation also updates the package fragment model.
     * <p>
     * Subclasses may extend this method to perform their own validation.
     * </p>
     *
     * @return the status of the validation
     */
    protected IStatus packageChanged() {
        StatusInfo status = new StatusInfo();
        IPackageFragmentRoot root = getPackageFragmentRoot();
        fPackageDialogField.enableButton(root != null);

        IJavaProject project = root != null ? root.getJavaProject() : null;

        String packName = getPackageText();
        if (packName.length() > 0) {
            IStatus val = validatePackageName(packName, project);
            if (val.getSeverity() == IStatus.ERROR) {
                status.setError(Messages.format(NewWizardMessages.NewTypeWizardPage_error_InvalidPackageName, val.getMessage()));
                return status;
            } else if (val.getSeverity() == IStatus.WARNING) {
                status.setWarning(Messages.format(NewWizardMessages.NewTypeWizardPage_warning_DiscouragedPackageName, val.getMessage()));
                // continue
            }
        } else {
            status.setWarning(NewWizardMessages.NewTypeWizardPage_warning_DefaultPackageDiscouraged);
        }

        if (project != null) {
            assert (root != null);
            if (project.exists() && packName.length() > 0) {
                try {
                    IPath rootPath = root.getPath();
                    IPath outputPath = project.getOutputLocation();
                    if (rootPath.isPrefixOf(outputPath) && !rootPath.equals(outputPath)) {
                        // if the bin folder is inside of our root, don't allow to name a package
                        // like the bin folder
                        IPath packagePath = rootPath.append(packName.replace('.', '/'));
                        if (outputPath.isPrefixOf(packagePath)) {
                            status.setError(NewWizardMessages.NewTypeWizardPage_error_ClashOutputLocation);
                            return status;
                        }
                    }
                } catch (JavaModelException e) {
                    JavaPlugin.log(e);
                    // let pass
                }
            }

            fCurrPackage = root.getPackageFragment(packName);
            IResource resource = fCurrPackage.getResource();
            if (resource != null) {
                if (resource.isVirtual()) {
                    status.setError(NewWizardMessages.NewTypeWizardPage_error_PackageIsVirtual);
                    return status;
                }
                if (!ResourcesPlugin.getWorkspace()
                    .validateFiltered(resource)
                    .isOK()) {
                    status.setError(NewWizardMessages.NewTypeWizardPage_error_PackageNameFiltered);
                    return status;
                }
            }
        } else {
            status.setError(""); //$NON-NLS-1$
        }
        return status;
    }

    /*
     * Updates the 'default' label next to the package field.
     */
    private void updatePackageStatusLabel() {
        String packName = getPackageText();

        if (packName.length() == 0) {
            fPackageDialogField.setStatus(NewWizardMessages.NewTypeWizardPage_default);
        } else {
            fPackageDialogField.setStatus(""); //$NON-NLS-1$
        }
    }

    /*
     * Updates the enable state of buttons related to the enclosing type selection checkbox.
     */
    private void updateEnableState() {
        boolean enclosing = isEnclosingTypeSelected();
        fPackageDialogField.setEnabled(fCanModifyPackage && !enclosing);
        fEnclosingTypeDialogField.setEnabled(fCanModifyEnclosingType && enclosing);
        if (fTypeKind == ENUM_TYPE || fTypeKind == ANNOTATION_TYPE) {
            fOtherMdfButtons.enableSelectionButton(ABSTRACT_INDEX, enclosing);
            fOtherMdfButtons.enableSelectionButton(ENUM_ANNOT_STATIC_INDEX, enclosing);
        }
    }

    /**
     * Hook method that gets called when the enclosing type name has changed. The method validates the enclosing type
     * and returns the status of the validation. It also updates the enclosing type model.
     * <p>
     * Subclasses may extend this method to perform their own validation.
     * </p>
     *
     * @return the status of the validation
     */
    protected IStatus enclosingTypeChanged() {
        StatusInfo status = new StatusInfo();
        fCurrEnclosingType = null;

        IPackageFragmentRoot root = getPackageFragmentRoot();

        fEnclosingTypeDialogField.enableButton(root != null);
        if (root == null) {
            status.setError(""); //$NON-NLS-1$
            return status;
        }

        String enclName = getEnclosingTypeText();
        if (enclName.length() == 0) {
            status.setError(NewWizardMessages.NewTypeWizardPage_error_EnclosingTypeEnterName);
            return status;
        }
        try {
            IType type = findType(root.getJavaProject(), enclName);
            if (type == null) {
                status.setError(NewWizardMessages.NewTypeWizardPage_error_EnclosingTypeNotExists);
                return status;
            }

            if (type.getCompilationUnit() == null) {
                status.setError(NewWizardMessages.NewTypeWizardPage_error_EnclosingNotInCU);
                return status;
            }
            if (!JavaModelUtil.isEditable(type.getCompilationUnit())) {
                status.setError(NewWizardMessages.NewTypeWizardPage_error_EnclosingNotEditable);
                return status;
            }

            fCurrEnclosingType = type;
            IPackageFragmentRoot enclosingRoot = JavaModelUtil.getPackageFragmentRoot(type);
            if (!enclosingRoot.equals(root)) {
                status.setWarning(NewWizardMessages.NewTypeWizardPage_warning_EnclosingNotInSourceFolder);
            }
            return status;
        } catch (JavaModelException e) {
            status.setError(NewWizardMessages.NewTypeWizardPage_error_EnclosingTypeNotExists);
            JavaPlugin.log(e);
            return status;
        }
    }

    private IType findType(IJavaProject project, String typeName) throws JavaModelException {
        if (project.exists()) {
            return project.findType(typeName);
        }
        return null;
    }

    private String getTypeNameWithoutParameters() {
        String typeNameWithParameters = getTypeName();
        int angleBracketOffset = typeNameWithParameters.indexOf('<');
        if (angleBracketOffset == -1) {
            return typeNameWithParameters;
        }

        return typeNameWithParameters.substring(0, angleBracketOffset);
    }

    /**
     * Hook method that is called when evaluating the name of the compilation unit to create. By default, a file
     * extension <code>java</code> is added to the given type name, but implementors can override this behavior.
     *
     * @param typeName the name of the type to create the compilation unit for.
     * @return the name of the compilation unit to be created for the given name
     * @since 3.2
     */
    protected String getCompilationUnitName(String typeName) {
        return typeName + JavaModelUtil.DEFAULT_CU_SUFFIX;
    }

    /**
     * Hook method that gets called when the type name has changed. The method validates the type name and returns the
     * status of the validation.
     * <p>
     * Subclasses may extend this method to perform their own validation.
     * </p>
     *
     * @return the status of the validation
     */
    protected IStatus typeNameChanged() {
        StatusInfo status = new StatusInfo();
        fCurrType = null;
        String typeNameWithParameters = getTypeName();
        // must not be empty
        if (typeNameWithParameters.length() == 0) {
            status.setError(NewWizardMessages.NewTypeWizardPage_error_EnterTypeName);
            return status;
        }

        String typeName = getTypeNameWithoutParameters();
        if (typeName.indexOf('.') != -1) {
            status.setError(NewWizardMessages.NewTypeWizardPage_error_QualifiedName);
            return status;
        }

        IJavaProject project = getJavaProject();
        IStatus val = validateJavaTypeName(typeName, project);
        if (val.getSeverity() == IStatus.ERROR) {
            status.setError(Messages.format(NewWizardMessages.NewTypeWizardPage_error_InvalidTypeName, val.getMessage()));
            return status;
        } else if (val.getSeverity() == IStatus.WARNING) {
            status.setWarning(Messages.format(NewWizardMessages.NewTypeWizardPage_warning_TypeNameDiscouraged, val.getMessage()));
            // continue checking
        }

        // must not exist
        if (!isEnclosingTypeSelected()) {
            IPackageFragment pack = getPackageFragment();
            if (pack != null) {
                ICompilationUnit cu = pack.getCompilationUnit(getCompilationUnitName(typeName));
                fCurrType = cu.getType(typeName);
                IResource resource = cu.getResource();

                if (resource.exists()) {
                    status.setError(NewWizardMessages.NewTypeWizardPage_error_TypeNameExists);
                    return status;
                }
                if (!ResourcesPlugin.getWorkspace()
                    .validateFiltered(resource)
                    .isOK()) {
                    status.setError(NewWizardMessages.NewTypeWizardPage_error_TypeNameFiltered);
                    return status;
                }
                URI location = resource.getLocationURI();
                if (location != null) {
                    try {
                        IFileStore store = EFS.getStore(location);
                        if (store.fetchInfo()
                            .exists()) {
                            status.setError(NewWizardMessages.NewTypeWizardPage_error_TypeNameExistsDifferentCase);
                            return status;
                        }
                    } catch (CoreException e) {
                        status.setError(Messages.format(NewWizardMessages.NewTypeWizardPage_error_uri_location_unkown, BasicElementLabels.getURLPart(Resources.getLocationString(resource))));
                    }
                }
            }
        } else {
            IType type = getEnclosingType();
            if (type != null) {
                fCurrType = type.getType(typeName);
                if (fCurrType.exists()) {
                    status.setError(NewWizardMessages.NewTypeWizardPage_error_TypeNameExists);
                    return status;
                }
            }
        }

        if (!typeNameWithParameters.equals(typeName) && project != null) {
            if (!JavaModelUtil.is50OrHigher(project)) {
                status.setError(NewWizardMessages.NewTypeWizardPage_error_TypeParameters);
                return status;
            }
            String typeDeclaration = "class " + typeNameWithParameters + " {}"; //$NON-NLS-1$//$NON-NLS-2$
            ASTParser parser = ASTParser.newParser(ASTProvider.SHARED_AST_LEVEL);
            parser.setSource(typeDeclaration.toCharArray());
            parser.setProject(project);
            CompilationUnit compilationUnit = (CompilationUnit) parser.createAST(null);
            IProblem[] problems = compilationUnit.getProblems();
            if (problems.length > 0) {
                status.setError(Messages.format(NewWizardMessages.NewTypeWizardPage_error_InvalidTypeName, problems[0].getMessage()));
                return status;
            }
        }
        return status;
    }

    /**
     * Hook method that gets called when the superclass name has changed. The method validates the superclass name and
     * returns the status of the validation.
     * <p>
     * Subclasses may extend this method to perform their own validation.
     * </p>
     *
     * @return the status of the validation
     */
    protected IStatus superClassChanged() {
        StatusInfo status = new StatusInfo();
        IPackageFragmentRoot root = getPackageFragmentRoot();
        fSuperClassDialogField.enableButton(root != null);

        fSuperClassStubTypeContext = null;

        String sclassName = getSuperClass();
        if (sclassName.length() == 0) {
            // accept the empty field (stands for java.lang.Object)
            return status;
        }

        if (root != null) {
            Type type = TypeContextChecker.parseSuperClass(sclassName);
            if (type == null) {
                status.setError(NewWizardMessages.NewTypeWizardPage_error_InvalidSuperClassName);
                return status;
            }
            if (type instanceof ParameterizedType && !JavaModelUtil.is50OrHigher(root.getJavaProject())) {
                status.setError(NewWizardMessages.NewTypeWizardPage_error_SuperClassNotParameterized);
                return status;
            }
        } else {
            status.setError(""); //$NON-NLS-1$
        }
        return status;
    }

    private StubTypeContext getSuperClassStubTypeContext() {
        if (fSuperClassStubTypeContext == null) {
            String typeName;
            if (fCurrType != null) {
                typeName = getTypeName();
            } else {
                typeName = JavaTypeCompletionProcessor.DUMMY_CLASS_NAME;
            }
            fSuperClassStubTypeContext = TypeContextChecker.createSuperClassStubTypeContext(typeName, getEnclosingType(), getPackageFragment());
        }
        return fSuperClassStubTypeContext;
    }

    /**
     * Hook method that gets called when the list of super interface has changed. The method validates the super
     * interfaces and returns the status of the validation.
     * <p>
     * Subclasses may extend this method to perform their own validation.
     * </p>
     *
     * @return the status of the validation
     */
    protected IStatus superInterfacesChanged() {
        StatusInfo status = new StatusInfo();

        IPackageFragmentRoot root = getPackageFragmentRoot();
        fSuperInterfacesDialogField.enableButton(0, root != null);

        if (root != null) {
            List<InterfaceWrapper> elements = fSuperInterfacesDialogField.getElements();
            int nElements = elements.size();
            for (int i = 0; i < nElements; i++) {
                String intfname = elements.get(i).interfaceName;
                Type type = TypeContextChecker.parseSuperInterface(intfname);
                if (type == null) {
                    status.setError(Messages.format(NewWizardMessages.NewTypeWizardPage_error_InvalidSuperInterfaceName, BasicElementLabels.getJavaElementName(intfname)));
                    return status;
                }
                if (type instanceof ParameterizedType && !JavaModelUtil.is50OrHigher(root.getJavaProject())) {
                    status.setError(Messages.format(NewWizardMessages.NewTypeWizardPage_error_SuperInterfaceNotParameterized, BasicElementLabels.getJavaElementName(intfname)));
                    return status;
                }
            }
        }
        return status;
    }

    private StubTypeContext getSuperInterfacesStubTypeContext() {
        if (fSuperInterfaceStubTypeContext == null) {
            String typeName;
            if (fCurrType != null) {
                typeName = getTypeName();
            } else {
                typeName = JavaTypeCompletionProcessor.DUMMY_CLASS_NAME;
            }
            fSuperInterfaceStubTypeContext = TypeContextChecker.createSuperInterfaceStubTypeContext(typeName, getEnclosingType(), getPackageFragment());
        }
        return fSuperInterfaceStubTypeContext;
    }

    /**
     * Hook method that gets called when the modifiers have changed. The method validates the modifiers and returns the
     * status of the validation.
     * <p>
     * Subclasses may extend this method to perform their own validation.
     * </p>
     *
     * @return the status of the validation
     */
    protected IStatus modifiersChanged() {
        StatusInfo status = new StatusInfo();
        int modifiers = getModifiers();
        if (Flags.isFinal(modifiers) && Flags.isAbstract(modifiers)) {
            status.setError(NewWizardMessages.NewTypeWizardPage_error_ModifiersFinalAndAbstract);
        }
        return status;
    }

    // selection dialogs

    /**
     * Opens a selection dialog that allows to select a package.
     *
     * @return returns the selected package or <code>null</code> if the dialog has been canceled. The caller typically
     *         sets the result to the package input field.
     *         <p>
     *         Clients can override this method if they want to offer a different dialog.
     *         </p>
     * @since 3.2
     */
    protected IPackageFragment choosePackage() {
        IPackageFragmentRoot froot = getPackageFragmentRoot();
        IJavaElement[] packages = null;
        try {
            if (froot != null && froot.exists()) {
                packages = froot.getChildren();
            }
        } catch (JavaModelException e) {
            JavaPlugin.log(e);
        }
        if (packages == null) {
            packages = new IJavaElement[0];
        }

        ElementListSelectionDialog dialog = new ElementListSelectionDialog(getShell(), new JavaElementLabelProvider(JavaElementLabelProvider.SHOW_DEFAULT));
        dialog.setIgnoreCase(false);
        dialog.setTitle(NewWizardMessages.NewTypeWizardPage_ChoosePackageDialog_title);
        dialog.setMessage(NewWizardMessages.NewTypeWizardPage_ChoosePackageDialog_description);
        dialog.setEmptyListMessage(NewWizardMessages.NewTypeWizardPage_ChoosePackageDialog_empty);
        dialog.setElements(packages);
        dialog.setHelpAvailable(false);

        IPackageFragment pack = getPackageFragment();
        if (pack != null) {
            dialog.setInitialSelections(new Object[] {
                pack
            });
        }

        if (dialog.open() == Window.OK) {
            return (IPackageFragment) dialog.getFirstResult();
        }
        return null;
    }

    /**
     * Opens a selection dialog that allows to select an enclosing type.
     *
     * @return returns the selected type or <code>null</code> if the dialog has been canceled. The caller typically sets
     *         the result to the enclosing type input field.
     *         <p>
     *         Clients can override this method if they want to offer a different dialog.
     *         </p>
     * @since 3.2
     */
    protected IType chooseEnclosingType() {
        IPackageFragmentRoot root = getPackageFragmentRoot();
        if (root == null) {
            return null;
        }

        IJavaSearchScope scope = SearchEngine.createJavaSearchScope(new IJavaElement[] {
            root
        });

        FilteredTypesSelectionDialog dialog = new FilteredTypesSelectionDialog(getShell(), false, getWizard().getContainer(), scope, IJavaSearchConstants.TYPE);
        dialog.setTitle(NewWizardMessages.NewTypeWizardPage_ChooseEnclosingTypeDialog_title);
        dialog.setMessage(NewWizardMessages.NewTypeWizardPage_ChooseEnclosingTypeDialog_description);
        dialog.setInitialPattern(Signature.getSimpleName(getEnclosingTypeText()));

        if (dialog.open() == Window.OK) {
            return (IType) dialog.getFirstResult();
        }
        return null;
    }

    /**
     * Opens a selection dialog that allows to select a super class.
     *
     * @return returns the selected type or <code>null</code> if the dialog has been canceled. The caller typically sets
     *         the result to the super class input field.
     *         <p>
     *         Clients can override this method if they want to offer a different dialog.
     *         </p>
     * @since 3.2
     */
    protected IType chooseSuperClass() {
        IJavaProject project = getJavaProject();
        if (project == null) {
            return null;
        }

        IJavaElement[] elements = new IJavaElement[] {
            project
        };
        IJavaSearchScope scope = SearchEngine.createJavaSearchScope(elements);

        FilteredTypesSelectionDialog dialog = new FilteredTypesSelectionDialog(getShell(), false, getWizard().getContainer(), scope, IJavaSearchConstants.CLASS);
        dialog.setTitle(NewWizardMessages.NewTypeWizardPage_SuperClassDialog_title);
        dialog.setMessage(NewWizardMessages.NewTypeWizardPage_SuperClassDialog_message);
        dialog.setInitialPattern(getSuperClass());

        if (dialog.open() == Window.OK) {
            return (IType) dialog.getFirstResult();
        }
        return null;
    }

    /**
     * Opens a selection dialog that allows to select the super interfaces. The selected interfaces are directly added
     * to the wizard page using {@link #addSuperInterface(String)}.
     * <p>
     * Clients can override this method if they want to offer a different dialog.
     * </p>
     *
     * @since 3.2
     */
    protected void chooseSuperInterfaces() {
        IJavaProject project = getJavaProject();
        if (project == null) {
            return;
        }

        SuperInterfaceSelectionDialog dialog = new SuperInterfaceSelectionDialog(getShell(), getWizard().getContainer(), this, project);
        dialog.setTitle(getInterfaceDialogTitle());
        dialog.setMessage(NewWizardMessages.NewTypeWizardPage_InterfacesDialog_message);
        dialog.open();
    }

    private String getInterfaceDialogTitle() {
        if (fTypeKind == INTERFACE_TYPE)
            return NewWizardMessages.NewTypeWizardPage_InterfacesDialog_interface_title;
        return NewWizardMessages.NewTypeWizardPage_InterfacesDialog_class_title;
    }

    // ---- creation ----------------

    /**
     * Creates the new type using the entered field values.
     *
     * @param monitor a progress monitor to report progress.
     * @throws CoreException Thrown when the creation failed.
     * @throws InterruptedException Thrown when the operation was canceled.
     */
    public void createType(IProgressMonitor monitor) throws CoreException, InterruptedException {
        IProgressMonitor monitorInternal = monitor;
        if (monitorInternal == null) {
            monitorInternal = new NullProgressMonitor();
        }

        monitorInternal.beginTask(NewWizardMessages.NewTypeWizardPage_operationdesc, 8);

        IPackageFragmentRoot root = getPackageFragmentRoot();
        IPackageFragment pack = getPackageFragment();
        if (pack == null) {
            pack = root.getPackageFragment(""); //$NON-NLS-1$
        }

        if (!pack.exists()) {
            String packName = pack.getElementName();
            pack = root.createPackageFragment(packName, true, SubMonitor.convert(monitorInternal, 1));
        } else {
            monitorInternal.worked(1);
        }

        boolean needsSave;
        ICompilationUnit connectedCU = null;

        try {
            String typeName = getTypeNameWithoutParameters();

            boolean isInnerClass = isEnclosingTypeSelected();

            IType createdType;
            ImportsManager imports;
            int indent = 0;

            Set<String> existingImports;

            String lineDelimiter = null;
            if (!isInnerClass) {
                lineDelimiter = StubUtility.getLineDelimiterUsed(pack.getJavaProject());

                String cuName = getCompilationUnitName(typeName);
                ICompilationUnit parentCU = pack.createCompilationUnit(cuName, "", false, SubMonitor.convert(monitorInternal, 2)); //$NON-NLS-1$
                // create a working copy with a new owner

                needsSave = true;
                parentCU.becomeWorkingCopy(SubMonitor.convert(monitorInternal, 1)); // cu is now a (primary) working
                                                                                    // copy
                connectedCU = parentCU;

                IBuffer buffer = parentCU.getBuffer();

                String simpleTypeStub = constructSimpleTypeStub();
                String cuContent = constructCUContent(parentCU, simpleTypeStub, lineDelimiter);
                buffer.setContents(cuContent);

                CompilationUnit astRoot = createASTForImports(parentCU);
                existingImports = getExistingImports(astRoot);

                imports = new ImportsManager(astRoot);
                // add an import that will be removed again. Having this import solves 14661
                imports.addImport(JavaModelUtil.concatenateName(pack.getElementName(), typeName));

                String typeContent = constructTypeStub(parentCU, imports, lineDelimiter);

                int index = cuContent.lastIndexOf(simpleTypeStub);
                if (index == -1) {
                    AbstractTypeDeclaration typeNode = (AbstractTypeDeclaration) astRoot.types()
                        .get(0);
                    int start = ((ASTNode) typeNode.modifiers()
                        .get(0)).getStartPosition();
                    int end = typeNode.getStartPosition() + typeNode.getLength();
                    buffer.replace(start, end - start, typeContent);
                } else {
                    buffer.replace(index, simpleTypeStub.length(), typeContent);
                }

                createdType = parentCU.getType(typeName);
            } else {
                IType enclosingType = getEnclosingType();

                ICompilationUnit parentCU = enclosingType.getCompilationUnit();

                needsSave = !parentCU.isWorkingCopy();
                parentCU.becomeWorkingCopy(SubMonitor.convert(monitorInternal, 1)); // cu is now for sure (primary)
                                                                                    // a working copy
                connectedCU = parentCU;

                CompilationUnit astRoot = createASTForImports(parentCU);
                imports = new ImportsManager(astRoot);
                existingImports = getExistingImports(astRoot);

                // add imports that will be removed again. Having the imports solves 14661
                IType[] topLevelTypes = parentCU.getTypes();
                for (int i = 0; i < topLevelTypes.length; i++) {
                    imports.addImport(topLevelTypes[i].getFullyQualifiedName('.'));
                }

                lineDelimiter = StubUtility.getLineDelimiterUsed(enclosingType);
                StringBuffer content = new StringBuffer();

                String comment = getTypeComment(parentCU, lineDelimiter);
                if (comment != null) {
                    content.append(comment);
                    content.append(lineDelimiter);
                }

                content.append(constructTypeStub(parentCU, imports, lineDelimiter));
                IJavaElement sibling = null;
                if (enclosingType.isEnum()) {
                    IField[] fields = enclosingType.getFields();
                    if (fields.length > 0) {
                        for (int i = 0, max = fields.length; i < max; i++) {
                            if (!fields[i].isEnumConstant()) {
                                sibling = fields[i];
                                break;
                            }
                        }
                    }
                } else {
                    IJavaElement[] elems = enclosingType.getChildren();
                    sibling = elems.length > 0 ? elems[0] : null;
                }

                createdType = enclosingType.createType(content.toString(), sibling, false, SubMonitor.convert(monitorInternal, 2));

                indent = StubUtility.getIndentUsed(enclosingType) + 1;
            }
            if (monitorInternal.isCanceled()) {
                throw new InterruptedException();
            }

            // add imports for superclass/interfaces, so types can be resolved correctly

            ICompilationUnit cu = createdType.getCompilationUnit();

            imports.create(false, SubMonitor.convert(monitorInternal, 1));

            JavaModelUtil.reconcile(cu);

            if (monitorInternal.isCanceled()) {
                throw new InterruptedException();
            }

            // set up again
            CompilationUnit astRoot = createASTForImports(imports.getCompilationUnit());
            imports = new ImportsManager(astRoot);

            createTypeMembers(createdType, imports, SubMonitor.convert(monitorInternal, 1));

            // add imports
            imports.create(false, SubMonitor.convert(monitorInternal, 1));

            removeUnusedImports(cu, existingImports, false);

            JavaModelUtil.reconcile(cu);

            ISourceRange range = createdType.getSourceRange();

            IBuffer buf = cu.getBuffer();
            String originalContent = buf.getText(range.getOffset(), range.getLength());

            String formattedContent = CodeFormatterUtil.format(CodeFormatter.K_CLASS_BODY_DECLARATIONS, originalContent, indent, lineDelimiter, pack.getJavaProject());
            formattedContent = Strings.trimLeadingTabsAndSpaces(formattedContent);
            buf.replace(range.getOffset(), range.getLength(), formattedContent);
            if (!isInnerClass) {
                String fileComment = getFileComment(cu);
                if (fileComment != null && fileComment.length() > 0) {
                    buf.replace(0, 0, fileComment + lineDelimiter);
                }
            }
            fCreatedType = createdType;

            if (needsSave) {
                cu.commitWorkingCopy(true, SubMonitor.convert(monitorInternal, 1));
            } else {
                monitorInternal.worked(1);
            }

        } finally {
            if (connectedCU != null) {
                connectedCU.discardWorkingCopy();
            }
            monitorInternal.done();
        }
    }

    private CompilationUnit createASTForImports(ICompilationUnit cu) {
        ASTParser parser = ASTParser.newParser(ASTProvider.SHARED_AST_LEVEL);
        parser.setSource(cu);
        parser.setResolveBindings(false);
        parser.setFocalPosition(0);
        return (CompilationUnit) parser.createAST(null);
    }

    private Set<String> getExistingImports(CompilationUnit root) {
        @SuppressWarnings("unchecked")
        List<ImportDeclaration> imports = root.imports();
        Set<String> res = new HashSet<String>(imports.size());
        for (int i = 0; i < imports.size(); i++) {
            res.add(ASTNodes.asString(imports.get(i)));
        }
        return res;
    }

    private void removeUnusedImports(ICompilationUnit cu, Set<String> existingImports, boolean needsSave) throws CoreException {
        ASTParser parser = ASTParser.newParser(ASTProvider.SHARED_AST_LEVEL);
        parser.setSource(cu);
        parser.setResolveBindings(true);

        CompilationUnit root = (CompilationUnit) parser.createAST(null);
        if (root.getProblems().length == 0) {
            return;
        }

        @SuppressWarnings("unchecked")
        List<ImportDeclaration> importsDecls = root.imports();
        if (importsDecls.isEmpty()) {
            return;
        }
        ImportsManager imports = new ImportsManager(root);

        int importsEnd = ASTNodes.getExclusiveEnd(importsDecls.get(importsDecls.size() - 1));
        IProblem[] problems = root.getProblems();
        for (int i = 0; i < problems.length; i++) {
            IProblem curr = problems[i];
            if (curr.getSourceEnd() < importsEnd) {
                int id = curr.getID();
                if (id == IProblem.UnusedImport || id == IProblem.NotVisibleType) { // not visible problems hide unused
                                                                                    // -> remove both
                    int pos = curr.getSourceStart();
                    for (int k = 0; k < importsDecls.size(); k++) {
                        ImportDeclaration decl = importsDecls.get(k);
                        if (decl.getStartPosition() <= pos && pos < decl.getStartPosition() + decl.getLength()) {
                            if (existingImports.isEmpty() || !existingImports.contains(ASTNodes.asString(decl))) {
                                String name = decl.getName()
                                    .getFullyQualifiedName();
                                if (decl.isOnDemand()) {
                                    name += ".*"; //$NON-NLS-1$
                                }
                                if (decl.isStatic()) {
                                    imports.removeStaticImport(name);
                                } else {
                                    imports.removeImport(name);
                                }
                            }
                            break;
                        }
                    }
                }
            }
        }
        imports.create(needsSave, null);
    }

    /**
     * Uses the New Java file template from the code template page to generate a compilation unit with the given type
     * content.
     *
     * @param cu The new created compilation unit
     * @param typeContent The content of the type, including signature and type body.
     * @param lineDelimiter The line delimiter to be used.
     * @return String Returns the result of evaluating the new file template with the given type content.
     * @throws CoreException when fetching the file comment fails or fetching the content for the new compilation unit
     *             fails
     * @since 2.1
     */
    protected String constructCUContent(ICompilationUnit cu, String typeContent, String lineDelimiter) throws CoreException {
        String fileComment = getFileComment(cu, lineDelimiter);
        String typeComment = getTypeComment(cu, lineDelimiter);
        IPackageFragment pack = (IPackageFragment) cu.getParent();
        String content = CodeGeneration.getCompilationUnitContent(cu, fileComment, typeComment, typeContent, lineDelimiter);
        if (content != null) {
            ASTParser parser = ASTParser.newParser(ASTProvider.SHARED_AST_LEVEL);
            parser.setProject(cu.getJavaProject());
            parser.setSource(content.toCharArray());
            CompilationUnit unit = (CompilationUnit) parser.createAST(null);
            if ((pack.isDefaultPackage() || unit.getPackage() != null) && !unit.types()
                .isEmpty()) {
                return content;
            }
        }
        StringBuffer buf = new StringBuffer();
        if (!pack.isDefaultPackage()) {
            buf.append("package ") //$NON-NLS-1$
                .append(pack.getElementName())
                .append(';');
        }
        buf.append(lineDelimiter)
            .append(lineDelimiter);
        if (typeComment != null) {
            buf.append(typeComment)
                .append(lineDelimiter);
        }
        buf.append(typeContent);
        return buf.toString();
    }

    /**
     * Returns the created type or <code>null</code> is the type has not been created yet. The method only returns a
     * valid type after <code>createType</code> has been called.
     *
     * @return the created type
     * @see #createType(IProgressMonitor)
     */
    public IType getCreatedType() {
        return fCreatedType;
    }

    // ---- construct CU body----------------

    private void writeSuperClass(StringBuffer buf, ImportsManager imports) {
        String superclass = getSuperClass();
        if (fTypeKind == CLASS_TYPE && superclass.length() > 0 && !"java.lang.Object".equals(superclass)) { //$NON-NLS-1$
            buf.append(" extends "); //$NON-NLS-1$

            ITypeBinding binding = null;
            if (fCurrType != null) {
                binding = TypeContextChecker.resolveSuperClass(superclass, fCurrType, getSuperClassStubTypeContext());
            }
            if (binding != null) {
                buf.append(imports.addImport(binding));
            } else {
                buf.append(imports.addImport(superclass));
            }
        }
    }

    private void writeSuperInterfaces(StringBuffer buf, ImportsManager imports) {
        List<String> interfaces = getSuperInterfaces();
        int last = interfaces.size() - 1;
        if (last >= 0) {
            if (fTypeKind != INTERFACE_TYPE) {
                buf.append(" implements "); //$NON-NLS-1$
            } else {
                buf.append(" extends "); //$NON-NLS-1$
            }
            String[] intfs = interfaces.toArray(new String[0]);
            ITypeBinding[] bindings;
            if (fCurrType != null) {
                bindings = TypeContextChecker.resolveSuperInterfaces(intfs, fCurrType, getSuperInterfacesStubTypeContext());
            } else {
                bindings = new ITypeBinding[intfs.length];
            }
            for (int i = 0; i <= last; i++) {
                ITypeBinding binding = bindings[i];
                if (binding != null) {
                    buf.append(imports.addImport(binding));
                } else {
                    buf.append(imports.addImport(intfs[i]));
                }
                if (i < last) {
                    buf.append(',');
                }
            }
        }
    }

    private String constructSimpleTypeStub() {
        StringBuffer buf = new StringBuffer("public class "); //$NON-NLS-1$
        buf.append(getTypeName());
        buf.append("{ }"); //$NON-NLS-1$
        return buf.toString();
    }

    /*
     * Called from createType to construct the source for this type
     */
    protected String constructTypeStub(ICompilationUnit parentCU, ImportsManager imports, String lineDelimiter) throws CoreException {
        StringBuffer buf = new StringBuffer();

        constructTypeAnnotationStubs(buf, imports, lineDelimiter);

        int modifiers = getModifiers();
        buf.append(Flags.toString(modifiers));
        if (modifiers != 0) {
            buf.append(' ');
        }
        String type = ""; //$NON-NLS-1$
        String templateID = ""; //$NON-NLS-1$
        switch (fTypeKind) {
            case CLASS_TYPE :
                type = "class "; //$NON-NLS-1$
                templateID = CodeGeneration.CLASS_BODY_TEMPLATE_ID;
                break;
            case ENUM_TYPE :
                type = "enum "; //$NON-NLS-1$
                templateID = CodeGeneration.ENUM_BODY_TEMPLATE_ID;
                break;
            case ANNOTATION_TYPE :
                type = "@interface "; //$NON-NLS-1$
                templateID = CodeGeneration.ANNOTATION_BODY_TEMPLATE_ID;
                break;

            case INTERFACE_TYPE :
            default :
                type = "interface "; //$NON-NLS-1$
                templateID = CodeGeneration.INTERFACE_BODY_TEMPLATE_ID;
                break;
        }
        buf.append(type);
        buf.append(getTypeName());
        writeSuperClass(buf, imports);
        writeSuperInterfaces(buf, imports);

        buf.append(" {") //$NON-NLS-1$
            .append(lineDelimiter);
        String typeBody = CodeGeneration.getTypeBody(templateID, parentCU, getTypeName(), lineDelimiter);
        if (typeBody != null) {
            buf.append(typeBody);
        } else {
            buf.append(lineDelimiter);
        }
        buf.append('}')
            .append(lineDelimiter);
        return buf.toString();
    }

    /**
     * Construct the type annotations to add to the class. This base implementation does nothing, subclasses may extend.
     */
    @SuppressWarnings("unused")
    protected void constructTypeAnnotationStubs(StringBuffer buffer, ImportsManager imports, String lineDelimiter) {
    }

    /**
     * Hook method that gets called from <code>createType</code> to support adding of unanticipated methods, fields, and
     * inner types to the created type.
     * <p>
     * Implementers can use any methods defined on <code>IType</code> to manipulate the new type.
     * </p>
     * <p>
     * The source code of the new type will be formatted using the platform's formatter. Needed imports are added by the
     * wizard at the end of the type creation process using the given import manager.
     * </p>
     *
     * @param newType the new type created via <code>createType</code>
     * @param imports an import manager which can be used to add new imports
     * @param monitor a progress monitor to report progress. Must not be <code>null</code>
     * @throws CoreException thrown when creation of the type members failed
     * @see #createType(IProgressMonitor)
     */
    @SuppressWarnings("unused")
    protected void createTypeMembers(IType newType, final ImportsManager imports, IProgressMonitor monitor) throws CoreException {
        // default implementation does nothing
        // example would be
        // String mainMathod= "public void foo(Vector vec) {}"
        // createdType.createMethod(main, null, false, null);
        // imports.addImport("java.lang.Vector");
    }

    /**
     * @param parentCU the current compilation unit
     * @return returns the file template or <code>null</code>
     * @deprecated Instead of file templates, the new type code template specifies the stub for a compilation unit.
     */
    @SuppressWarnings("unused")
    @Deprecated
    protected String getFileComment(ICompilationUnit parentCU) {
        return null;
    }

    /**
     * Hook method that gets called from <code>createType</code> to retrieve a file comment. This default implementation
     * returns the content of the 'file comment' template or <code>null</code> if no comment should be created.
     *
     * @param parentCU the parent compilation unit
     * @param lineDelimiter the line delimiter to use
     * @return the file comment or <code>null</code> if a file comment is not desired
     * @throws CoreException when fetching the file comment fails
     * @since 3.1
     */
    protected String getFileComment(ICompilationUnit parentCU, String lineDelimiter) throws CoreException {
        if (isAddComments()) {
            return CodeGeneration.getFileComment(parentCU, lineDelimiter);
        }
        return null;

    }

    private boolean isValidComment(String template) {
        IScanner scanner = ToolFactory.createScanner(true, false, false, false);
        scanner.setSource(template.toCharArray());
        try {
            int next = scanner.getNextToken();
            while (TokenScanner.isComment(next)) {
                next = scanner.getNextToken();
            }
            return next == ITerminalSymbols.TokenNameEOF;
        } catch (InvalidInputException e) {
        }
        return false;
    }

    /**
     * Hook method that gets called from <code>createType</code> to retrieve a type comment. This default implementation
     * returns the content of the 'type comment' template.
     *
     * @param parentCU the parent compilation unit
     * @param lineDelimiter the line delimiter to use
     * @return the type comment or <code>null</code> if a type comment is not desired
     * @since 3.0
     */
    protected String getTypeComment(ICompilationUnit parentCU, String lineDelimiter) {
        if (isAddComments()) {
            try {
                StringBuffer typeName = new StringBuffer();
                if (isEnclosingTypeSelected()) {
                    typeName.append(getEnclosingType().getTypeQualifiedName('.'))
                        .append('.');
                }
                typeName.append(getTypeNameWithoutParameters());
                String[] typeParamNames = new String[0];
                String comment = CodeGeneration.getTypeComment(parentCU, typeName.toString(), typeParamNames, lineDelimiter);
                if (comment != null && isValidComment(comment)) {
                    return comment;
                }
            } catch (CoreException e) {
                JavaPlugin.log(e);
            }
        }
        return null;
    }

    /**
     * @param parentCU the current compilation unit
     * @return returns the template or <code>null</code>
     * @deprecated Use getTypeComment(ICompilationUnit, String)
     */
    @Deprecated
    protected String getTypeComment(ICompilationUnit parentCU) {
        if (StubUtility.doAddComments(parentCU.getJavaProject()))
            return getTypeComment(parentCU, StubUtility.getLineDelimiterUsed(parentCU));
        return null;
    }

    /**
     * @param name the name of the template
     * @param parentCU the current compilation unit
     * @return returns the template or <code>null</code>
     * @deprecated Use getTemplate(String,ICompilationUnit,int)
     */
    @Deprecated
    protected String getTemplate(String name, ICompilationUnit parentCU) {
        return getTemplate(name, parentCU, 0);
    }

    /**
     * Returns the string resulting from evaluation the given template in the context of the given compilation unit.
     * This accesses the normal template page, not the code templates. To use code templates use
     * <code>constructCUContent</code> to construct a compilation unit stub or getTypeComment for the comment of the
     * type.
     *
     * @param name the template to be evaluated
     * @param parentCU the templates evaluation context
     * @param pos a source offset into the parent compilation unit. The template is evaluated at the given source offset
     * @return return the template with the given name or <code>null</code> if the template could not be found.
     */
    protected String getTemplate(String name, ICompilationUnit parentCU, int pos) {
        try {
            Template template = JavaPlugin.getDefault()
                .getTemplateStore()
                .findTemplate(name);
            if (template != null) {
                return JavaContext.evaluateTemplate(template, parentCU, pos);
            }
        } catch (CoreException e) {
            JavaPlugin.log(e);
        } catch (BadLocationException e) {
            JavaPlugin.log(e);
        } catch (TemplateException e) {
            JavaPlugin.log(e);
        }
        return null;
    }

    /**
     * Creates the bodies of all unimplemented methods and constructors and adds them to the type. Method is typically
     * called by implementers of <code>NewTypeWizardPage</code> to add needed method and constructors.
     *
     * @param type the type for which the new methods and constructor are to be created
     * @param doConstructors if <code>true</code> unimplemented constructors are created
     * @param doUnimplementedMethods if <code>true</code> unimplemented methods are created
     * @param imports an import manager to add all needed import statements
     * @param monitor a progress monitor to report progress
     * @return the created methods.
     * @throws CoreException thrown when the creation fails.
     */
    protected IMethod[] createInheritedMethods(IType type, boolean doConstructors, boolean doUnimplementedMethods, ImportsManager imports, IProgressMonitor monitor) throws CoreException {
        final ICompilationUnit cu = type.getCompilationUnit();
        JavaModelUtil.reconcile(cu);
        IMethod[] typeMethods = type.getMethods();
        Set<String> handleIds = new HashSet<String>(typeMethods.length);
        for (int index = 0; index < typeMethods.length; index++)
            handleIds.add(typeMethods[index].getHandleIdentifier());
        ArrayList<IMethod> newMethods = new ArrayList<IMethod>();
        CodeGenerationSettings settings = JavaPreferencesSettings.getCodeGenerationSettings(type.getJavaProject());
        settings.createComments = isAddComments();
        ASTParser parser = ASTParser.newParser(ASTProvider.SHARED_AST_LEVEL);
        parser.setResolveBindings(true);
        parser.setSource(cu);
        CompilationUnit unit = (CompilationUnit) parser.createAST(SubMonitor.convert(monitor, 1));
        final ITypeBinding binding = ASTNodes.getTypeBinding(unit, type);
        if (binding != null) {
            if (doUnimplementedMethods) {
                AddUnimplementedMethodsOperation operation = new AddUnimplementedMethodsOperation(unit, binding, null, -1, false, true, false);
                operation.setCreateComments(isAddComments());
                operation.run(monitor);
                createImports(imports, operation.getCreatedImports());
            }
            if (doConstructors) {
                AddUnimplementedConstructorsOperation operation = new AddUnimplementedConstructorsOperation(unit, binding, null, -1, false, true, false);
                operation.setOmitSuper(true);
                operation.setCreateComments(isAddComments());
                operation.run(monitor);
                createImports(imports, operation.getCreatedImports());
            }
        }
        JavaModelUtil.reconcile(cu);
        typeMethods = type.getMethods();
        for (int index = 0; index < typeMethods.length; index++)
            if (!handleIds.contains(typeMethods[index].getHandleIdentifier()))
                newMethods.add(typeMethods[index]);
        IMethod[] methods = newMethods.toArray(new IMethod[0]);
        return methods;
    }

    private void createImports(ImportsManager imports, String[] createdImports) {
        for (int index = 0; index < createdImports.length; index++)
            imports.addImport(createdImports[index]);
    }

    // ---- creation ----------------

    /**
     * Returns the runnable that creates the type using the current settings. The returned runnable must be executed in
     * the UI thread.
     *
     * @return the runnable to create the new type
     */
    public IRunnableWithProgress getRunnable() {
        return new IRunnableWithProgress() {
            @Override
            public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
                try {
                    IProgressMonitor monitorInternal = monitor;
                    if (monitorInternal == null) {
                        monitorInternal = new NullProgressMonitor();
                    }
                    createType(monitorInternal);
                } catch (CoreException e) {
                    throw new InvocationTargetException(e);
                }
            }
        };
    }
}
