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.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.manipulation.CodeGeneration;
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.core.manipulation.StubUtility;
import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels;
import org.eclipse.jdt.internal.core.manipulation.util.Strings;
import org.eclipse.jdt.internal.corext.CorextMessages;
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.dom.ASTNodes;
import org.eclipse.jdt.internal.corext.dom.IASTSharedValues;
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.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.ValidateEditException;
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.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.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.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.ITextSelection;
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<>(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<>(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, null);
		}
		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<>(interfaces.size());
		for (InterfaceWrapper wrapper : interfaces) {
			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<>(interfacesNames.size());
		for (String interfacesName : interfacesNames) {
			interfaces.add(new InterfaceWrapper(interfacesName));
		}
		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) {
			if (project.exists() && packName.length() > 0) {
				try {
					@SuppressWarnings("null")
					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
				}
			}

			@SuppressWarnings("null")
			IPackageFragment frag = root.getPackageFragment(packName);
			fCurrPackage = frag;
			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(IASTSharedValues.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(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, //$NON-NLS-1$
					SubMonitor.convert(monitorInternal, 2));
				// 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 (IType topLevelType : topLevelTypes) {
					imports.addImport(topLevelType.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 (IField field : fields) {
							if (!field.isEnumConstant()) {
								sibling = field;
								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(IASTSharedValues.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<>(imports.size());
		for (ImportDeclaration import1 : imports) {
			res.add(ASTNodes.asString(import1));
		}
		return res;
	}

	private void removeUnusedImports(ICompilationUnit cu, Set<String> existingImports, boolean needsSave)
		throws CoreException {
		ASTParser parser = ASTParser.newParser(IASTSharedValues.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 (IProblem curr : problems) {
			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 (ImportDeclaration decl : importsDecls) {
						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(IASTSharedValues.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;
	}

	/**
	 * 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<>(typeMethods.length);
		for (IMethod typeMethod : typeMethods)
			handleIds.add(typeMethod.getHandleIdentifier());
		ArrayList<IMethod> newMethods = new ArrayList<>();
		CodeGenerationSettings settings = JavaPreferencesSettings.getCodeGenerationSettings(type.getJavaProject());
		settings.createComments = isAddComments();
		ASTParser parser = ASTParser.newParser(IASTSharedValues.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, null);
				operation.setOmitSuper(true);
				operation.setCreateComments(isAddComments());
				operation.run(monitor);
				createImports(imports, operation.getCreatedImports());
			}
		}
		JavaModelUtil.reconcile(cu);
		typeMethods = type.getMethods();
		for (IMethod typeMethod : typeMethods)
			if (!handleIds.contains(typeMethod.getHandleIdentifier()))
				newMethods.add(typeMethod);
		IMethod[] methods = newMethods.toArray(new IMethod[0]);
		return methods;
	}

	private void createImports(ImportsManager imports, String[] createdImports) {
		for (String createdImport : createdImports)
			imports.addImport(createdImport);
	}

	// ---- 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 monitor -> {
			try {
				IProgressMonitor monitorInternal = monitor;
				if (monitorInternal == null) {
					monitorInternal = new NullProgressMonitor();
				}
				createType(monitorInternal);
			} catch (CoreException e) {
				throw new InvocationTargetException(e);
			}
		};
	}
}
