OSDN Git Service

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