diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiClassAttributeNode.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiClassAttributeNode.java | 736 |
1 files changed, 736 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiClassAttributeNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiClassAttributeNode.java new file mode 100644 index 000000000..4c829d9ec --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiClassAttributeNode.java @@ -0,0 +1,736 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.eclipse.org/org/documents/epl-v10.php + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ide.eclipse.adt.internal.editors.manifest.model; + +import com.android.SdkConstants; +import com.android.ide.eclipse.adt.AdtConstants; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; +import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor; +import com.android.ide.eclipse.adt.internal.editors.descriptors.TextAttributeDescriptor; +import com.android.ide.eclipse.adt.internal.editors.manifest.descriptors.AndroidManifestDescriptors; +import com.android.ide.eclipse.adt.internal.editors.ui.SectionHelper; +import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; +import com.android.ide.eclipse.adt.internal.editors.uimodel.UiTextAttributeNode; +import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; +import com.android.xml.AndroidManifest; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.core.IClasspathEntry; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IPackageFragment; +import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.ITypeHierarchy; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.search.IJavaSearchScope; +import org.eclipse.jdt.core.search.SearchEngine; +import org.eclipse.jdt.ui.IJavaElementSearchConstants; +import org.eclipse.jdt.ui.JavaUI; +import org.eclipse.jdt.ui.actions.OpenNewClassWizardAction; +import org.eclipse.jdt.ui.dialogs.ITypeInfoFilterExtension; +import org.eclipse.jdt.ui.dialogs.ITypeInfoRequestor; +import org.eclipse.jdt.ui.dialogs.ITypeSelectionComponent; +import org.eclipse.jdt.ui.dialogs.TypeSelectionExtension; +import org.eclipse.jdt.ui.wizards.NewClassWizardPage; +import org.eclipse.jface.dialogs.IMessageProvider; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IFileEditorInput; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.dialogs.SelectionDialog; +import org.eclipse.ui.forms.IManagedForm; +import org.eclipse.ui.forms.events.HyperlinkAdapter; +import org.eclipse.ui.forms.events.HyperlinkEvent; +import org.eclipse.ui.forms.widgets.FormText; +import org.eclipse.ui.forms.widgets.FormToolkit; +import org.eclipse.ui.forms.widgets.TableWrapData; +import org.w3c.dom.Element; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Represents an XML attribute for a class, that can be modified using a simple text field or + * a dialog to choose an existing class. Also, there's a link to create a new class. + * <p/> + * See {@link UiTextAttributeNode} for more information. + */ +public class UiClassAttributeNode extends UiTextAttributeNode { + + private String mReferenceClass; + private IPostTypeCreationAction mPostCreationAction; + private boolean mMandatory; + private final boolean mDefaultToProjectOnly; + + private class HierarchyTypeSelection extends TypeSelectionExtension { + + private IJavaProject mJavaProject; + private IType mReferenceType; + private Button mProjectOnly; + private boolean mUseProjectOnly; + + public HierarchyTypeSelection(IProject project, String referenceClass) + throws JavaModelException { + mJavaProject = JavaCore.create(project); + mReferenceType = mJavaProject.findType(referenceClass); + } + + @Override + public ITypeInfoFilterExtension getFilterExtension() { + return new ITypeInfoFilterExtension() { + @Override + public boolean select(ITypeInfoRequestor typeInfoRequestor) { + + boolean projectOnly = mUseProjectOnly; + + String packageName = typeInfoRequestor.getPackageName(); + String typeName = typeInfoRequestor.getTypeName(); + String enclosingType = typeInfoRequestor.getEnclosingName(); + + // build the full class name. + StringBuilder sb = new StringBuilder(packageName); + sb.append('.'); + if (enclosingType.length() > 0) { + sb.append(enclosingType); + sb.append('.'); + } + sb.append(typeName); + + String className = sb.toString(); + + try { + IType type = mJavaProject.findType(className); + + if (type == null) { + return false; + } + + // don't display abstract classes + if ((type.getFlags() & Flags.AccAbstract) != 0) { + return false; + } + + // if project-only is selected, make sure the package fragment is + // an actual source (thus "from this project"). + if (projectOnly) { + IPackageFragment frag = type.getPackageFragment(); + if (frag == null || frag.getKind() != IPackageFragmentRoot.K_SOURCE) { + return false; + } + } + + // get the type hierarchy and reference type is one of the super classes. + ITypeHierarchy hierarchy = type.newSupertypeHierarchy( + new NullProgressMonitor()); + + IType[] supertypes = hierarchy.getAllSupertypes(type); + int n = supertypes.length; + for (int i = 0; i < n; i++) { + IType st = supertypes[i]; + if (mReferenceType.equals(st)) { + return true; + } + } + } catch (JavaModelException e) { + } + + return false; + } + }; + } + + @Override + public Control createContentArea(Composite parent) { + + mProjectOnly = new Button(parent, SWT.CHECK); + mProjectOnly.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + mProjectOnly.setText(String.format("Display classes from sources of project '%s' only", + mJavaProject.getProject().getName())); + + mUseProjectOnly = mDefaultToProjectOnly; + mProjectOnly.setSelection(mUseProjectOnly); + + mProjectOnly.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + super.widgetSelected(e); + mUseProjectOnly = mProjectOnly.getSelection(); + getTypeSelectionComponent().triggerSearch(); + } + }); + + return super.createContentArea(parent); + } + } + + /** + * Classes which implement this interface provide a method processing newly created classes. + */ + public static interface IPostTypeCreationAction { + /** + * Sent to process a newly created class. + * @param newType the IType representing the newly created class. + */ + public void processNewType(IType newType); + } + + /** + * Creates a {@link UiClassAttributeNode} object that will display ui to select or create + * classes. + * @param referenceClass The allowed supertype of the classes that are to be selected + * or created. Can be null. + * @param postCreationAction a {@link IPostTypeCreationAction} object handling post creation + * modification of the class. + * @param mandatory indicates if the class value is mandatory + * @param attributeDescriptor the {@link AttributeDescriptor} object linked to the Ui Node. + * @param defaultToProjectOnly When true display classes of this project only by default. + * When false any class path will be considered. The user can always toggle this. + */ + public UiClassAttributeNode(String referenceClass, IPostTypeCreationAction postCreationAction, + boolean mandatory, AttributeDescriptor attributeDescriptor, UiElementNode uiParent, + boolean defaultToProjectOnly) { + super(attributeDescriptor, uiParent); + + mReferenceClass = referenceClass; + mPostCreationAction = postCreationAction; + mMandatory = mandatory; + mDefaultToProjectOnly = defaultToProjectOnly; + } + + /* (non-java doc) + * Creates a label widget and an associated text field. + * <p/> + * As most other parts of the android manifest editor, this assumes the + * parent uses a table layout with 2 columns. + */ + @Override + public void createUiControl(final Composite parent, IManagedForm managedForm) { + setManagedForm(managedForm); + FormToolkit toolkit = managedForm.getToolkit(); + TextAttributeDescriptor desc = (TextAttributeDescriptor) getDescriptor(); + + StringBuilder label = new StringBuilder(); + label.append("<form><p><a href='unused'>"); + label.append(desc.getUiName()); + label.append("</a></p></form>"); + FormText formText = SectionHelper.createFormText(parent, toolkit, true /* isHtml */, + label.toString(), true /* setupLayoutData */); + formText.addHyperlinkListener(new HyperlinkAdapter() { + @Override + public void linkActivated(HyperlinkEvent e) { + super.linkActivated(e); + handleLabelClick(); + } + }); + formText.setLayoutData(new TableWrapData(TableWrapData.LEFT, TableWrapData.MIDDLE)); + SectionHelper.addControlTooltip(formText, desc.getTooltip()); + + Composite composite = toolkit.createComposite(parent); + composite.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.MIDDLE)); + GridLayout gl = new GridLayout(2, false); + gl.marginHeight = gl.marginWidth = 0; + composite.setLayout(gl); + // Fixes missing text borders under GTK... also requires adding a 1-pixel margin + // for the text field below + toolkit.paintBordersFor(composite); + + final Text text = toolkit.createText(composite, getCurrentValue()); + GridData gd = new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalIndent = 1; // Needed by the fixed composite borders under GTK + text.setLayoutData(gd); + Button browseButton = toolkit.createButton(composite, "Browse...", SWT.PUSH); + + setTextWidget(text); + + browseButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + super.widgetSelected(e); + handleBrowseClick(); + } + }); + } + + /* (non-java doc) + * + * Add a modify listener that will check the validity of the class + */ + @Override + protected void onAddValidators(final Text text) { + ModifyListener listener = new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + try { + String textValue = text.getText().trim(); + if (textValue.length() == 0) { + if (mMandatory) { + setErrorMessage("Value is mandatory", text); + } else { + setErrorMessage(null, text); + } + return; + } + // first we need the current java package. + String javaPackage = getManifestPackage(); + + // build the fully qualified name of the class + String className = AndroidManifest.combinePackageAndClassName( + javaPackage, textValue); + + // only test the vilibility for activities. + boolean testVisibility = SdkConstants.CLASS_ACTIVITY.equals( + mReferenceClass); + + // test the class + setErrorMessage(BaseProjectHelper.testClassForManifest( + BaseProjectHelper.getJavaProject(getProject()), className, + mReferenceClass, testVisibility), text); + } catch (CoreException ce) { + setErrorMessage(ce.getMessage(), text); + } + } + }; + + text.addModifyListener(listener); + + // Make sure the validator removes its message(s) when the widget is disposed + text.addDisposeListener(new DisposeListener() { + @Override + public void widgetDisposed(DisposeEvent e) { + // we don't want to use setErrorMessage, because we don't want to reset + // the error flag in the UiAttributeNode + getManagedForm().getMessageManager().removeMessage(text, text); + } + }); + + // Finally call the validator once to make sure the initial value is processed + listener.modifyText(null); + } + + private void handleBrowseClick() { + Text text = getTextWidget(); + + // we need to get the project of the manifest. + IProject project = getProject(); + if (project != null) { + + // Create a search scope including only the source folder of the current + // project. + IPackageFragmentRoot[] packageFragmentRoots = getPackageFragmentRoots(project, + true /*include_containers*/); + IJavaSearchScope scope = SearchEngine.createJavaSearchScope( + packageFragmentRoots, + false); + + try { + SelectionDialog dlg = JavaUI.createTypeDialog(text.getShell(), + PlatformUI.getWorkbench().getProgressService(), + scope, + IJavaElementSearchConstants.CONSIDER_CLASSES, // style + false, // no multiple selection + "**", //$NON-NLS-1$ //filter + new HierarchyTypeSelection(project, mReferenceClass)); + dlg.setMessage(String.format("Select class name for element %1$s:", + getUiParent().getBreadcrumbTrailDescription(false /* include_root */))); + if (dlg instanceof ITypeSelectionComponent) { + ((ITypeSelectionComponent)dlg).triggerSearch(); + } + + if (dlg.open() == Window.OK) { + Object[] results = dlg.getResult(); + if (results.length == 1) { + handleNewType((IType)results[0]); + } + } + } catch (JavaModelException e1) { + AdtPlugin.log(e1, "UiClassAttributeNode HandleBrowser failed"); + } + } + } + + private void handleLabelClick() { + // get the current value + String className = getTextWidget().getText().trim(); + + // get the package name from the manifest. + String packageName = getManifestPackage(); + + if (className.length() == 0) { + createNewClass(packageName, null /* className */); + } else { + // build back the fully qualified class name. + String fullClassName = className; + if (className.startsWith(".")) { //$NON-NLS-1$ + fullClassName = packageName + className; + } else { + String[] segments = className.split(AdtConstants.RE_DOT); + if (segments.length == 1) { + fullClassName = packageName + "." + className; //$NON-NLS-1$ + } + } + + // in case the type is enclosed, we need to replace the $ with . + fullClassName = fullClassName.replaceAll("\\$", "\\."); //$NON-NLS-1$ //$NON-NLS2$ + + // now we try to find the file that contains this class and we open it in the editor. + IProject project = getProject(); + IJavaProject javaProject = JavaCore.create(project); + + try { + IType result = javaProject.findType(fullClassName); + if (result != null) { + JavaUI.openInEditor(result); + } else { + // split the last segment from the fullClassname + int index = fullClassName.lastIndexOf('.'); + if (index != -1) { + createNewClass(fullClassName.substring(0, index), + fullClassName.substring(index+1)); + } else { + createNewClass(packageName, className); + } + } + } catch (JavaModelException e) { + AdtPlugin.log(e, "UiClassAttributeNode HandleLabel failed"); + } catch (PartInitException e) { + AdtPlugin.log(e, "UiClassAttributeNode HandleLabel failed"); + } + } + } + + private IProject getProject() { + UiElementNode uiNode = getUiParent(); + AndroidXmlEditor editor = uiNode.getEditor(); + IEditorInput input = editor.getEditorInput(); + if (input instanceof IFileEditorInput) { + // from the file editor we can get the IFile object, and from it, the IProject. + IFile file = ((IFileEditorInput)input).getFile(); + return file.getProject(); + } + + return null; + } + + + /** + * Returns the current value of the /manifest/package attribute. + * @return the package or an empty string if not found + */ + private String getManifestPackage() { + // get the root uiNode to get the 'package' attribute value. + UiElementNode rootNode = getUiParent().getUiRoot(); + + Element xmlElement = (Element) rootNode.getXmlNode(); + + if (xmlElement != null) { + return xmlElement.getAttribute(AndroidManifestDescriptors.PACKAGE_ATTR); + } + return ""; //$NON-NLS-1$ + } + + + /** + * Computes and return the {@link IPackageFragmentRoot}s corresponding to the source folders of + * the specified project. + * @param project the project + * @param include_containers True to include containers + * @return an array of IPackageFragmentRoot. + */ + private IPackageFragmentRoot[] getPackageFragmentRoots(IProject project, + boolean include_containers) { + ArrayList<IPackageFragmentRoot> result = new ArrayList<IPackageFragmentRoot>(); + try { + IJavaProject javaProject = JavaCore.create(project); + IPackageFragmentRoot[] roots = javaProject.getPackageFragmentRoots(); + for (int i = 0; i < roots.length; i++) { + IClasspathEntry entry = roots[i].getRawClasspathEntry(); + if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE || + (include_containers && + entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER)) { + result.add(roots[i]); + } + } + } catch (JavaModelException e) { + } + + return result.toArray(new IPackageFragmentRoot[result.size()]); + } + + private void handleNewType(IType type) { + Text text = getTextWidget(); + + // get the fully qualified name with $ to properly detect the enclosing types. + String name = type.getFullyQualifiedName('$'); + + String packageValue = getManifestPackage(); + + // check if the class doesn't start with the package. + if (packageValue.length() > 0 && name.startsWith(packageValue)) { + // if it does, we remove the package and the first dot. + name = name.substring(packageValue.length() + 1); + + // look for how many segments we have left. + // if one, just write it that way. + // if more than one, write it with a leading dot. + String[] packages = name.split(AdtConstants.RE_DOT); + if (packages.length == 1) { + text.setText(name); + } else { + text.setText("." + name); //$NON-NLS-1$ + } + } else { + text.setText(name); + } + } + + private void createNewClass(String packageName, String className) { + // create the wizard page for the class creation, and configure it + NewClassWizardPage page = new NewClassWizardPage(); + + // set the parent class + page.setSuperClass(mReferenceClass, true /* canBeModified */); + + // get the source folders as java elements. + IPackageFragmentRoot[] roots = getPackageFragmentRoots(getProject(), + true /*include_containers*/); + + IPackageFragmentRoot currentRoot = null; + IPackageFragment currentFragment = null; + int packageMatchCount = -1; + + for (IPackageFragmentRoot root : roots) { + // Get the java element for the package. + // This method is said to always return a IPackageFragment even if the + // underlying folder doesn't exist... + IPackageFragment fragment = root.getPackageFragment(packageName); + if (fragment != null && fragment.exists()) { + // we have a perfect match! we use it. + currentRoot = root; + currentFragment = fragment; + packageMatchCount = -1; + break; + } else { + // we don't have a match. we look for the fragment with the best match + // (ie the closest parent package we can find) + try { + IJavaElement[] children; + children = root.getChildren(); + for (IJavaElement child : children) { + if (child instanceof IPackageFragment) { + fragment = (IPackageFragment)child; + if (packageName.startsWith(fragment.getElementName())) { + // its a match. get the number of segments + String[] segments = fragment.getElementName().split("\\."); //$NON-NLS-1$ + if (segments.length > packageMatchCount) { + packageMatchCount = segments.length; + currentFragment = fragment; + currentRoot = root; + } + } + } + } + } catch (JavaModelException e) { + // Couldn't get the children: we just ignore this package root. + } + } + } + + ArrayList<IPackageFragment> createdFragments = null; + + if (currentRoot != null) { + // if we have a perfect match, we set it and we're done. + if (packageMatchCount == -1) { + page.setPackageFragmentRoot(currentRoot, true /* canBeModified*/); + page.setPackageFragment(currentFragment, true /* canBeModified */); + } else { + // we have a partial match. + // create the package. We have to start with the first segment so that we + // know what to delete in case of a cancel. + try { + createdFragments = new ArrayList<IPackageFragment>(); + + int totalCount = packageName.split("\\.").length; //$NON-NLS-1$ + int count = 0; + int index = -1; + // skip the matching packages + while (count < packageMatchCount) { + index = packageName.indexOf('.', index+1); + count++; + } + + // create the rest of the segments, except for the last one as indexOf will + // return -1; + while (count < totalCount - 1) { + index = packageName.indexOf('.', index+1); + count++; + createdFragments.add(currentRoot.createPackageFragment( + packageName.substring(0, index), + true /* force*/, new NullProgressMonitor())); + } + + // create the last package + createdFragments.add(currentRoot.createPackageFragment( + packageName, true /* force*/, new NullProgressMonitor())); + + // set the root and fragment in the Wizard page + page.setPackageFragmentRoot(currentRoot, true /* canBeModified*/); + page.setPackageFragment(createdFragments.get(createdFragments.size()-1), + true /* canBeModified */); + } catch (JavaModelException e) { + // if we can't create the packages, there's a problem. we revert to the default + // package + for (IPackageFragmentRoot root : roots) { + // Get the java element for the package. + // This method is said to always return a IPackageFragment even if the + // underlying folder doesn't exist... + IPackageFragment fragment = root.getPackageFragment(packageName); + if (fragment != null && fragment.exists()) { + page.setPackageFragmentRoot(root, true /* canBeModified*/); + page.setPackageFragment(fragment, true /* canBeModified */); + break; + } + } + } + } + } else if (roots.length > 0) { + // if we haven't found a valid fragment, we set the root to the first source folder. + page.setPackageFragmentRoot(roots[0], true /* canBeModified*/); + } + + // if we have a starting class name we use it + if (className != null) { + page.setTypeName(className, true /* canBeModified*/); + } + + // create the action that will open it the wizard. + OpenNewClassWizardAction action = new OpenNewClassWizardAction(); + action.setConfiguredWizardPage(page); + action.run(); + IJavaElement element = action.getCreatedElement(); + + if (element != null) { + if (element.getElementType() == IJavaElement.TYPE) { + + IType type = (IType)element; + + if (mPostCreationAction != null) { + mPostCreationAction.processNewType(type); + } + + handleNewType(type); + } + } else { + // lets delete the packages we created just for this. + // we need to start with the leaf and go up + if (createdFragments != null) { + try { + for (int i = createdFragments.size() - 1 ; i >= 0 ; i--) { + createdFragments.get(i).delete(true /* force*/, new NullProgressMonitor()); + } + } catch (JavaModelException e) { + e.printStackTrace(); + } + } + } + } + + /** + * Sets the error messages. If message is <code>null</code>, the message is removed. + * @param message the message to set, or <code>null</code> to remove the current message + * @param textWidget the {@link Text} widget associated to the message. + */ + private final void setErrorMessage(String message, Text textWidget) { + if (message != null) { + setHasError(true); + getManagedForm().getMessageManager().addMessage(textWidget, message, null /* data */, + IMessageProvider.ERROR, textWidget); + } else { + setHasError(false); + getManagedForm().getMessageManager().removeMessage(textWidget, textWidget); + } + } + + @Override + public String[] getPossibleValues(String prefix) { + // Compute a list of existing classes for content assist completion + IProject project = getProject(); + if (project == null || mReferenceClass == null) { + return null; + } + + try { + IJavaProject javaProject = BaseProjectHelper.getJavaProject(project); + IType type = javaProject.findType(mReferenceClass); + // Use sets because query sometimes repeats the same class + Set<String> libraryTypes = new HashSet<String>(80); + Set<String> localTypes = new HashSet<String>(30); + if (type != null) { + ITypeHierarchy hierarchy = type.newTypeHierarchy(new NullProgressMonitor()); + IType[] allSubtypes = hierarchy.getAllSubtypes(type); + for (IType subType : allSubtypes) { + int flags = subType.getFlags(); + if (Flags.isPublic(flags) && !Flags.isAbstract(flags)) { + String fqcn = subType.getFullyQualifiedName(); + if (subType.getResource() != null) { + localTypes.add(fqcn); + } else { + libraryTypes.add(fqcn); + } + } + } + } + + List<String> local = new ArrayList<String>(localTypes); + List<String> library = new ArrayList<String>(libraryTypes); + Collections.sort(local); + Collections.sort(library); + List<String> combined = new ArrayList<String>(local.size() + library.size()); + combined.addAll(local); + combined.addAll(library); + return combined.toArray(new String[combined.size()]); + } catch (Exception e) { + AdtPlugin.log(e, null); + } + + return null; + } +} + |