/******************************************************************************* * Copyright (c) 2004, 2016 QNX Software Systems and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * QNX Software Systems - initial API and implementation * IBM Corporation * Warren Paul (Nokia) - 174238 * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.ui.wizards; import java.net.URI; import java.util.List; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; 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.MultiStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.window.Window; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.FocusListener; 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.Text; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.cdt.core.CConventions; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.browser.AllTypesCache; import org.eclipse.cdt.core.browser.IQualifiedTypeName; import org.eclipse.cdt.core.browser.ITypeInfo; import org.eclipse.cdt.core.browser.ITypeSearchScope; import org.eclipse.cdt.core.browser.QualifiedTypeName; import org.eclipse.cdt.core.browser.TypeSearchScope; import org.eclipse.cdt.core.browser.TypeUtil; import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType; import org.eclipse.cdt.core.dom.ast.cpp.ICPPNamespace; import org.eclipse.cdt.core.model.CModelException; import org.eclipse.cdt.core.model.CoreModel; import org.eclipse.cdt.core.model.ICContainer; import org.eclipse.cdt.core.model.ICElement; import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.core.model.ISourceRoot; import org.eclipse.cdt.core.parser.IScannerInfo; import org.eclipse.cdt.core.parser.IScannerInfoProvider; import org.eclipse.cdt.core.parser.ast.ASTAccessVisibility; import org.eclipse.cdt.ui.CUIPlugin; import org.eclipse.cdt.utils.PathUtil; import org.eclipse.cdt.internal.ui.dialogs.StatusInfo; import org.eclipse.cdt.internal.ui.dialogs.StatusUtil; import org.eclipse.cdt.internal.ui.util.SWTUtil; import org.eclipse.cdt.internal.ui.wizards.NewElementWizardPage; import org.eclipse.cdt.internal.ui.wizards.SourceFolderSelectionDialog; import org.eclipse.cdt.internal.ui.wizards.classwizard.BaseClassInfo; import org.eclipse.cdt.internal.ui.wizards.classwizard.BaseClassesListDialogField; import org.eclipse.cdt.internal.ui.wizards.classwizard.ConstructorMethodStub; import org.eclipse.cdt.internal.ui.wizards.classwizard.DestructorMethodStub; import org.eclipse.cdt.internal.ui.wizards.classwizard.IBaseClassInfo; import org.eclipse.cdt.internal.ui.wizards.classwizard.IMethodStub; import org.eclipse.cdt.internal.ui.wizards.classwizard.MethodStubsListDialogField; import org.eclipse.cdt.internal.ui.wizards.classwizard.NamespaceSelectionDialog; import org.eclipse.cdt.internal.ui.wizards.classwizard.NewBaseClassSelectionDialog; import org.eclipse.cdt.internal.ui.wizards.classwizard.NewBaseClassSelectionDialog.ITypeSelectionListener; import org.eclipse.cdt.internal.ui.wizards.classwizard.NewClassCodeGenerator; import org.eclipse.cdt.internal.ui.wizards.classwizard.NewClassWizardMessages; import org.eclipse.cdt.internal.ui.wizards.classwizard.NewClassWizardPrefs; import org.eclipse.cdt.internal.ui.wizards.classwizard.NewClassWizardUtil; import org.eclipse.cdt.internal.ui.wizards.classwizard.SourceFileSelectionDialog; import org.eclipse.cdt.internal.ui.wizards.dialogfields.DialogField; import org.eclipse.cdt.internal.ui.wizards.dialogfields.IDialogFieldListener; import org.eclipse.cdt.internal.ui.wizards.dialogfields.IListAdapter; import org.eclipse.cdt.internal.ui.wizards.dialogfields.IStringButtonAdapter; import org.eclipse.cdt.internal.ui.wizards.dialogfields.LayoutUtil; import org.eclipse.cdt.internal.ui.wizards.dialogfields.ListDialogField; import org.eclipse.cdt.internal.ui.wizards.dialogfields.SelectionButtonDialogField; import org.eclipse.cdt.internal.ui.wizards.dialogfields.Separator; import org.eclipse.cdt.internal.ui.wizards.dialogfields.StringButtonDialogField; import org.eclipse.cdt.internal.ui.wizards.dialogfields.StringDialogField; import org.eclipse.cdt.internal.ui.wizards.filewizard.NewSourceFileGenerator; public class NewClassCreationWizardPage extends NewElementWizardPage { protected static final String PAGE_NAME = "NewClassWizardPage"; //$NON-NLS-1$ protected static final int MAX_FIELD_CHARS = 50; // Dialog setting keys. private static final String KEY_NAMESPACE_SELECTED = "namespaceSelected"; //$NON-NLS-1$ private static final String KEY_NAMESPACE = "namespace"; //$NON-NLS-1$ private static final String KEY_TEST_FILE_SELECTED = "testFileSelected"; //$NON-NLS-1$ private static final String KEY_STUB_SELECTED = "stubSelected"; //$NON-NLS-1$ private static final String KEY_STUB_VIRTUAL = "stubVirtual"; //$NON-NLS-1$ private static final String KEY_STUB_INLINE = "stubInline"; //$NON-NLS-1$ // Field IDs protected static final int SOURCE_FOLDER_ID = 1; protected static final int NAMESPACE_ID = 2; protected static final int CLASS_NAME_ID = 4; protected static final int BASE_CLASSES_ID = 8; protected static final int METHOD_STUBS_ID = 16; protected static final int HEADER_FILE_ID = 32; protected static final int SOURCE_FILE_ID = 64; /** @since 5.3 */ protected static final int TEST_FILE_ID = 128; protected static final int ALL_FIELDS = SOURCE_FOLDER_ID | NAMESPACE_ID | CLASS_NAME_ID | BASE_CLASSES_ID | METHOD_STUBS_ID | HEADER_FILE_ID | SOURCE_FILE_ID | TEST_FILE_ID; protected int fLastFocusedField = 0; protected StringButtonDialogField fSourceFolderDialogField; protected SelectionButtonDialogField fNamespaceSelection; protected StringButtonDialogField fNamespaceDialogField; protected StringDialogField fClassNameDialogField; protected BaseClassesListDialogField fBaseClassesDialogField; protected MethodStubsListDialogField fMethodStubsDialogField; protected StringButtonDialogField fHeaderFileDialogField; protected StringButtonDialogField fSourceFileDialogField; /** @since 5.3 */ protected StringButtonDialogField fTestFileDialogField; /** @since 5.3 */ protected SelectionButtonDialogField fTestFileSelection; /** @since 5.3 */ protected boolean fHeaderFileDerivedFromClassName; /** @since 5.3 */ protected boolean fSourceFileDerivedFromClassName; /** @since 5.3 */ protected boolean fTestFileDerivedFromClassName; protected IStatus fSourceFolderStatus; protected IStatus fNamespaceStatus; protected IStatus fClassNameStatus; protected IStatus fBaseClassesStatus; protected IStatus fMethodStubsStatus; protected IStatus fHeaderFileStatus; protected IStatus fSourceFileStatus; /** @since 5.3 */ protected IStatus fTestFileStatus; protected final IStatus STATUS_OK = new StatusInfo(); protected IFile fCreatedHeaderFile; protected IFile fCreatedSourceFile; /** @since 5.3 */ protected IFile fCreatedTestFile; protected ICElement fCreatedClass; /** * This flag isFirstTime is used to keep a note * that the class creation wizard has just been * created. */ private boolean isFirstTime = false; /** * Constructor for NewClassCreationWizardPage */ public NewClassCreationWizardPage() { super(PAGE_NAME); setTitle(NewClassWizardMessages.NewClassCreationWizardPage_title); setDescription(NewClassWizardMessages.NewClassCreationWizardPage_description); SourceFolderFieldAdapter sourceFolderAdapter = new SourceFolderFieldAdapter(); fSourceFolderDialogField = new StringButtonDialogField(sourceFolderAdapter); fSourceFolderDialogField.setDialogFieldListener(sourceFolderAdapter); fSourceFolderDialogField.setLabelText(NewClassWizardMessages.NewClassCreationWizardPage_sourceFolder_label); fSourceFolderDialogField.setButtonLabel(NewClassWizardMessages.NewClassCreationWizardPage_sourceFolder_button); NamespaceFieldAdapter namespaceAdapter = new NamespaceFieldAdapter(); fNamespaceSelection = new SelectionButtonDialogField(SWT.CHECK); fNamespaceSelection.setDialogFieldListener(namespaceAdapter); fNamespaceSelection.setLabelText(NewClassWizardMessages.NewClassCreationWizardPage_namespace_label); fNamespaceDialogField = new StringButtonDialogField(namespaceAdapter); fNamespaceDialogField.setDialogFieldListener(namespaceAdapter); fNamespaceDialogField.setButtonLabel(NewClassWizardMessages.NewClassCreationWizardPage_namespace_button); ClassNameFieldAdapter classAdapter = new ClassNameFieldAdapter(); fClassNameDialogField = new StringDialogField(); fClassNameDialogField.setDialogFieldListener(classAdapter); fClassNameDialogField.setLabelText(NewClassWizardMessages.NewClassCreationWizardPage_className_label); BaseClassesFieldAdapter baseClassesAdapter = new BaseClassesFieldAdapter(); fBaseClassesDialogField = new BaseClassesListDialogField(NewClassWizardMessages.NewClassCreationWizardPage_baseClasses_label, baseClassesAdapter); MethodStubsFieldAdapter methodStubsAdapter = new MethodStubsFieldAdapter(); fMethodStubsDialogField = new MethodStubsListDialogField(NewClassWizardMessages.NewClassCreationWizardPage_methodStubs_label, methodStubsAdapter); FileGroupFieldAdapter fileGroupAdapter = new FileGroupFieldAdapter(); fHeaderFileDialogField = new StringButtonDialogField(fileGroupAdapter); fHeaderFileDialogField.setDialogFieldListener(fileGroupAdapter); fHeaderFileDialogField.setLabelText(NewClassWizardMessages.NewClassCreationWizardPage_headerFile_label); fHeaderFileDialogField.setButtonLabel(NewClassWizardMessages.NewClassCreationWizardPage_headerFile_button); fSourceFileDialogField = new StringButtonDialogField(fileGroupAdapter); fSourceFileDialogField.setDialogFieldListener(fileGroupAdapter); fSourceFileDialogField.setLabelText(NewClassWizardMessages.NewClassCreationWizardPage_sourceFile_label); fSourceFileDialogField.setButtonLabel(NewClassWizardMessages.NewClassCreationWizardPage_sourceFile_button); fTestFileSelection = new SelectionButtonDialogField(SWT.CHECK); fTestFileSelection.setDialogFieldListener(fileGroupAdapter); fTestFileSelection.setLabelText(NewClassWizardMessages.NewClassCreationWizardPage_testFile_label); fTestFileDialogField = new StringButtonDialogField(fileGroupAdapter); fTestFileDialogField.setDialogFieldListener(fileGroupAdapter); fTestFileDialogField.setButtonLabel(NewClassWizardMessages.NewClassCreationWizardPage_testFile_button); fHeaderFileDerivedFromClassName = true; fSourceFileDerivedFromClassName = true; fTestFileDerivedFromClassName = true; fSourceFolderStatus = STATUS_OK; fNamespaceStatus = STATUS_OK; fClassNameStatus = STATUS_OK; fBaseClassesStatus = STATUS_OK; fMethodStubsStatus = STATUS_OK; fHeaderFileStatus = STATUS_OK; fSourceFileStatus = STATUS_OK; fTestFileStatus = STATUS_OK; fLastFocusedField = 0; isFirstTime = true; } // -------- UI Creation --------- /* (non-Javadoc) * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite) */ @Override public void createControl(Composite parent) { initializeDialogUnits(parent); Composite composite = new Composite(parent, SWT.NONE); int nColumns = 4; GridLayout layout = new GridLayout(); layout.numColumns = nColumns; composite.setLayout(layout); composite.setLayoutData(new GridData(GridData.FILL_BOTH)); composite.setFont(parent.getFont()); createSourceFolderControls(composite, nColumns); createNamespaceControls(composite, nColumns); createSeparator(composite, nColumns); createClassNameControls(composite, nColumns); createBaseClassesControls(composite, nColumns); createMethodStubsControls(composite, nColumns); createSeparator(composite, nColumns); createFileControls(composite, nColumns); composite.layout(); setErrorMessage(null); setMessage(null); setControl(composite); } /** * 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 necessary controls (label, text field and browse button) to edit * the source folder location. The method expects that the parent composite * uses a <code>GridLayout</code> as its layout manager and that the * grid layout has at least 3 columns. * * @param parent the parent composite * @param nColumns the number of columns to span. This number must be * greater or equal three */ protected void createSourceFolderControls(Composite parent, int nColumns) { fSourceFolderDialogField.doFillIntoGrid(parent, nColumns); Text textControl = fSourceFolderDialogField.getTextControl(null); LayoutUtil.setWidthHint(textControl, getMaxFieldWidth()); textControl.addFocusListener(new StatusFocusListener(SOURCE_FOLDER_ID)); } /** * Creates the controls for the namespace 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 createNamespaceControls(Composite composite, int nColumns) { Composite tabGroup= new Composite(composite, SWT.NONE); GridLayout layout= new GridLayout(); layout.marginWidth= 0; layout.marginHeight= 0; tabGroup.setLayout(layout); fNamespaceSelection.doFillIntoGrid(tabGroup, 1); Text textControl= fNamespaceDialogField.getTextControl(composite); GridData gd= new GridData(GridData.FILL_HORIZONTAL); gd.widthHint= getMaxFieldWidth(); gd.horizontalSpan= 2; textControl.setLayoutData(gd); textControl.addFocusListener(new StatusFocusListener(NAMESPACE_ID)); Button button= fNamespaceDialogField.getChangeControl(composite); gd= new GridData(GridData.HORIZONTAL_ALIGN_FILL); gd.widthHint = SWTUtil.getButtonWidthHint(button); button.setLayoutData(gd); } /** * Creates the controls for the class 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 createClassNameControls(Composite composite, int nColumns) { fClassNameDialogField.doFillIntoGrid(composite, nColumns - 1); DialogField.createEmptySpace(composite); Text textControl = fClassNameDialogField.getTextControl(null); LayoutUtil.setWidthHint(textControl, getMaxFieldWidth()); textControl.addFocusListener(new StatusFocusListener(CLASS_NAME_ID)); } /** * Creates the controls for the base classes 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 createBaseClassesControls(Composite composite, int nColumns) { fBaseClassesDialogField.doFillIntoGrid(composite, nColumns); Control listControl = fBaseClassesDialogField.getListControl(null); LayoutUtil.setVerticalGrabbing(listControl, false); listControl.addFocusListener(new StatusFocusListener(BASE_CLASSES_ID)); } /** * Creates the controls for the method stubs 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 createMethodStubsControls(Composite composite, int nColumns) { fMethodStubsDialogField.doFillIntoGrid(composite, nColumns); Control listControl = fMethodStubsDialogField.getListControl(null); LayoutUtil.setHeightHint(listControl, convertHeightInCharsToPixels(6) + convertHeightInCharsToPixels(1) / 2); LayoutUtil.setVerticalGrabbing(listControl, false); listControl.addFocusListener(new StatusFocusListener(METHOD_STUBS_ID)); } /** * Creates the controls for the file name fields. Expects a <code>GridLayout</code> with * at least 4 columns. * * @param composite the parent composite * @param nColumns number of columns to span */ protected void createFileControls(Composite composite, int nColumns) { fHeaderFileDialogField.doFillIntoGrid(composite, nColumns); Text textControl = fHeaderFileDialogField.getTextControl(null); LayoutUtil.setWidthHint(textControl, getMaxFieldWidth()); textControl.addFocusListener(new StatusFocusListener(HEADER_FILE_ID)); fSourceFileDialogField.doFillIntoGrid(composite, nColumns); textControl = fSourceFileDialogField.getTextControl(null); LayoutUtil.setWidthHint(textControl, getMaxFieldWidth()); textControl.addFocusListener(new StatusFocusListener(SOURCE_FILE_ID)); Composite tabGroup = new Composite(composite, SWT.NONE); GridLayout layout = new GridLayout(); layout.marginWidth = 0; layout.marginHeight = 0; tabGroup.setLayout(layout); fTestFileSelection.doFillIntoGrid(tabGroup, 1); textControl = fTestFileDialogField.getTextControl(composite); GridData gd = new GridData(GridData.FILL_HORIZONTAL); gd.widthHint = getMaxFieldWidth(); gd.horizontalSpan = 2; textControl.setLayoutData(gd); textControl.addFocusListener(new StatusFocusListener(TEST_FILE_ID)); Button button = fTestFileDialogField.getChangeControl(composite); gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL); gd.widthHint = SWTUtil.getButtonWidthHint(button); button.setLayoutData(gd); } /** * The wizard owning this page is responsible for calling this method with the * current selection. The selection is used to initialize the fields of the wizard * page. * * @param selection used to initialize the fields */ public void init(IStructuredSelection selection) { if (fDialogSettings == null) { fDialogSettings = getDialogSettings().getSection(PAGE_NAME); if (fDialogSettings == null) { fDialogSettings = getDialogSettings().addNewSection(PAGE_NAME); } } ICElement celem = getInitialCElement(selection); String namespace = null; if (celem != null) { ICElement ns = NewClassWizardUtil.getNamespace(celem); if (ns != null) { namespace = TypeUtil.getFullyQualifiedName(ns).toString(); if (namespace != null && namespace.length() == 0) { namespace = null; } } } if (namespace == null) { namespace = fDialogSettings.get(KEY_NAMESPACE); } setNamespaceText(namespace, false); setNamespaceSelection(namespace != null || fDialogSettings.getBoolean(KEY_NAMESPACE_SELECTED), true); IPath folderPath = null; if (celem != null) { ICContainer folder = NewClassWizardUtil.getSourceFolder(celem); if (folder == null) { ICProject cproject = celem.getCProject(); if (cproject != null) { folder = NewClassWizardUtil.getFirstSourceRoot(cproject); } } if (folder != null) { folderPath = folder.getResource().getFullPath(); } } setSourceFolderFullPath(folderPath, false); String className = null; ITextSelection textSel = getEditorTextSelection(); if (textSel != null) { String text = textSel.getText(); if (text != null && text.length() > 0 && CConventions.validateClassName(text).isOK()) { className = text; } } // Forcing update will also populate default file names. setClassName(className, true); IMethodStub[] stubs = getDefaultMethodStubs(); for (int i = 0; i < stubs.length; ++i) { IMethodStub stub = stubs[i]; if (stub.canModifyVirtual()) { stub.setVirtual(getBooleanSettingWithDefault(KEY_STUB_VIRTUAL + i, stub.isVirtual())); } if (stub.canModifyInline()) { stub.setInline(getBooleanSettingWithDefault(KEY_STUB_INLINE + i, stub.isInline())); } addMethodStub(stub, getBooleanSettingWithDefault(KEY_STUB_SELECTED + i, true)); } setTestFileSelection(fDialogSettings.getBoolean(KEY_TEST_FILE_SELECTED), true); handleFieldChanged(ALL_FIELDS); } private boolean getBooleanSettingWithDefault(String key, boolean defaultValue) { String value = fDialogSettings.get(key); if (value == null) { return defaultValue; } return Boolean.valueOf(value); } /** * Attempts to extract a C Element from the initial selection. * * @param selection the initial selection * @return a C Element, or <code>null</code> if not available */ protected ICElement getInitialCElement(IStructuredSelection selection) { ICElement celem = NewClassWizardUtil.getCElementFromSelection(selection); if (celem == null) { celem = NewClassWizardUtil.getCElementFromEditor(); } if (celem == null || celem.getElementType() == ICElement.C_MODEL) { try { ICProject[] projects = CoreModel.create(NewClassWizardUtil.getWorkspaceRoot()).getCProjects(); if (projects.length == 1) { celem = projects[0]; } } catch (CModelException e) { CUIPlugin.log(e); } } return celem; } /** * Returns the recommended maximum width for text fields (in pixels). This * method requires that createContent has been called before this method is * call. Subclasses may override to change the maximum width for text * fields. * * @return the recommended maximum width for text fields. */ protected int getMaxFieldWidth() { return convertWidthInCharsToPixels(MAX_FIELD_CHARS); } /** * Returns the text selection of the current editor. <code>null</code> is returned * when the current editor does not have focus or does not return a text selection. * * @return the selection of the current editor, or <code>null</code>. */ protected ITextSelection getEditorTextSelection() { IWorkbenchPage page = CUIPlugin.getActivePage(); if (page != null) { IWorkbenchPart part = page.getActivePart(); if (part instanceof IEditorPart) { ISelectionProvider selectionProvider = part.getSite().getSelectionProvider(); if (selectionProvider != null) { ISelection selection = selectionProvider.getSelection(); if (selection instanceof ITextSelection) { return (ITextSelection) selection; } } } } return null; } /** * Returns the method stubs to display in the wizard. * * @return array of method stubs * * @noreference This method is not intended to be referenced by clients. * @nooverride This method is not intended to be re-implemented or extended by clients. */ protected IMethodStub[] getDefaultMethodStubs() { return new IMethodStub[] { new ConstructorMethodStub(), new DestructorMethodStub() }; } /** * Returns the text entered into the source folder input field. * * @return the source folder */ public String getSourceFolderText() { return fSourceFolderDialogField.getText().trim(); } /** * Sets the text of the source folder input field. * * @param folder the folder name * @param update <code>true</code> if the dialog should be updated */ public void setSourceFolderText(String folder, boolean update) { fSourceFolderDialogField.setTextWithoutUpdate(folder != null ? folder : ""); //$NON-NLS-1$ if (update) { fSourceFolderDialogField.dialogFieldChanged(); } } /** * Returns the current source folder as a path. * * @return the source folder path */ protected IPath getSourceFolderFullPath() { String text = getSourceFolderText(); if (text.length() > 0) return new Path(text).makeAbsolute(); return null; } /** * Sets the source folder from the given path. * * @param folderPath the source folder path * @param update <code>true</code> if the dialog should be updated */ protected void setSourceFolderFullPath(IPath folderPath, boolean update) { String str = (folderPath != null) ? folderPath.makeRelative().toString() : ""; //.makeRelative().toString(); //$NON-NLS-1$ setSourceFolderText(str, update); } /** * Returns the current project, based on the current source folder. * * @return the current project */ protected ICProject getCurrentProject() { IPath folderPath = getSourceFolderFullPath(); if (folderPath != null) { return toCProject(PathUtil.getEnclosingProject(folderPath)); } return null; } private ICProject toCProject(IProject enclosingProject) { if (enclosingProject != null) return CoreModel.getDefault().create(enclosingProject); return null; } /** * Returns the text entered into the namespace input field. * * @return the namespace */ public String getNamespaceText() { return fNamespaceDialogField.getText().trim(); } /** * Sets the text of the namespace input field. * * @param namespace the namespace name * @param update <code>true</code> if the dialog should be updated */ public void setNamespaceText(String namespace, boolean update) { fNamespaceDialogField.setTextWithoutUpdate(namespace != null ? namespace : ""); //$NON-NLS-1$ if (update) { fNamespaceDialogField.dialogFieldChanged(); } } /** * Returns the selection state of the namespace checkbox. * * @return the selection state of the namespace checkbox */ public boolean isNamespaceSelected() { return fNamespaceSelection.isSelected(); } /** * Sets the namespace checkbox's selection state. * * @param isSelected the checkbox's selection state * @param canBeModified if <code>true</code> the checkbox is * modifiable; otherwise it is read-only. */ public void setNamespaceSelection(boolean isSelected, boolean canBeModified) { fNamespaceSelection.setSelection(isSelected); fNamespaceSelection.setEnabled(canBeModified); updateNamespaceEnableState(); } /** * Updates the enable state of the namespace button. */ private void updateNamespaceEnableState() { fNamespaceDialogField.setEnabled(isNamespaceSelected()); } /** * Returns the class name entered into the class input field. * * @return the class name */ public String getClassName() { return fClassNameDialogField.getText().trim(); } /** * Sets the text of the class name input field. * * @param name the new class name * @param update <code>true</code> if the dialog should be updated */ public void setClassName(String name, boolean update) { fClassNameDialogField.setTextWithoutUpdate(name != null ? name : ""); //$NON-NLS-1$ if (update) { fClassNameDialogField.dialogFieldChanged(); } } /** * Returns the currently selected (checked) method stubs. * * @return array of <code>IMethodStub</code> or empty array if none selected. * * @noreference This method is not intended to be referenced by clients. * @nooverride This method is not intended to be re-implemented or extended by clients. */ protected IMethodStub[] getSelectedMethodStubs() { return fMethodStubsDialogField.getCheckedMethodStubs(); } /** * Adds a method stub to the method stubs field. * * @param methodStub the method stub to add * @param selected <code>true</code> if the stub is initially selected (checked) * * @noreference This method is not intended to be referenced by clients. * @nooverride This method is not intended to be re-implemented or extended by clients. */ protected void addMethodStub(IMethodStub methodStub, boolean selected) { fMethodStubsDialogField.addMethodStub(methodStub, selected); } /** * Returns the contents of the base classes field. * * @return array of <code>IBaseClassInfo</code> * * @noreference This method is not intended to be referenced by clients. * @nooverride This method is not intended to be re-implemented or extended by clients. */ protected IBaseClassInfo[] getBaseClasses() { List<IBaseClassInfo> classesList = fBaseClassesDialogField.getElements(); return classesList.toArray(new IBaseClassInfo[classesList.size()]); } /** * Adds a base class to the base classes field. * @param newBaseClass the new base class * @param access the access visibility (public/private/protected) * @param isVirtual <code>true</code> if the inheritance is virtual */ protected void addBaseClass(ITypeInfo newBaseClass, ASTAccessVisibility access, boolean isVirtual) { // Check if already exists. List<IBaseClassInfo> baseClasses = fBaseClassesDialogField.getElements(); if (baseClasses != null) { for (IBaseClassInfo baseClassInfo : baseClasses) { BaseClassInfo info = (BaseClassInfo) baseClassInfo; if (info.getType().equals(newBaseClass)) { // already added return; } } } if (verifyBaseClasses()) { NewClassWizardUtil.resolveClassLocation(newBaseClass, getContainer()); } fBaseClassesDialogField.addBaseClass(new BaseClassInfo(newBaseClass, access, isVirtual)); } /** * Sets the use test file creation checkbox's selection state. * * @param isSelected the checkbox's selection state * @param canBeModified if <code>true</code> the checkbox is modifiable; otherwise it is read-only. * @since 5.3 */ public void setTestFileSelection(boolean isSelected, boolean canBeModified) { fTestFileSelection.setSelection(isSelected); fTestFileSelection.setEnabled(canBeModified); updateTestFileEnableState(); } /** * Updates the enable state of test file name text box. */ private void updateTestFileEnableState() { fTestFileDialogField.setEnabled(fTestFileSelection.isSelected()); } /** * Returns the text entered into the header file input field. * * @return the header file */ public String getHeaderFileText() { return fHeaderFileDialogField.getText().trim(); } /** * Sets the text of the header file input field. * * @param header the header file name * @param update <code>true</code> if the dialog should be updated */ public void setHeaderFileText(String header, boolean update) { setFileText(fHeaderFileDialogField, header, update); } /** * Returns the current header file as a path. * * @return the header file path */ protected IPath getHeaderFileFullPath() { return getFilePath(getHeaderFileText()); } /** * Sets the header file from the given path. * * @param path the header file path * @param update <code>true</code> if the dialog should be updated */ protected void setHeaderFileFullPath(IPath path, boolean update) { setFileFullPath(fHeaderFileDialogField, path, update); } /** * Returns the text entered into the source file input field. * * @return the source file */ public String getSourceFileText() { return fSourceFileDialogField.getText().trim(); } /** * Sets the text of the source file input field. * * @param source the source file name * @param update <code>true</code> if the dialog should be updated */ public void setSourceFileText(String source, boolean update) { setFileText(fSourceFileDialogField, source, update); } /** * Returns the current source file as a path. * * @return the source file path */ protected IPath getSourceFileFullPath() { return getFilePath(getSourceFileText()); } /** * Sets the source file from the given path. * * @param path the source file path * @param update <code>true</code> if the dialog should be updated */ protected void setSourceFileFullPath(IPath path, boolean update) { setFileFullPath(fSourceFileDialogField, path, update); } /** * Returns the text entered into the source file input field. * * @return the source file * @since 5.3 */ public String getTestFileText() { return fTestFileDialogField.isEnabled() ? fTestFileDialogField.getText().trim() : null; } /** * Sets the text of the test file input field. * * @param testFile the test file name * @param update <code>true</code> if the dialog should be updated * @since 5.3 */ public void setTestFileText(String testFile, boolean update) { setFileText(fTestFileDialogField, testFile, update); } /** * Returns the current test file as a path. Returns {@code null} if creation of test file * is disabled. * * @return the test file path, or {@code null} if creation of test file is disabled. * @since 5.3 */ protected IPath getTestFileFullPath() { return getFilePath(getTestFileText()); } /** * Returns a path corresponding to a file name. * @param filename the name of a header, a source, or a test file. Can be {@code null}. * @return the corresponding path, or {@code null} if the filename is {@code null}. */ private IPath getFilePath(String filename) { if (filename == null || filename.length() == 0) { return null; } IPath path = new Path(filename); if (!path.isAbsolute()) { IPath folderPath = getSourceFolderFullPath(); if (folderPath != null) path = folderPath.append(path); } return path; } /** * Sets a file name field to a given value. * * @param field the field to set * @param filename the new value of the field * @param update <code>true</code> if the dialog should be updated */ private void setFileText(StringButtonDialogField field, String filename, boolean update) { field.setTextWithoutUpdate(filename != null ? filename : ""); //$NON-NLS-1$ if (update) { field.dialogFieldChanged(); } } /** * Sets a file name field from the given path. * * @param path the file path * @param update <code>true</code> if the dialog should be updated */ private void setFileFullPath(StringButtonDialogField field, IPath path, boolean update) { String str = null; if (path != null) { IPath sourceFolder = getSourceFolderFullPath(); if (sourceFolder != null) { IPath relativePath = PathUtil.makeRelativePath(path, sourceFolder); if (relativePath != null) path = relativePath; } str = path.makeRelative().toString(); } setFileText(field, str, update); } /** * Sets the test file from the given path. * * @param path the test file path * @param update <code>true</code> if the dialog should be updated * @since 5.3 */ protected void setTestFileFullPath(IPath path, boolean update) { setFileFullPath(fTestFileDialogField, path, update); } /* * @see WizardPage#becomesVisible */ @Override public void setVisible(boolean visible) { super.setVisible(visible); if (visible) { setFocus(); } } /** * Sets the focus on the class name input field. */ protected void setFocus() { fClassNameDialogField.setFocus(); } // ----------- UI Validation ---------- /** * Causes doStatusUpdate() to be called whenever the focus changes. * Remembers the last focused field. */ private final class StatusFocusListener implements FocusListener { private int fieldID; public StatusFocusListener(int fieldID) { this.fieldID = fieldID; } @Override public void focusGained(FocusEvent e) { if (fLastFocusedField != this.fieldID) { fLastFocusedField = this.fieldID; if (isFirstTime) { isFirstTime = false; return; } doStatusUpdate(); } } @Override public void focusLost(FocusEvent e) { if (fLastFocusedField != 0) { fLastFocusedField = 0; doStatusUpdate(); } } } /** * Handles changes to the source folder field */ private final class SourceFolderFieldAdapter implements IStringButtonAdapter, IDialogFieldListener { @Override public void changeControlPressed(DialogField field) { IPath oldFolderPath = getSourceFolderFullPath(); IPath newFolderPath = chooseSourceFolder(oldFolderPath); if (newFolderPath != null) { setSourceFolderFullPath(newFolderPath, false); handleFieldChanged(SOURCE_FOLDER_ID|ALL_FIELDS); } } @Override public void dialogFieldChanged(DialogField field) { handleFieldChanged(SOURCE_FOLDER_ID|ALL_FIELDS); } } private IPath chooseSourceFolder(IPath initialPath) { ICElement initElement = NewClassWizardUtil.getSourceFolder(initialPath); if (initElement instanceof ISourceRoot) { ICProject cProject = initElement.getCProject(); ISourceRoot projRoot = cProject.findSourceRoot(cProject.getProject()); if (projRoot != null && projRoot.equals(initElement)) initElement = cProject; } SourceFolderSelectionDialog dialog = new SourceFolderSelectionDialog(getShell()); dialog.setInput(CoreModel.create(NewClassWizardUtil.getWorkspaceRoot())); dialog.setInitialSelection(initElement); if (dialog.open() == Window.OK) { Object result = dialog.getFirstResult(); if (result instanceof ICElement) { ICElement element = (ICElement)result; if (element instanceof ICProject) { ICProject cproject = (ICProject)element; ISourceRoot folder = cproject.findSourceRoot(cproject.getProject()); if (folder != null) return folder.getResource().getFullPath(); } return element.getResource().getFullPath(); } } return null; } /** * handles changes to the namespace field */ private final class NamespaceFieldAdapter implements IStringButtonAdapter, IDialogFieldListener { @Override public void changeControlPressed(DialogField field) { ITypeInfo ns = chooseNamespace(); if (ns != null) { int changedFields = NAMESPACE_ID|CLASS_NAME_ID; IPath oldFolderPath = getSourceFolderFullPath(); if (oldFolderPath == null) { IPath headerPath = getHeaderFileFullPath(); IPath sourcePath = getSourceFileFullPath(); IPath testPath = getTestFileFullPath(); IPath newFolderPath = updateSourceFolderFromPath(ns.getEnclosingProject().getProject().getFullPath()); if (newFolderPath != null) { changedFields |= SOURCE_FOLDER_ID | HEADER_FILE_ID | SOURCE_FILE_ID | TEST_FILE_ID; setSourceFolderFullPath(newFolderPath, false); // Adjust the relative paths setHeaderFileFullPath(headerPath, false); setSourceFileFullPath(sourcePath, false); setTestFileFullPath(testPath, false); } } setNamespaceText(ns.getQualifiedTypeName().toString(), false); handleFieldChanged(changedFields); } } @Override public void dialogFieldChanged(DialogField field) { updateNamespaceEnableState(); handleFieldChanged(NAMESPACE_ID|CLASS_NAME_ID); } } private IPath updateSourceFolderFromPath(IPath filePath) { ICElement folder = NewClassWizardUtil.getSourceFolder(filePath); if (folder instanceof ISourceRoot) { ICProject cProject = folder.getCProject(); ISourceRoot projRoot = cProject.findSourceRoot(cProject.getProject()); if (projRoot != null && projRoot.equals(folder)) folder = cProject; } if (folder != null) { return folder.getPath(); } IProject proj = PathUtil.getEnclosingProject(filePath); if (proj != null) return proj.getFullPath(); return null; } private ITypeInfo chooseNamespace() { ITypeSearchScope scope; ICProject project = getCurrentProject(); if (project != null) { scope = new TypeSearchScope(project); } else { scope = new TypeSearchScope(true); } ITypeInfo[] elements = AllTypesCache.getNamespaces(scope, false); if (elements == null || elements.length == 0) { String title = NewClassWizardMessages.NewClassCreationWizardPage_getTypes_noNamespaces_title; String message = NewClassWizardMessages.NewClassCreationWizardPage_getTypes_noNamespaces_message; MessageDialog.openInformation(getShell(), title, message); return null; } NamespaceSelectionDialog dialog = new NamespaceSelectionDialog(getShell()); dialog.setElements(elements); int result = dialog.open(); if (result == IDialogConstants.OK_ID) { return (ITypeInfo) dialog.getFirstResult(); } return null; } /** * Handles changes to the class name field */ private final class ClassNameFieldAdapter implements IDialogFieldListener { @Override public void dialogFieldChanged(DialogField field) { int changedFields = CLASS_NAME_ID; updateFilesFromClassName(fClassNameDialogField.getText().trim()); changedFields |= HEADER_FILE_ID | SOURCE_FILE_ID | TEST_FILE_ID; handleFieldChanged(changedFields); } } /** * Handles changes to the base classes field */ private final class BaseClassesFieldAdapter implements IListAdapter<IBaseClassInfo> { @Override public void customButtonPressed(ListDialogField<IBaseClassInfo> field, int index) { if (index == 0) { chooseBaseClasses(); } handleFieldChanged(BASE_CLASSES_ID); } @Override public void selectionChanged(ListDialogField<IBaseClassInfo> field) { } @Override public void doubleClicked(ListDialogField<IBaseClassInfo> field) { } } private void chooseBaseClasses() { List<IBaseClassInfo> oldContents = fBaseClassesDialogField.getElements(); NewBaseClassSelectionDialog dialog = new NewBaseClassSelectionDialog(getShell()); dialog.addListener(new ITypeSelectionListener() { @Override public void typeAdded(ITypeInfo newBaseClass) { addBaseClass(newBaseClass, ASTAccessVisibility.PUBLIC, false); } }); int result = dialog.open(); if (result != IDialogConstants.OK_ID) { // Restore the old contents fBaseClassesDialogField.setElements(oldContents); } } /** * Handles changes to the method stubs field */ private final class MethodStubsFieldAdapter implements IListAdapter<IMethodStub> { @Override public void customButtonPressed(ListDialogField<IMethodStub> field, int index) { } @Override public void selectionChanged(ListDialogField<IMethodStub> field) { } @Override public void doubleClicked(ListDialogField<IMethodStub> field) { } } /** * handles changes to the file name fields */ private final class FileGroupFieldAdapter implements IStringButtonAdapter, IDialogFieldListener { @Override public void changeControlPressed(DialogField field) { IPath filePath = null; IPath headerPath = getHeaderFileFullPath(); IPath sourcePath = getSourceFileFullPath(); IPath testPath = getTestFileFullPath(); if (field == fHeaderFileDialogField) { filePath = chooseFile(NewClassWizardMessages.NewClassCreationWizardPage_ChooseHeaderFileDialog_title, getHeaderFileFullPath()); if (filePath != null) { headerPath = filePath; } } else if (field == fSourceFileDialogField) { filePath = chooseFile(NewClassWizardMessages.NewClassCreationWizardPage_ChooseSourceFileDialog_title, getSourceFileFullPath()); if (filePath != null) { sourcePath = filePath; } } else if (field == fTestFileDialogField) { filePath = chooseFile(NewClassWizardMessages.NewClassCreationWizardPage_ChooseTestFileDialog_title, getTestFileFullPath()); if (filePath != null) { testPath = filePath; } } if (filePath != null) { IPath folderPath = null; int changedFields = 0; int headerSegments = 0; int sourceSegments = 0; int testSegments = 0; if (headerPath != null) headerSegments = filePath.matchingFirstSegments(headerPath); if (sourcePath != null) sourceSegments = filePath.matchingFirstSegments(sourcePath); if (testPath != null) testSegments = filePath.matchingFirstSegments(testPath); int segments = Math.min(Math.min(headerSegments, sourceSegments), testSegments); if (segments > 0) { IPath newFolderPath = filePath.uptoSegment(segments); folderPath = updateSourceFolderFromPath(newFolderPath); } if (folderPath != null) { changedFields |= SOURCE_FOLDER_ID | HEADER_FILE_ID | SOURCE_FILE_ID | TEST_FILE_ID; // Adjust the relative paths setSourceFolderFullPath(folderPath, false); setHeaderFileFullPath(headerPath, false); setSourceFileFullPath(sourcePath, false); setTestFileFullPath(testPath, false); } if (field == fHeaderFileDialogField) { setHeaderFileFullPath(filePath, false); changedFields |= HEADER_FILE_ID; } else if (field == fSourceFileDialogField) { setSourceFileFullPath(filePath, false); changedFields |= SOURCE_FILE_ID; } else if (field == fTestFileDialogField) { setTestFileFullPath(filePath, false); changedFields |= TEST_FILE_ID; } handleFieldChanged(changedFields); } } @Override public void dialogFieldChanged(DialogField field) { int changedFields = 0; if (field == fTestFileSelection) { boolean enabled = fTestFileSelection.isSelected(); fTestFileDialogField.setEnabled(enabled); if (enabled) { fTestFileDerivedFromClassName = true; updateFilesFromClassName(fClassNameDialogField.getText().trim()); } else { fTestFileDialogField.setTextWithoutUpdate(""); //$NON-NLS-1$ } changedFields = TEST_FILE_ID; updateTestFileEnableState(); handleFieldChanged(SOURCE_FOLDER_ID); } if (field == fHeaderFileDialogField) { changedFields |= HEADER_FILE_ID; fHeaderFileDerivedFromClassName = false; } else if (field == fSourceFileDialogField) { changedFields |= SOURCE_FILE_ID; fSourceFileDerivedFromClassName = false; } else if (field == fTestFileDialogField) { changedFields |= TEST_FILE_ID; fTestFileDerivedFromClassName = false; } handleFieldChanged(changedFields); } } private IPath chooseFile(String title, IPath initialPath) { SourceFileSelectionDialog dialog = new SourceFileSelectionDialog(getShell()); dialog.setTitle(title); ICElement input = CoreModel.create(NewClassWizardUtil.getWorkspaceRoot()); ICProject project = getCurrentProject(); if (project != null) input = project; dialog.setInput(input); IPath filePath = initialPath; if (filePath != null) { String folderName = filePath.removeLastSegments(1).toString(); String fileName = filePath.lastSegment(); dialog.setInitialSelection(folderName, fileName); } else { filePath = getSourceFolderFullPath(); if (filePath != null) { dialog.setInitialSelection(filePath.toString(), null); } } if (dialog.open() == Window.OK) { return dialog.getFilePath(); } return null; } /** * update header and source file fields from the class name */ private void updateFilesFromClassName(String className) { String headerName = ""; //$NON-NLS-1$ String sourceName = ""; //$NON-NLS-1$ String testName = ""; //$NON-NLS-1$ IPath folder = getSourceFolderFullPath(); if (className != null && className.length() > 0) { String[] names = generateFileNames(className, folder); if (names != null && names.length == 3) { headerName = names[0]; sourceName = names[1]; testName = names[2]; } } if (fHeaderFileDerivedFromClassName) fHeaderFileDialogField.setTextWithoutUpdate(headerName); if (fSourceFileDerivedFromClassName) fSourceFileDialogField.setTextWithoutUpdate(sourceName); if (fTestFileDerivedFromClassName && fTestFileDialogField.isEnabled()) fTestFileDialogField.setTextWithoutUpdate(testName); } private static final int MAX_UNIQUE_CLASSNAME = 99; private IDialogSettings fDialogSettings; /** * Returns the names of the header file and source file which will be * used when this class is created, e.g. "MyClass" -> ["MyClass.h","MyClass.cpp"] * Note: the file names should be unique to avoid overwriting existing files. * * @param className the class name * @param folder the folder where the files are to be created, or <code>null</code> * @return an array of 2 Strings, containing the header file name and * source file name, respectively. */ protected String[] generateFileNames(String className, IPath folder) { String headerName = null; String sourceName = null; String testName = null; if (folder == null) { headerName = NewSourceFileGenerator.generateHeaderFileNameFromClass(className); sourceName = NewSourceFileGenerator.generateSourceFileNameFromClass(className); testName = NewSourceFileGenerator.generateTestFileNameFromClass(className); } else { // Make sure the file names are unique. String currName = className; int count = 0; String separator = ""; //$NON-NLS-1$ if (Character.isDigit(className.charAt(className.length() - 1))) separator = "_"; //$NON-NLS-1$ while (count < MAX_UNIQUE_CLASSNAME) { String header = NewSourceFileGenerator.generateHeaderFileNameFromClass(currName); IPath path = folder.append(header); if (!path.toFile().exists()) { String source = NewSourceFileGenerator.generateSourceFileNameFromClass(currName); path = folder.append(source); if (!path.toFile().exists()) { String test = NewSourceFileGenerator.generateTestFileNameFromClass(currName); path = folder.append(test); if (!path.toFile().exists()) { headerName = header; sourceName = source; testName = test; // We're done. break; } } } ++count; currName = className + separator + count; } } return new String[] { headerName, sourceName, testName }; } /** * Hook method that gets called when a field on this page has changed. * * @param fields Bitwise-OR'd ids of the fields that changed. */ protected void handleFieldChanged(int fields) { if (fields == 0) return; // no change if (fieldChanged(fields, SOURCE_FOLDER_ID)) { fSourceFolderStatus = sourceFolderChanged(); } if (fieldChanged(fields, NAMESPACE_ID)) { fNamespaceStatus = namespaceChanged(); } if (fieldChanged(fields, CLASS_NAME_ID)) { fClassNameStatus = classNameChanged(); } if (fieldChanged(fields, BASE_CLASSES_ID)) { fBaseClassesStatus = baseClassesChanged(); } if (fieldChanged(fields, METHOD_STUBS_ID)) { fMethodStubsStatus = methodStubsChanged(); } if (fieldChanged(fields, HEADER_FILE_ID)) { fHeaderFileStatus = headerFileChanged(); } if (fieldChanged(fields, SOURCE_FILE_ID)) { fSourceFileStatus = sourceFileChanged(); } if (fieldChanged(fields, TEST_FILE_ID)) { fTestFileStatus = testFileChanged(); } doStatusUpdate(); } private boolean fieldChanged(int fields, int fieldID) { return ((fields & fieldID) != 0); } /** * Updates the status line and the ok button according to the status of the fields * on the page. The most severe error is taken, with the last-focused field being * evaluated first. */ protected void doStatusUpdate() { // do the last focused field first IStatus lastStatus = getLastFocusedStatus(); final boolean isClassNameWarning = fClassNameStatus.getSeverity() == IStatus.WARNING; // status of all used components IStatus[] status = new IStatus[] { lastStatus, (fSourceFolderStatus != lastStatus) ? fSourceFolderStatus : STATUS_OK, (fNamespaceStatus != lastStatus) ? fNamespaceStatus : STATUS_OK, // Give priority to file-level warnings over class name warnings (fHeaderFileStatus != lastStatus && isClassNameWarning) ? fHeaderFileStatus : STATUS_OK, (fSourceFileStatus != lastStatus && isClassNameWarning) ? fSourceFileStatus : STATUS_OK, (fTestFileStatus != lastStatus && isClassNameWarning) ? fTestFileStatus : STATUS_OK, (fClassNameStatus != lastStatus) ? fClassNameStatus : STATUS_OK, (fBaseClassesStatus != lastStatus) ? fBaseClassesStatus : STATUS_OK, (fMethodStubsStatus != lastStatus) ? fMethodStubsStatus : STATUS_OK, (fHeaderFileStatus != lastStatus) ? fHeaderFileStatus : STATUS_OK, (fSourceFileStatus != lastStatus) ? fSourceFileStatus : STATUS_OK, (fTestFileStatus != lastStatus) ? fTestFileStatus : STATUS_OK, }; // the mode severe status will be displayed and the ok button enabled/disabled. updateStatus(status); } /** * Returns the status of the last field which had focus. * * @return status of the last field which had focus */ protected IStatus getLastFocusedStatus() { switch (fLastFocusedField) { case SOURCE_FOLDER_ID: return fSourceFolderStatus; case NAMESPACE_ID: return fNamespaceStatus; case CLASS_NAME_ID: return fClassNameStatus; case BASE_CLASSES_ID: return fBaseClassesStatus; case METHOD_STUBS_ID: return fMethodStubsStatus; case HEADER_FILE_ID: return fHeaderFileStatus; case SOURCE_FILE_ID: return fSourceFileStatus; case TEST_FILE_ID: return fTestFileStatus; default: return STATUS_OK; } } /** * Hook method that gets called when the source folder has changed. The method validates the * source folder and returns the status of the validation. * * @return the status of the validation */ protected IStatus sourceFolderChanged() { StatusInfo status = new StatusInfo(); IPath folderPath = getSourceFolderFullPath(); if (folderPath == null) { status.setError(NewClassWizardMessages.NewClassCreationWizardPage_error_EnterSourceFolderName); return status; } IResource res = NewClassWizardUtil.getWorkspaceRoot().findMember(folderPath); if (res != null && res.exists()) { int resType = res.getType(); if (resType == IResource.PROJECT || resType == IResource.FOLDER) { IProject proj = res.getProject(); if (!proj.isOpen()) { status.setError(NLS.bind(NewClassWizardMessages.NewClassCreationWizardPage_error_NotAFolder, folderPath)); return status; } if (!CoreModel.hasCCNature(proj) && !CoreModel.hasCNature(proj)) { if (resType == IResource.PROJECT) { status.setError(NewClassWizardMessages.NewClassCreationWizardPage_warning_NotACProject); return status; } status.setWarning(NewClassWizardMessages.NewClassCreationWizardPage_warning_NotInACProject); } if (!NewClassWizardUtil.isOnSourceRoot(res)) { status.setError(NLS.bind(NewClassWizardMessages.NewClassCreationWizardPage_error_NotASourceFolder, folderPath)); return status; } } else { status.setError(NLS.bind(NewClassWizardMessages.NewClassCreationWizardPage_error_NotAFolder, folderPath)); return status; } } else { status.setError(NLS.bind(NewClassWizardMessages.NewClassCreationWizardPage_error_FolderDoesNotExist, folderPath)); return status; } return status; } /** * Hook method that gets called when the namespace has changed. The method validates the * namespace and returns the status of the validation. * * @return the status of the validation */ protected IStatus namespaceChanged() { StatusInfo status = new StatusInfo(); if (!isNamespaceSelected()) { return status; } // must not be empty String namespace = getNamespaceText(); if (namespace == null || namespace.length() == 0) { status.setError(NewClassWizardMessages.NewClassCreationWizardPage_error_EnterNamespace); return status; } IStatus val = CConventions.validateNamespaceName(namespace); if (val.getSeverity() == IStatus.ERROR) { status.setError(NLS.bind(NewClassWizardMessages.NewClassCreationWizardPage_error_InvalidNamespace, val.getMessage())); return status; } else if (val.getSeverity() == IStatus.WARNING) { status.setWarning(NLS.bind(NewClassWizardMessages.NewClassCreationWizardPage_warning_NamespaceDiscouraged, val.getMessage())); } IQualifiedTypeName typeName = new QualifiedTypeName(namespace); ICProject project = getCurrentProject(); if (project != null) { int searchResult = NewClassWizardUtil.searchForCppType(typeName, project, ICPPNamespace.class); switch (searchResult) { case NewClassWizardUtil.SEARCH_MATCH_FOUND_EXACT: status.setOK(); return status; case NewClassWizardUtil.SEARCH_MATCH_FOUND_EXACT_ANOTHER_TYPE: status.setWarning(NewClassWizardMessages.NewClassCreationWizardPage_error_TypeMatchingNamespaceExists); return status; case NewClassWizardUtil.SEARCH_MATCH_FOUND_ANOTHER_NAMESPACE: status.setWarning(NewClassWizardMessages.NewClassCreationWizardPage_error_NamespaceExistsDifferentCase); return status; case NewClassWizardUtil.SEARCH_MATCH_FOUND_ANOTHER_TYPE: status.setWarning(NewClassWizardMessages.NewClassCreationWizardPage_error_TypeMatchingNamespaceExistsDifferentCase); return status; case NewClassWizardUtil.SEARCH_MATCH_NOTFOUND: // Find the highest ancestor namespace that does not exist. IQualifiedTypeName ns = typeName; while (ns.isQualified()) { IQualifiedTypeName ns1 = ns.getEnclosingTypeName(); if (NewClassWizardUtil.searchForCppType(ns1, project, ICPPNamespace.class) == NewClassWizardUtil.SEARCH_MATCH_FOUND_EXACT) { break; } ns = ns1; } status.setWarning(NLS.bind(NewClassWizardMessages.NewClassCreationWizardPage_warning_NamespaceNotExists, ns.getFullyQualifiedName())); break; } } val = CConventions.validateNamespaceName(typeName.lastSegment()); if (val.getSeverity() == IStatus.ERROR) { status.setError(NLS.bind(NewClassWizardMessages.NewClassCreationWizardPage_error_InvalidNamespace, val.getMessage())); return status; } return status; } /** * Hook method that gets called when the class name has changed. The method validates the * class name and returns the status of the validation. * * @return the status of the validation */ protected IStatus classNameChanged() { StatusInfo status = new StatusInfo(); String className = getClassName(); // Must not be empty. if (className == null || className.length() == 0) { status.setError(NewClassWizardMessages.NewClassCreationWizardPage_error_EnterClassName); return status; } IQualifiedTypeName typeName = new QualifiedTypeName(className); if (typeName.isQualified()) { status.setError(NewClassWizardMessages.NewClassCreationWizardPage_error_QualifiedClassName); return status; } IStatus val = CConventions.validateClassName(className); if (val.getSeverity() == IStatus.ERROR) { status.setError(NLS.bind(NewClassWizardMessages.NewClassCreationWizardPage_error_InvalidClassName, val.getMessage())); return status; } else if (val.getSeverity() == IStatus.WARNING) { status.setWarning(NLS.bind(NewClassWizardMessages.NewClassCreationWizardPage_warning_ClassNameDiscouraged, val.getMessage())); // continue checking } ICProject project = getCurrentProject(); if (project != null) { IQualifiedTypeName fullyQualifiedName = typeName; if (isNamespaceSelected()) { String namespace = getNamespaceText(); if (namespace != null && namespace.length() > 0) { fullyQualifiedName = new QualifiedTypeName(namespace).append(typeName); } } int searchResult = NewClassWizardUtil.searchForCppType(fullyQualifiedName, project, ICPPClassType.class); switch (searchResult) { case NewClassWizardUtil.SEARCH_MATCH_FOUND_EXACT: status.setError(NewClassWizardMessages.NewClassCreationWizardPage_error_ClassNameExists); return status; case NewClassWizardUtil.SEARCH_MATCH_FOUND_EXACT_ANOTHER_TYPE: status.setError(NewClassWizardMessages.NewClassCreationWizardPage_error_TypeMatchingClassExists); return status; case NewClassWizardUtil.SEARCH_MATCH_FOUND_ANOTHER_NAMESPACE: status.setWarning(NewClassWizardMessages.NewClassCreationWizardPage_error_ClassNameExistsDifferentCase); return status; case NewClassWizardUtil.SEARCH_MATCH_FOUND_ANOTHER_TYPE: status.setWarning(NewClassWizardMessages.NewClassCreationWizardPage_error_TypeMatchingClassExistsDifferentCase); return status; case NewClassWizardUtil.SEARCH_MATCH_NOTFOUND: break; } } return status; } /** * Hook method that gets called when the list of base classes has changed. The method * validates the base classes and returns the status of the validation. * * @return the status of the validation */ protected IStatus baseClassesChanged() { if (verifyBaseClasses()) { IPath folder = getSourceFolderFullPath(); ICProject project = getCurrentProject(); if (project != null) { IBaseClassInfo[] baseClasses = getBaseClasses(); // make sure all classes belong to the project if (baseClasses != null && baseClasses.length > 0) { IStatus status = baseClassesChanged(project, folder, baseClasses); if (status.isMultiStatus()) { // we only want to show the most severe error return StatusUtil.getMostSevere(status.getChildren()); } return status; } } } return Status.OK_STATUS; } /** * This method validates the base classes by searching through the project's * include paths and checking if each base class is reachable. * * @param project the current project * @param sourceFolder the current source folder * @param baseClasses an array of base classes * * @return the status of the validation * * @noreference This method is not intended to be referenced by clients. * @nooverride This method is not intended to be re-implemented or extended by clients. */ protected IStatus baseClassesChanged(ICProject project, IPath sourceFolder, IBaseClassInfo[] baseClasses) { MultiStatus status = new MultiStatus(CUIPlugin.getPluginId(), IStatus.OK, "", null); //$NON-NLS-1$ IScannerInfoProvider provider = CCorePlugin.getDefault().getScannerInfoProvider(project.getProject()); if (provider != null) { // TODO Get the scanner info for the actual source folder. IScannerInfo info = provider.getScannerInformation(project.getProject()); if (info != null) { String[] includePaths = info.getIncludePaths(); for (int i = 0; i < baseClasses.length; ++i) { IBaseClassInfo baseClass = baseClasses[i]; ITypeInfo baseType = baseClass.getType(); StatusInfo baseClassStatus = new StatusInfo(); if (!NewClassWizardUtil.isTypeReachable(baseType, project, includePaths)) { baseClassStatus.setError(NLS.bind(NewClassWizardMessages.NewClassCreationWizardPage_error_BaseClassNotExistsInProject, baseType.getQualifiedTypeName().toString())); } status.add(baseClassStatus); } } } return status; } /** * Checks if the base classes need to be verified (ie they must exist in the project) * * @return <code>true</code> if the base classes should be verified */ public boolean verifyBaseClasses() { return NewClassWizardPrefs.verifyBaseClasses(); } /** * Hook method that gets called when the list of method stubs has changed. The method * validates the method stubs and returns the status of the validation. * * @return the status of the validation */ protected IStatus methodStubsChanged() { // do nothing return Status.OK_STATUS; } /** * Hook method that gets called when the header file has changed. The method * validates the header file and returns the status of the validation. * * @return the status of the validation */ protected IStatus headerFileChanged() { StatusInfo status = new StatusInfo(); IPath path = getHeaderFileFullPath(); if (path == null) { status.setError(NewClassWizardMessages.NewClassCreationWizardPage_error_EnterHeaderFileName); return status; } IPath sourceFolderPath = getSourceFolderFullPath(); if (sourceFolderPath == null || !sourceFolderPath.isPrefixOf(path)) { status.setError(NewClassWizardMessages.NewClassCreationWizardPage_error_HeaderFileNotInSourceFolder); return status; } // Make sure the file location is under a source root if (!NewClassWizardUtil.isOnSourceRoot(path)) { status.setError(NewClassWizardMessages.NewClassCreationWizardPage_error_HeaderFileNotInSourceFolder); return status; } boolean fileExists = false; // Check if the file already exists IResource file = NewClassWizardUtil.getWorkspaceRoot().getFile(path); if (file.getType() == IResource.FILE) { if (!file.exists()) { URI location = file.getLocationURI(); try { IFileStore store = EFS.getStore(location); fileExists = store.fetchInfo().exists(); } catch (CoreException e) { status.setError(NewClassWizardMessages.NewClassCreationWizardPage_error_LocationUnknown); return status; } } else { fileExists = true; } IProject proj = file.getProject(); if (!proj.isOpen()) { status.setError(NLS.bind(NewClassWizardMessages.NewClassCreationWizardPage_error_NotAFile, path)); return status; } if (!CoreModel.hasCCNature(proj) && !CoreModel.hasCNature(proj)) { status.setWarning(NewClassWizardMessages.NewClassCreationWizardPage_warning_NotInACProject); } else if (fileExists) { status.setWarning(NewClassWizardMessages.NewClassCreationWizardPage_warning_HeaderFileExists); } } else { status.setError(NewClassWizardMessages.NewClassCreationWizardPage_error_NotAFile); return status; } // Check if folder exists IPath folderPath = path.removeLastSegments(1).makeRelative(); IResource folder = NewClassWizardUtil.getWorkspaceRoot().findMember(folderPath); if (folder == null || !folder.exists() || (folder.getType() != IResource.PROJECT && folder.getType() != IResource.FOLDER)) { status.setError(NLS.bind(NewClassWizardMessages.NewClassCreationWizardPage_error_FolderDoesNotExist, folderPath)); return status; } if (!fileExists) { IStatus val = CConventions.validateHeaderFileName(getCurrentProject().getProject(), path.lastSegment()); if (val.getSeverity() == IStatus.ERROR) { status.setError(NLS.bind(NewClassWizardMessages.NewClassCreationWizardPage_error_InvalidHeaderFileName, val.getMessage())); return status; } else if (val.getSeverity() == IStatus.WARNING) { status.setWarning(NLS.bind(NewClassWizardMessages.NewClassCreationWizardPage_warning_HeaderFileNameDiscouraged, val.getMessage())); } } return status; } /** * Hook method that gets called when the source file has changed. The method * validates the source file and returns the status of the validation. * * @return the status of the validation */ protected IStatus sourceFileChanged() { StatusInfo status = new StatusInfo(); IPath path = getSourceFileFullPath(); if (path == null) { status.setError(NewClassWizardMessages.NewClassCreationWizardPage_error_EnterSourceFileName); return status; } IPath sourceFolderPath = getSourceFolderFullPath(); if (sourceFolderPath == null || !sourceFolderPath.isPrefixOf(path)) { status.setError(NewClassWizardMessages.NewClassCreationWizardPage_error_SourceFileNotInSourceFolder); return status; } // Make sure the file location is under a source root if (!NewClassWizardUtil.isOnSourceRoot(path)) { status.setError(NewClassWizardMessages.NewClassCreationWizardPage_error_SourceFileNotInSourceFolder); return status; } boolean fileExists = false; // Check if file already exists IResource file = NewClassWizardUtil.getWorkspaceRoot().getFile(path); if (file.getType() == IResource.FILE) { if (!file.exists()) { URI location = file.getLocationURI(); try { IFileStore store = EFS.getStore(location); fileExists = store.fetchInfo().exists(); } catch (CoreException e) { status.setError(NewClassWizardMessages.NewClassCreationWizardPage_error_LocationUnknown); return status; } } else { fileExists = true; } IProject proj = file.getProject(); if (!proj.isOpen()) { status.setError(NLS.bind(NewClassWizardMessages.NewClassCreationWizardPage_error_NotAFile, path)); return status; } if (!CoreModel.hasCCNature(proj) && !CoreModel.hasCNature(proj)) { status.setWarning(NewClassWizardMessages.NewClassCreationWizardPage_warning_NotInACProject); } else if (fileExists) { status.setWarning(NewClassWizardMessages.NewClassCreationWizardPage_warning_SourceFileExists); } } else { status.setError(NewClassWizardMessages.NewClassCreationWizardPage_error_NotAFile); return status; } // Check if folder exists IPath folderPath = path.removeLastSegments(1).makeRelative(); IResource folder = NewClassWizardUtil.getWorkspaceRoot().findMember(folderPath); if (folder == null || !folder.exists() || (folder.getType() != IResource.PROJECT && folder.getType() != IResource.FOLDER)) { status.setError(NLS.bind(NewClassWizardMessages.NewClassCreationWizardPage_error_FolderDoesNotExist, folderPath)); return status; } if (!fileExists) { IStatus val = CConventions.validateSourceFileName(getCurrentProject().getProject(), path.lastSegment()); if (val.getSeverity() == IStatus.ERROR) { status.setError(NLS.bind(NewClassWizardMessages.NewClassCreationWizardPage_error_InvalidSourceFileName, val.getMessage())); return status; } else if (val.getSeverity() == IStatus.WARNING) { status.setWarning(NLS.bind(NewClassWizardMessages.NewClassCreationWizardPage_warning_SourceFileNameDiscouraged, val.getMessage())); } } return status; } /** * Hook method that gets called when the test file has changed. The method * validates the test file and returns the status of the validation. * * @return the status of the validation * @since 5.3 */ protected IStatus testFileChanged() { StatusInfo status = new StatusInfo(); if (!fTestFileDialogField.isEnabled()) { return status; } IPath path = getTestFileFullPath(); if (path == null) { status.setError(NewClassWizardMessages.NewClassCreationWizardPage_error_EnterTestFileName); return status; } IPath sourceFolderPath = getSourceFolderFullPath(); if (sourceFolderPath == null || !sourceFolderPath.isPrefixOf(path)) { status.setError(NewClassWizardMessages.NewClassCreationWizardPage_error_TestFileNotInSourceFolder); return status; } // Make sure the file location is under a source root if (!NewClassWizardUtil.isOnSourceRoot(path)) { status.setError(NewClassWizardMessages.NewClassCreationWizardPage_error_TestFileNotInSourceFolder); return status; } boolean fileExists = false; // Check if file already exists IResource file = NewClassWizardUtil.getWorkspaceRoot().getFile(path); if (file.getType() == IResource.FILE) { if (!file.exists()) { URI location = file.getLocationURI(); try { IFileStore store = EFS.getStore(location); fileExists = store.fetchInfo().exists(); } catch (CoreException e) { status.setError(NewClassWizardMessages.NewClassCreationWizardPage_error_LocationUnknown); return status; } } else { fileExists = true; } IProject proj = file.getProject(); if (!proj.isOpen()) { status.setError(NLS.bind(NewClassWizardMessages.NewClassCreationWizardPage_error_NotAFile, path)); return status; } if (!CoreModel.hasCCNature(proj) && !CoreModel.hasCNature(proj)) { status.setWarning(NewClassWizardMessages.NewClassCreationWizardPage_warning_NotInACProject); } else if (fileExists) { status.setWarning(NewClassWizardMessages.NewClassCreationWizardPage_warning_TestFileExists); } } else { status.setError(NewClassWizardMessages.NewClassCreationWizardPage_error_NotAFile); return status; } // Check if folder exists IPath folderPath = path.removeLastSegments(1).makeRelative(); IResource folder = NewClassWizardUtil.getWorkspaceRoot().findMember(folderPath); if (folder == null || !folder.exists() || (folder.getType() != IResource.PROJECT && folder.getType() != IResource.FOLDER)) { status.setError(NLS.bind(NewClassWizardMessages.NewClassCreationWizardPage_error_FolderDoesNotExist, folderPath)); return status; } if (!fileExists) { IStatus val = CConventions.validateSourceFileName(getCurrentProject().getProject(), path.lastSegment()); if (val.getSeverity() == IStatus.ERROR) { status.setError(NLS.bind(NewClassWizardMessages.NewClassCreationWizardPage_error_InvalidTestFileName, val.getMessage())); return status; } else if (val.getSeverity() == IStatus.WARNING) { status.setWarning(NLS.bind(NewClassWizardMessages.NewClassCreationWizardPage_warning_TestFileNameDiscouraged, val.getMessage())); } } return status; } // -------- Code Generation --------- /** * Creates the new class using the entered field values. * * @param monitor a progress monitor to report progress. * @throws CoreException Thrown when the creation failed. */ public void createClass(IProgressMonitor monitor) throws CoreException { // Update dialog settings. fDialogSettings.put(KEY_NAMESPACE_SELECTED, fNamespaceSelection.isSelected()); fDialogSettings.put(KEY_TEST_FILE_SELECTED, fTestFileSelection.isSelected()); String namespace = fNamespaceSelection.isSelected() ? getNamespaceText() : null; fDialogSettings.put(KEY_NAMESPACE, namespace); IMethodStub[] stubs = fMethodStubsDialogField.getMethodStubs(); for (int i = 0; i < stubs.length; ++i) { IMethodStub stub = stubs[i]; if (stub.canModifyVirtual()) { fDialogSettings.put(KEY_STUB_VIRTUAL + i, stub.isVirtual()); } if (stub.canModifyInline()) { fDialogSettings.put(KEY_STUB_INLINE + i, stub.isInline()); } fDialogSettings.put(KEY_STUB_SELECTED + i, fMethodStubsDialogField.isChecked(stub)); } fCreatedClass = null; fCreatedHeaderFile = null; fCreatedSourceFile = null; fCreatedTestFile = null; IPath headerPath = getHeaderFileFullPath(); IPath sourcePath = getSourceFileFullPath(); IPath testPath = getTestFileFullPath(); createClass(headerPath, sourcePath, testPath, getClassName(), namespace, getBaseClasses(), getSelectedMethodStubs(), monitor); } /** * Returns whether the generated header and source files should be * opened in editors after the finish button is pressed. * * @return <code>true</code> if the header and source file should be * displayed */ public boolean openClassInEditor() { return NewClassWizardPrefs.openClassInEditor(); } /** * Creates a new class. * * @param headerPath the header file path * @param sourcePath the source file path * @param testPath the test file path, can be {@code null}. * @param className the class name * @param namespace the namespace * @param baseClasses array of base classes * @param methodStubs array of method stubs * @param monitor a progress monitor * @throws CoreException if the creation failed * @since 5.3 * * @noreference This method is not intended to be referenced by clients. * @nooverride This method is not intended to be re-implemented or extended by clients. */ protected void createClass(IPath headerPath, IPath sourcePath, IPath testPath, String className, String namespace, IBaseClassInfo[] baseClasses, IMethodStub[] methodStubs, IProgressMonitor monitor) throws CoreException { NewClassCodeGenerator generator = new NewClassCodeGenerator( headerPath, sourcePath, testPath, className, namespace, baseClasses, methodStubs); generator.setForceSourceFileCreation(true); generator.createClass(monitor); fCreatedClass = generator.getCreatedClass(); fCreatedHeaderFile = generator.getCreatedHeaderFile(); fCreatedSourceFile = generator.getCreatedSourceFile(); fCreatedTestFile = generator.getCreatedTestFile(); } /** * @noreference This method is not intended to be referenced by clients. * @nooverride This method is not intended to be re-implemented or extended by clients. */ protected void createClass(IPath headerPath, IPath sourcePath, String className, String namespace, IBaseClassInfo[] baseClasses, IMethodStub[] methodStubs, IProgressMonitor monitor) throws CoreException { createClass(headerPath, sourcePath, null, className, namespace, baseClasses, methodStubs, monitor); } /** * Returns the created class. The method only returns a valid class * after {@link #createClass} has been called. * * @return the created class * @see #createClass(IProgressMonitor) */ public ICElement getCreatedClass() { return fCreatedClass; } /** * Returns the created header file. The method only returns a valid file * after {@link #createClass} has been called. * * @return the created header file * @see #createClass(IProgressMonitor) */ public IFile getCreatedHeaderFile() { return fCreatedHeaderFile; } /** * Returns the created source file. The method only returns a valid file * after {@link #createClass} has been called. * * @return the created source file * @see #createClass(IProgressMonitor) */ public IFile getCreatedSourceFile() { return fCreatedSourceFile; } /** * Returns the created test file. The method only returns a valid file * after {@link #createClass} has been called. * * @return the created test file * @see #createClass(IProgressMonitor) * @since 5.3 */ public IFile getCreatedTestFile() { return fCreatedTestFile; } }