Home | History | Annotate | Download | only in model
      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.AdtConstants;
     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(AdtConstants.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(AdtConstants.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 
    691