OSDN Git Service

android-2.1_r1 snapshot
[android-x86/sdk.git] / eclipse / plugins / com.android.ide.eclipse.adt / src / com / android / ide / eclipse / adt / internal / editors / manifest / model / UiClassAttributeNode.java
1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.eclipse.org/org/documents/epl-v10.php
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.android.ide.eclipse.adt.internal.editors.manifest.model;
18
19 import com.android.ide.eclipse.adt.AdtPlugin;
20 import com.android.ide.eclipse.adt.AndroidConstants;
21 import com.android.ide.eclipse.adt.internal.editors.AndroidEditor;
22 import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
23 import com.android.ide.eclipse.adt.internal.editors.descriptors.TextAttributeDescriptor;
24 import com.android.ide.eclipse.adt.internal.editors.manifest.descriptors.AndroidManifestDescriptors;
25 import com.android.ide.eclipse.adt.internal.editors.ui.SectionHelper;
26 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
27 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiTextAttributeNode;
28 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
29 import com.android.sdklib.xml.AndroidManifest;
30
31 import org.eclipse.core.resources.IFile;
32 import org.eclipse.core.resources.IProject;
33 import org.eclipse.core.runtime.CoreException;
34 import org.eclipse.core.runtime.NullProgressMonitor;
35 import org.eclipse.jdt.core.Flags;
36 import org.eclipse.jdt.core.IClasspathEntry;
37 import org.eclipse.jdt.core.IJavaElement;
38 import org.eclipse.jdt.core.IJavaProject;
39 import org.eclipse.jdt.core.IPackageFragment;
40 import org.eclipse.jdt.core.IPackageFragmentRoot;
41 import org.eclipse.jdt.core.IType;
42 import org.eclipse.jdt.core.ITypeHierarchy;
43 import org.eclipse.jdt.core.JavaCore;
44 import org.eclipse.jdt.core.JavaModelException;
45 import org.eclipse.jdt.core.search.IJavaSearchScope;
46 import org.eclipse.jdt.core.search.SearchEngine;
47 import org.eclipse.jdt.ui.IJavaElementSearchConstants;
48 import org.eclipse.jdt.ui.JavaUI;
49 import org.eclipse.jdt.ui.actions.OpenNewClassWizardAction;
50 import org.eclipse.jdt.ui.dialogs.ITypeInfoFilterExtension;
51 import org.eclipse.jdt.ui.dialogs.ITypeInfoRequestor;
52 import org.eclipse.jdt.ui.dialogs.ITypeSelectionComponent;
53 import org.eclipse.jdt.ui.dialogs.TypeSelectionExtension;
54 import org.eclipse.jdt.ui.wizards.NewClassWizardPage;
55 import org.eclipse.jface.dialogs.IMessageProvider;
56 import org.eclipse.jface.window.Window;
57 import org.eclipse.swt.SWT;
58 import org.eclipse.swt.events.DisposeEvent;
59 import org.eclipse.swt.events.DisposeListener;
60 import org.eclipse.swt.events.ModifyEvent;
61 import org.eclipse.swt.events.ModifyListener;
62 import org.eclipse.swt.events.SelectionAdapter;
63 import org.eclipse.swt.events.SelectionEvent;
64 import org.eclipse.swt.layout.GridData;
65 import org.eclipse.swt.layout.GridLayout;
66 import org.eclipse.swt.widgets.Button;
67 import org.eclipse.swt.widgets.Composite;
68 import org.eclipse.swt.widgets.Control;
69 import org.eclipse.swt.widgets.Text;
70 import org.eclipse.ui.IEditorInput;
71 import org.eclipse.ui.IFileEditorInput;
72 import org.eclipse.ui.PartInitException;
73 import org.eclipse.ui.PlatformUI;
74 import org.eclipse.ui.dialogs.SelectionDialog;
75 import org.eclipse.ui.forms.IManagedForm;
76 import org.eclipse.ui.forms.events.HyperlinkAdapter;
77 import org.eclipse.ui.forms.events.HyperlinkEvent;
78 import org.eclipse.ui.forms.widgets.FormText;
79 import org.eclipse.ui.forms.widgets.FormToolkit;
80 import org.eclipse.ui.forms.widgets.TableWrapData;
81 import org.w3c.dom.Element;
82
83 import java.util.ArrayList;
84
85 /**
86  * Represents an XML attribute for a class, that can be modified using a simple text field or
87  * a dialog to choose an existing class. Also, there's a link to create a new class.
88  * <p/>
89  * See {@link UiTextAttributeNode} for more information.
90  */
91 public class UiClassAttributeNode extends UiTextAttributeNode {
92
93     private String mReferenceClass;
94     private IPostTypeCreationAction mPostCreationAction;
95     private boolean mMandatory;
96     private final boolean mDefaultToProjectOnly;
97
98     private class HierarchyTypeSelection extends TypeSelectionExtension {
99
100         private IJavaProject mJavaProject;
101         private IType mReferenceType;
102         private Button mProjectOnly;
103         private boolean mUseProjectOnly;
104
105         public HierarchyTypeSelection(IProject project, String referenceClass)
106                 throws JavaModelException {
107             mJavaProject = JavaCore.create(project);
108             mReferenceType = mJavaProject.findType(referenceClass);
109         }
110
111         @Override
112         public ITypeInfoFilterExtension getFilterExtension() {
113             return new ITypeInfoFilterExtension() {
114                 public boolean select(ITypeInfoRequestor typeInfoRequestor) {
115
116                     boolean projectOnly = mUseProjectOnly;
117
118                     String packageName = typeInfoRequestor.getPackageName();
119                     String typeName = typeInfoRequestor.getTypeName();
120                     String enclosingType = typeInfoRequestor.getEnclosingName();
121
122                     // build the full class name.
123                     StringBuilder sb = new StringBuilder(packageName);
124                     sb.append('.');
125                     if (enclosingType.length() > 0) {
126                         sb.append(enclosingType);
127                         sb.append('.');
128                     }
129                     sb.append(typeName);
130
131                     String className = sb.toString();
132
133                     try {
134                         IType type = mJavaProject.findType(className);
135
136                         if (type == null) {
137                             return false;
138                         }
139
140                         // don't display abstract classes
141                         if ((type.getFlags() & Flags.AccAbstract) != 0) {
142                             return false;
143                         }
144
145                         // if project-only is selected, make sure the package fragment is
146                         // an actual source (thus "from this project").
147                         if (projectOnly) {
148                             IPackageFragment frag = type.getPackageFragment();
149                             if (frag == null || frag.getKind() != IPackageFragmentRoot.K_SOURCE) {
150                                 return false;
151                             }
152                         }
153
154                         // get the type hierarchy and reference type is one of the super classes.
155                         ITypeHierarchy hierarchy = type.newSupertypeHierarchy(
156                                 new NullProgressMonitor());
157
158                         IType[] supertypes = hierarchy.getAllSupertypes(type);
159                         int n = supertypes.length;
160                         for (int i = 0; i < n; i++) {
161                             IType st = supertypes[i];
162                             if (mReferenceType.equals(st)) {
163                                 return true;
164                             }
165                         }
166                     } catch (JavaModelException e) {
167                     }
168
169                     return false;
170                 }
171             };
172         }
173
174         @Override
175         public Control createContentArea(Composite parent) {
176
177             mProjectOnly = new Button(parent, SWT.CHECK);
178             mProjectOnly.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
179             mProjectOnly.setText(String.format("Display classes from sources of project '%s' only",
180                     mJavaProject.getProject().getName()));
181
182             mUseProjectOnly = mDefaultToProjectOnly;
183             mProjectOnly.setSelection(mUseProjectOnly);
184
185             mProjectOnly.addSelectionListener(new SelectionAdapter() {
186                 @Override
187                 public void widgetSelected(SelectionEvent e) {
188                     super.widgetSelected(e);
189                     mUseProjectOnly = mProjectOnly.getSelection();
190                     getTypeSelectionComponent().triggerSearch();
191                 }
192             });
193
194             return super.createContentArea(parent);
195         }
196     }
197
198     /**
199      * Classes which implement this interface provide a method processing newly created classes.
200      */
201     public static interface IPostTypeCreationAction {
202         /**
203          * Sent to process a newly created class.
204          * @param newType the IType representing the newly created class.
205          */
206         public void processNewType(IType newType);
207     }
208
209     /**
210      * Creates a {@link UiClassAttributeNode} object that will display ui to select or create
211      * classes.
212      * @param referenceClass The allowed supertype of the classes that are to be selected
213      * or created. Can be null.
214      * @param postCreationAction a {@link IPostTypeCreationAction} object handling post creation
215      * modification of the class.
216      * @param mandatory indicates if the class value is mandatory
217      * @param attributeDescriptor the {@link AttributeDescriptor} object linked to the Ui Node.
218      * @param defaultToProjectOnly When true display classes of this project only by default.
219      *         When false any class path will be considered. The user can always toggle this.
220      */
221     public UiClassAttributeNode(String referenceClass, IPostTypeCreationAction postCreationAction,
222             boolean mandatory, AttributeDescriptor attributeDescriptor, UiElementNode uiParent,
223             boolean defaultToProjectOnly) {
224         super(attributeDescriptor, uiParent);
225
226         mReferenceClass = referenceClass;
227         mPostCreationAction = postCreationAction;
228         mMandatory = mandatory;
229         mDefaultToProjectOnly = defaultToProjectOnly;
230     }
231
232     /* (non-java doc)
233      * Creates a label widget and an associated text field.
234      * <p/>
235      * As most other parts of the android manifest editor, this assumes the
236      * parent uses a table layout with 2 columns.
237      */
238     @Override
239     public void createUiControl(final Composite parent, IManagedForm managedForm) {
240         setManagedForm(managedForm);
241         FormToolkit toolkit = managedForm.getToolkit();
242         TextAttributeDescriptor desc = (TextAttributeDescriptor) getDescriptor();
243
244         StringBuilder label = new StringBuilder();
245         label.append("<form><p><a href='unused'>");
246         label.append(desc.getUiName());
247         label.append("</a></p></form>");
248         FormText formText = SectionHelper.createFormText(parent, toolkit, true /* isHtml */,
249                 label.toString(), true /* setupLayoutData */);
250         formText.addHyperlinkListener(new HyperlinkAdapter() {
251             @Override
252             public void linkActivated(HyperlinkEvent e) {
253                 super.linkActivated(e);
254                 handleLabelClick();
255             }
256         });
257         formText.setLayoutData(new TableWrapData(TableWrapData.LEFT, TableWrapData.MIDDLE));
258         SectionHelper.addControlTooltip(formText, desc.getTooltip());
259
260         Composite composite = toolkit.createComposite(parent);
261         composite.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.MIDDLE));
262         GridLayout gl = new GridLayout(2, false);
263         gl.marginHeight = gl.marginWidth = 0;
264         composite.setLayout(gl);
265         // Fixes missing text borders under GTK... also requires adding a 1-pixel margin
266         // for the text field below
267         toolkit.paintBordersFor(composite);
268
269         final Text text = toolkit.createText(composite, getCurrentValue());
270         GridData gd = new GridData(GridData.FILL_HORIZONTAL);
271         gd.horizontalIndent = 1;  // Needed by the fixed composite borders under GTK
272         text.setLayoutData(gd);
273         Button browseButton = toolkit.createButton(composite, "Browse...", SWT.PUSH);
274
275         setTextWidget(text);
276
277         browseButton.addSelectionListener(new SelectionAdapter() {
278             @Override
279             public void widgetSelected(SelectionEvent e) {
280                 super.widgetSelected(e);
281                 handleBrowseClick();
282             }
283         });
284     }
285
286     /* (non-java doc)
287      *
288      * Add a modify listener that will check the validity of the class
289      */
290     @Override
291     protected void onAddValidators(final Text text) {
292         ModifyListener listener = new ModifyListener() {
293             public void modifyText(ModifyEvent e) {
294                 try {
295                     String textValue = text.getText().trim();
296                     if (textValue.length() == 0) {
297                         if (mMandatory) {
298                             setErrorMessage("Value is mandatory", text);
299                         } else {
300                             setErrorMessage(null, text);
301                         }
302                         return;
303                     }
304                     // first we need the current java package.
305                     String javaPackage = getManifestPackage();
306
307                     // build the fully qualified name of the class
308                     String className = AndroidManifest.combinePackageAndClassName(
309                             javaPackage, textValue);
310
311                     // only test the vilibility for activities.
312                     boolean testVisibility = AndroidConstants.CLASS_ACTIVITY.equals(
313                             mReferenceClass);
314
315                     // test the class
316                     setErrorMessage(BaseProjectHelper.testClassForManifest(
317                             BaseProjectHelper.getJavaProject(getProject()), className,
318                             mReferenceClass, testVisibility), text);
319                 } catch (CoreException ce) {
320                     setErrorMessage(ce.getMessage(), text);
321                 }
322             }
323         };
324
325         text.addModifyListener(listener);
326
327         // Make sure the validator removes its message(s) when the widget is disposed
328         text.addDisposeListener(new DisposeListener() {
329             public void widgetDisposed(DisposeEvent e) {
330                 // we don't want to use setErrorMessage, because we don't want to reset
331                 // the error flag in the UiAttributeNode
332                 getManagedForm().getMessageManager().removeMessage(text, text);
333             }
334         });
335
336         // Finally call the validator once to make sure the initial value is processed
337         listener.modifyText(null);
338     }
339
340     private void handleBrowseClick() {
341         Text text = getTextWidget();
342
343         // we need to get the project of the manifest.
344         IProject project = getProject();
345         if (project != null) {
346
347             // Create a search scope including only the source folder of the current
348             // project.
349             IPackageFragmentRoot[] packageFragmentRoots = getPackageFragmentRoots(project,
350                     true /*include_containers*/);
351             IJavaSearchScope scope = SearchEngine.createJavaSearchScope(
352                     packageFragmentRoots,
353                     false);
354
355             try {
356                 SelectionDialog dlg = JavaUI.createTypeDialog(text.getShell(),
357                     PlatformUI.getWorkbench().getProgressService(),
358                     scope,
359                     IJavaElementSearchConstants.CONSIDER_CLASSES,  // style
360                     false, // no multiple selection
361                     "**",  //$NON-NLS-1$ //filter
362                     new HierarchyTypeSelection(project, mReferenceClass));
363                 dlg.setMessage(String.format("Select class name for element %1$s:",
364                         getUiParent().getBreadcrumbTrailDescription(false /* include_root */)));
365                 if (dlg instanceof ITypeSelectionComponent) {
366                     ((ITypeSelectionComponent)dlg).triggerSearch();
367                 }
368
369                 if (dlg.open() == Window.OK) {
370                     Object[] results = dlg.getResult();
371                     if (results.length == 1) {
372                         handleNewType((IType)results[0]);
373                     }
374                 }
375             } catch (JavaModelException e1) {
376                 AdtPlugin.log(e1, "UiClassAttributeNode HandleBrowser failed");
377             }
378         }
379     }
380
381     private void handleLabelClick() {
382         // get the current value
383         String className = getTextWidget().getText().trim();
384
385         // get the package name from the manifest.
386         String packageName = getManifestPackage();
387
388         if (className.length() == 0) {
389             createNewClass(packageName, null /* className */);
390         } else {
391             // build back the fully qualified class name.
392             String fullClassName = className;
393             if (className.startsWith(".")) { //$NON-NLS-1$
394                 fullClassName = packageName + className;
395             } else {
396                 String[] segments = className.split(AndroidConstants.RE_DOT);
397                 if (segments.length == 1) {
398                     fullClassName = packageName + "." + className; //$NON-NLS-1$
399                 }
400             }
401
402             // in case the type is enclosed, we need to replace the $ with .
403             fullClassName = fullClassName.replaceAll("\\$", "\\."); //$NON-NLS-1$ //$NON-NLS2$
404
405             // now we try to find the file that contains this class and we open it in the editor.
406             IProject project = getProject();
407             IJavaProject javaProject = JavaCore.create(project);
408
409             try {
410                 IType result = javaProject.findType(fullClassName);
411                 if (result != null) {
412                     JavaUI.openInEditor(result);
413                 } else {
414                     // split the last segment from the fullClassname
415                     int index = fullClassName.lastIndexOf('.');
416                     if (index != -1) {
417                         createNewClass(fullClassName.substring(0, index),
418                                 fullClassName.substring(index+1));
419                     } else {
420                         createNewClass(packageName, className);
421                     }
422                 }
423             } catch (JavaModelException e) {
424                 AdtPlugin.log(e, "UiClassAttributeNode HandleLabel failed");
425             } catch (PartInitException e) {
426                 AdtPlugin.log(e, "UiClassAttributeNode HandleLabel failed");
427             }
428         }
429     }
430
431     private IProject getProject() {
432         UiElementNode uiNode = getUiParent();
433         AndroidEditor editor = uiNode.getEditor();
434         IEditorInput input = editor.getEditorInput();
435         if (input instanceof IFileEditorInput) {
436             // from the file editor we can get the IFile object, and from it, the IProject.
437             IFile file = ((IFileEditorInput)input).getFile();
438             return file.getProject();
439         }
440
441         return null;
442     }
443
444
445     /**
446      * Returns the current value of the /manifest/package attribute.
447      * @return the package or an empty string if not found
448      */
449     private String getManifestPackage() {
450         // get the root uiNode to get the 'package' attribute value.
451         UiElementNode rootNode = getUiParent().getUiRoot();
452
453         Element xmlElement = (Element) rootNode.getXmlNode();
454
455         if (xmlElement != null) {
456             return xmlElement.getAttribute(AndroidManifestDescriptors.PACKAGE_ATTR);
457         }
458         return ""; //$NON-NLS-1$
459     }
460
461
462     /**
463      * Computes and return the {@link IPackageFragmentRoot}s corresponding to the source folders of
464      * the specified project.
465      * @param project the project
466      * @param include_containers True to include containers
467      * @return an array of IPackageFragmentRoot.
468      */
469     private IPackageFragmentRoot[] getPackageFragmentRoots(IProject project,
470             boolean include_containers) {
471         ArrayList<IPackageFragmentRoot> result = new ArrayList<IPackageFragmentRoot>();
472         try {
473             IJavaProject javaProject = JavaCore.create(project);
474             IPackageFragmentRoot[] roots = javaProject.getPackageFragmentRoots();
475             for (int i = 0; i < roots.length; i++) {
476                 IClasspathEntry entry = roots[i].getRawClasspathEntry();
477                 if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE ||
478                         (include_containers &&
479                                 entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER)) {
480                     result.add(roots[i]);
481                 }
482             }
483         } catch (JavaModelException e) {
484         }
485
486         return result.toArray(new IPackageFragmentRoot[result.size()]);
487     }
488
489     private void handleNewType(IType type) {
490         Text text = getTextWidget();
491
492         // get the fully qualified name with $ to properly detect the enclosing types.
493         String name = type.getFullyQualifiedName('$');
494
495         String packageValue = getManifestPackage();
496
497         // check if the class doesn't start with the package.
498         if (packageValue.length() > 0 && name.startsWith(packageValue)) {
499             // if it does, we remove the package and the first dot.
500             name = name.substring(packageValue.length() + 1);
501
502             // look for how many segments we have left.
503             // if one, just write it that way.
504             // if more than one, write it with a leading dot.
505             String[] packages = name.split(AndroidConstants.RE_DOT);
506             if (packages.length == 1) {
507                 text.setText(name);
508             } else {
509                 text.setText("." + name); //$NON-NLS-1$
510             }
511         } else {
512             text.setText(name);
513         }
514     }
515
516     private void createNewClass(String packageName, String className) {
517         // create the wizard page for the class creation, and configure it
518         NewClassWizardPage page = new NewClassWizardPage();
519
520         // set the parent class
521         page.setSuperClass(mReferenceClass, true /* canBeModified */);
522
523         // get the source folders as java elements.
524         IPackageFragmentRoot[] roots = getPackageFragmentRoots(getProject(),
525                 true /*include_containers*/);
526
527         IPackageFragmentRoot currentRoot = null;
528         IPackageFragment currentFragment = null;
529         int packageMatchCount = -1;
530
531         for (IPackageFragmentRoot root : roots) {
532             // Get the java element for the package.
533             // This method is said to always return a IPackageFragment even if the
534             // underlying folder doesn't exist...
535             IPackageFragment fragment = root.getPackageFragment(packageName);
536             if (fragment != null && fragment.exists()) {
537                 // we have a perfect match! we use it.
538                 currentRoot = root;
539                 currentFragment = fragment;
540                 packageMatchCount = -1;
541                 break;
542             } else {
543                 // we don't have a match. we look for the fragment with the best match
544                 // (ie the closest parent package we can find)
545                 try {
546                     IJavaElement[] children;
547                     children = root.getChildren();
548                     for (IJavaElement child : children) {
549                         if (child instanceof IPackageFragment) {
550                             fragment = (IPackageFragment)child;
551                             if (packageName.startsWith(fragment.getElementName())) {
552                                 // its a match. get the number of segments
553                                 String[] segments = fragment.getElementName().split("\\."); //$NON-NLS-1$
554                                 if (segments.length > packageMatchCount) {
555                                     packageMatchCount = segments.length;
556                                     currentFragment = fragment;
557                                     currentRoot = root;
558                                 }
559                             }
560                         }
561                     }
562                 } catch (JavaModelException e) {
563                     // Couldn't get the children: we just ignore this package root.
564                 }
565             }
566         }
567
568         ArrayList<IPackageFragment> createdFragments = null;
569
570         if (currentRoot != null) {
571             // if we have a perfect match, we set it and we're done.
572             if (packageMatchCount == -1) {
573                 page.setPackageFragmentRoot(currentRoot, true /* canBeModified*/);
574                 page.setPackageFragment(currentFragment, true /* canBeModified */);
575             } else {
576                 // we have a partial match.
577                 // create the package. We have to start with the first segment so that we
578                 // know what to delete in case of a cancel.
579                 try {
580                     createdFragments = new ArrayList<IPackageFragment>();
581
582                     int totalCount = packageName.split("\\.").length; //$NON-NLS-1$
583                     int count = 0;
584                     int index = -1;
585                     // skip the matching packages
586                     while (count < packageMatchCount) {
587                         index = packageName.indexOf('.', index+1);
588                         count++;
589                     }
590
591                     // create the rest of the segments, except for the last one as indexOf will
592                     // return -1;
593                     while (count < totalCount - 1) {
594                         index = packageName.indexOf('.', index+1);
595                         count++;
596                         createdFragments.add(currentRoot.createPackageFragment(
597                                 packageName.substring(0, index),
598                                 true /* force*/, new NullProgressMonitor()));
599                     }
600
601                     // create the last package
602                     createdFragments.add(currentRoot.createPackageFragment(
603                             packageName, true /* force*/, new NullProgressMonitor()));
604
605                     // set the root and fragment in the Wizard page
606                     page.setPackageFragmentRoot(currentRoot, true /* canBeModified*/);
607                     page.setPackageFragment(createdFragments.get(createdFragments.size()-1),
608                             true /* canBeModified */);
609                 } catch (JavaModelException e) {
610                     // if we can't create the packages, there's a problem. we revert to the default
611                     // package
612                     for (IPackageFragmentRoot root : roots) {
613                         // Get the java element for the package.
614                         // This method is said to always return a IPackageFragment even if the
615                         // underlying folder doesn't exist...
616                         IPackageFragment fragment = root.getPackageFragment(packageName);
617                         if (fragment != null && fragment.exists()) {
618                             page.setPackageFragmentRoot(root, true /* canBeModified*/);
619                             page.setPackageFragment(fragment, true /* canBeModified */);
620                             break;
621                         }
622                     }
623                 }
624             }
625         } else if (roots.length > 0) {
626             // if we haven't found a valid fragment, we set the root to the first source folder.
627             page.setPackageFragmentRoot(roots[0], true /* canBeModified*/);
628         }
629
630         // if we have a starting class name we use it
631         if (className != null) {
632             page.setTypeName(className, true /* canBeModified*/);
633         }
634
635         // create the action that will open it the wizard.
636         OpenNewClassWizardAction action = new OpenNewClassWizardAction();
637         action.setConfiguredWizardPage(page);
638         action.run();
639         IJavaElement element = action.getCreatedElement();
640
641         if (element != null) {
642             if (element.getElementType() == IJavaElement.TYPE) {
643
644                 IType type = (IType)element;
645
646                 if (mPostCreationAction != null) {
647                     mPostCreationAction.processNewType(type);
648                 }
649
650                 handleNewType(type);
651             }
652         } else {
653             // lets delete the packages we created just for this.
654             // we need to start with the leaf and go up
655             if (createdFragments != null) {
656                 try {
657                     for (int i = createdFragments.size() - 1 ; i >= 0 ; i--) {
658                         createdFragments.get(i).delete(true /* force*/, new NullProgressMonitor());
659                     }
660                 } catch (JavaModelException e) {
661                     e.printStackTrace();
662                 }
663             }
664         }
665     }
666
667     /**
668      * Sets the error messages. If message is <code>null</code>, the message is removed.
669      * @param message the message to set, or <code>null</code> to remove the current message
670      * @param textWidget the {@link Text} widget associated to the message.
671      */
672     private final void setErrorMessage(String message, Text textWidget) {
673         if (message != null) {
674             setHasError(true);
675             getManagedForm().getMessageManager().addMessage(textWidget, message, null /* data */,
676                     IMessageProvider.ERROR, textWidget);
677         } else {
678             setHasError(false);
679             getManagedForm().getMessageManager().removeMessage(textWidget, textWidget);
680         }
681     }
682
683     @Override
684     public String[] getPossibleValues(String prefix) {
685         // TODO: compute a list of existing classes for content assist completion
686         return null;
687     }
688 }
689