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