FileDocCategorySizeDatePackage
UiClassAttributeNode.javaAPI DocAndroid 1.5 API30101Wed May 06 22:41:10 BST 2009com.android.ide.eclipse.editors.manifest.model

UiClassAttributeNode

public class UiClassAttributeNode extends com.android.ide.eclipse.editors.uimodel.UiTextAttributeNode
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.

See {@link UiTextAttributeNode} for more information.

Fields Summary
private String
mReferenceClass
private IPostTypeCreationAction
mPostCreationAction
private boolean
mMandatory
private final boolean
mDefaultToProjectOnly
Constructors Summary
public UiClassAttributeNode(String referenceClass, IPostTypeCreationAction postCreationAction, boolean mandatory, com.android.ide.eclipse.editors.descriptors.AttributeDescriptor attributeDescriptor, com.android.ide.eclipse.editors.uimodel.UiElementNode uiParent, boolean defaultToProjectOnly)
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.

        super(attributeDescriptor, uiParent);
        
        mReferenceClass = referenceClass;
        mPostCreationAction = postCreationAction;
        mMandatory = mandatory;
        mDefaultToProjectOnly = defaultToProjectOnly;
    
Methods Summary
private voidcreateNewClass(java.lang.String packageName, java.lang.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();
                }
            }
        }
    
public voidcreateUiControl(org.eclipse.swt.widgets.Composite parent, org.eclipse.ui.forms.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();
            }
        });
    
private java.lang.StringgetManifestPackage()
Returns the current value of the /manifest/package attribute.

return
the package or an empty string if not found

        // 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$
    
private org.eclipse.jdt.core.IPackageFragmentRoot[]getPackageFragmentRoots(org.eclipse.core.resources.IProject project, boolean include_containers)
Computes and return the {@link IPackageFragmentRoot}s corresponding to the source folders of the specified project.

param
project the project
param
b
return
an array of IPackageFragmentRoot.

        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()]);
    
public java.lang.String[]getPossibleValues(java.lang.String prefix)

        // TODO: compute a list of existing classes for content assist completion
        return null;
    
private org.eclipse.core.resources.IProjectgetProject()

        UiElementNode uiNode = getUiParent();
        AndroidEditor 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;
    
private voidhandleBrowseClick()

        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 voidhandleLabelClick()

        // 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(AndroidConstants.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 voidhandleNewType(org.eclipse.jdt.core.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(AndroidConstants.RE_DOT);
            if (packages.length == 1) {
                text.setText(name);
            } else {
                text.setText("." + name); //$NON-NLS-1$
            }
        } else {
            text.setText(name);
        }
    
protected voidonAddValidators(org.eclipse.swt.widgets.Text text)

        ModifyListener listener = new ModifyListener() {
            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 = AndroidManifestParser.combinePackageAndClassName(
                            javaPackage, textValue);
                    
                    // only test the vilibility for activities.
                    boolean testVisibility = AndroidConstants.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() {
            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 final voidsetErrorMessage(java.lang.String message, org.eclipse.swt.widgets.Text textWidget)
Sets the error messages. If message is null, the message is removed.

param
message the message to set, or null to remove the current message
param
textWidget the {@link Text} widget associated to the message.

        if (message != null) {
            setHasError(true);
            getManagedForm().getMessageManager().addMessage(textWidget, message, null /* data */,
                    IMessageProvider.ERROR, textWidget);
        } else {
            setHasError(false);
            getManagedForm().getMessageManager().removeMessage(textWidget, textWidget);
        }