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 HierarchyTypeSelection(IProject project, String referenceClass)110 public HierarchyTypeSelection(IProject project, String referenceClass) 111 throws JavaModelException { 112 mJavaProject = JavaCore.create(project); 113 mReferenceType = mJavaProject.findType(referenceClass); 114 } 115 116 @Override getFilterExtension()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 createContentArea(Composite parent)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