org.eclipse.pde.api.tools.internal.ProjectApiDescription.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.pde.api.tools.internal.ProjectApiDescription.java

Source

/*******************************************************************************
 * Copyright (c) 2008, 2013 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.pde.api.tools.internal;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarFile;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.ICompilationUnit;
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.JavaModelException;
import org.eclipse.pde.api.tools.internal.builder.BuildStamps;
import org.eclipse.pde.api.tools.internal.model.BundleComponent;
import org.eclipse.pde.api.tools.internal.model.ProjectComponent;
import org.eclipse.pde.api.tools.internal.provisional.ApiDescriptionVisitor;
import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin;
import org.eclipse.pde.api.tools.internal.provisional.Factory;
import org.eclipse.pde.api.tools.internal.provisional.IApiAnnotations;
import org.eclipse.pde.api.tools.internal.provisional.RestrictionModifiers;
import org.eclipse.pde.api.tools.internal.provisional.VisibilityModifiers;
import org.eclipse.pde.api.tools.internal.provisional.descriptors.IElementDescriptor;
import org.eclipse.pde.api.tools.internal.provisional.descriptors.IPackageDescriptor;
import org.eclipse.pde.api.tools.internal.provisional.descriptors.IReferenceTypeDescriptor;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiBaseline;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiTypeContainer;
import org.eclipse.pde.api.tools.internal.provisional.scanner.TagScanner;
import org.eclipse.pde.api.tools.internal.util.Util;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * Implementation of an API description for a Java project.
 * 
 * @since 1.0
 */
public class ProjectApiDescription extends ApiDescription {

    /**
     * Associated Java project
     */
    private IJavaProject fProject;

    /**
     * Time stamp at which package information was created
     */
    public long fPackageTimeStamp = 0L;

    /**
     * Whether a package refresh is in progress
     */
    private boolean fRefreshingInProgress = false;

    /**
     * Associated manifest file
     */
    public IFile fManifestFile;

    /**
     * Whether this API description is in synch with its project. Becomes false
     * if anything in a project changes. When true, visiting can be performed by
     * traversing the cached nodes, rather than traversing the java model
     * elements (effectively building the cache).
     */
    private boolean fInSynch = false;

    /**
     * A node for a package.
     */
    class PackageNode extends ManifestNode {

        IPackageFragment[] fFragments;

        /**
         * Constructs a new node.
         * 
         * @param parent
         * @param element
         * @param visibility
         * @param restrictions
         */
        public PackageNode(IPackageFragment fragments[], ManifestNode parent, IElementDescriptor element,
                int visibility, int restrictions) {
            super(parent, element, visibility, restrictions);
            fFragments = fragments;
        }

        /*
         * (non-Javadoc)
         * @see
         * org.eclipse.pde.api.tools.internal.ApiDescription.ManifestNode#refresh
         * ()
         */
        @Override
        protected ManifestNode refresh() {
            refreshPackages();
            for (int i = 0; i < fFragments.length; i++) {
                if (!fFragments[i].exists()) {
                    modified();
                    return null;
                }
            }
            return this;
        }

        /*
         * (non-Javadoc)
         * @see
         * org.eclipse.pde.api.tools.internal.ApiDescription.ManifestNode#persistXML
         * (org.w3c.dom.Document, org.w3c.dom.Element, java.lang.String)
         */
        @Override
        void persistXML(Document document, Element parentElement) {
            if (hasApiVisibility(this)) {
                Element pkg = document.createElement(IApiXmlConstants.ELEMENT_PACKAGE);
                for (int i = 0; i < fFragments.length; i++) {
                    Element fragment = document.createElement(IApiXmlConstants.ELEMENT_PACKAGE_FRAGMENT);
                    fragment.setAttribute(IApiXmlConstants.ATTR_HANDLE, fFragments[i].getHandleIdentifier());
                    pkg.appendChild(fragment);
                }
                pkg.setAttribute(IApiXmlConstants.ATTR_VISIBILITY, Integer.toString(this.visibility));
                persistChildren(document, pkg, children);
                parentElement.appendChild(pkg);
            }
        }

        /*
         * (non-Javadoc)
         * @see
         * org.eclipse.pde.api.tools.internal.ApiDescription.ManifestNode#toString
         * ()
         */
        @Override
        public String toString() {
            StringBuffer buffer = new StringBuffer();
            String name = ((IPackageDescriptor) element).getName();
            buffer.append("Package Node: ").append(name.equals("") ? "<default package>" : name); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
            buffer.append("\nVisibility: ").append(VisibilityModifiers.getVisibilityName(visibility)); //$NON-NLS-1$
            buffer.append("\nRestrictions: ").append(RestrictionModifiers.getRestrictionText(restrictions)); //$NON-NLS-1$
            if (fFragments != null) {
                buffer.append("\nFragments:"); //$NON-NLS-1$
                IPackageFragment fragment = null;
                for (int i = 0; i < fFragments.length; i++) {
                    fragment = fFragments[i];
                    buffer.append("\n\t").append(fragment.getElementName()); //$NON-NLS-1$
                    buffer.append(" ["); //$NON-NLS-1$
                    buffer.append(fragment.getParent().getElementName());
                    buffer.append("]"); //$NON-NLS-1$
                }
            }
            return buffer.toString();
        }
    }

    /**
     * Node for a reference type.
     */
    class TypeNode extends ManifestNode {

        long fTimeStamp = -1L;
        long fBuildStamp = -1L;
        private boolean fRefreshing = false;

        IType fType;

        /**
         * Constructs a node for a reference type.
         * 
         * @param type
         * @param parent
         * @param element
         * @param visibility
         * @param restrictions
         */
        public TypeNode(IType type, ManifestNode parent, IElementDescriptor element, int visibility,
                int restrictions) {
            super(parent, element, visibility, restrictions);
            fType = type;
            if (parent instanceof TypeNode) {
                fTimeStamp = ((TypeNode) parent).fTimeStamp;
                fBuildStamp = ((TypeNode) parent).fBuildStamp;
            }
        }

        /*
         * (non-Javadoc)
         * @see
         * org.eclipse.pde.api.tools.internal.ApiDescription.ManifestNode#refresh
         * ()
         */
        @Override
        protected synchronized ManifestNode refresh() {
            if (fRefreshing) {
                if (ApiPlugin.DEBUG_API_DESCRIPTION) {
                    StringBuffer buffer = new StringBuffer();
                    buffer.append("Refreshing manifest node: "); //$NON-NLS-1$
                    buffer.append(this);
                    buffer.append(" aborted because a refresh is already in progress"); //$NON-NLS-1$
                    System.out.println(buffer.toString());
                }
                return this;
            }
            try {
                fRefreshing = true;
                int parentVis = resolveVisibility(parent);
                if (VisibilityModifiers.isAPI(parentVis)) {
                    ICompilationUnit unit = fType.getCompilationUnit();
                    if (unit != null) {
                        IResource resource = null;
                        try {
                            resource = unit.getUnderlyingResource();
                        } catch (JavaModelException e) {
                            if (ApiPlugin.DEBUG_API_DESCRIPTION) {
                                StringBuffer buffer = new StringBuffer();
                                buffer.append("Failed to get underlying resource for compilation unit: "); //$NON-NLS-1$
                                buffer.append(unit);
                                System.out.println(buffer.toString());
                            }
                            // exception if the resource does not exist
                            if (!e.getJavaModelStatus().isDoesNotExist()) {
                                ApiPlugin.log(e.getStatus());
                                return this;
                            }
                        }
                        if (resource != null && resource.exists()) {
                            long stamp = resource.getModificationStamp();
                            if (stamp != fTimeStamp) {
                                // compute current CRC
                                CRCVisitor visitor = new CRCVisitor();
                                visitType(this, visitor);
                                long crc = visitor.getValue();
                                if (ApiPlugin.DEBUG_API_DESCRIPTION) {
                                    StringBuffer buffer = new StringBuffer();
                                    buffer.append("Resource has changed for type manifest node: "); //$NON-NLS-1$
                                    buffer.append(this);
                                    buffer.append(" tag scanning the new type"); //$NON-NLS-1$
                                    buffer.append(" (CRC "); //$NON-NLS-1$
                                    buffer.append(crc);
                                    buffer.append(')');
                                    System.out.println(buffer.toString());
                                }
                                modified();
                                children.clear();
                                restrictions = RestrictionModifiers.NO_RESTRICTIONS;
                                fTimeStamp = resource.getModificationStamp();
                                try {
                                    TagScanner.newScanner().scan(unit, ProjectApiDescription.this,
                                            getApiTypeContainer(
                                                    (IPackageFragmentRoot) fType.getPackageFragment().getParent()),
                                            null);
                                } catch (CoreException e) {
                                    ApiPlugin.log(e.getStatus());
                                }
                                // see if the description changed
                                visitor = new CRCVisitor();
                                visitType(this, visitor);
                                long crc2 = visitor.getValue();
                                if (crc != crc2) {
                                    // update relative build time stamp
                                    fBuildStamp = BuildStamps.getBuildStamp(resource.getProject());
                                    if (ApiPlugin.DEBUG_API_DESCRIPTION) {
                                        StringBuffer buffer = new StringBuffer();
                                        buffer.append("CRC changed for type manifest node: "); //$NON-NLS-1$
                                        buffer.append(this);
                                        buffer.append(" (CRC "); //$NON-NLS-1$
                                        buffer.append(crc2);
                                        buffer.append(')');
                                        System.out.println(buffer.toString());
                                    }
                                }
                            }
                        } else {
                            if (ApiPlugin.DEBUG_API_DESCRIPTION) {
                                StringBuffer buffer = new StringBuffer();
                                buffer.append("Underlying resource for the type manifest node: "); //$NON-NLS-1$
                                buffer.append(this);
                                buffer.append(" does not exist or is null"); //$NON-NLS-1$
                                System.out.println(buffer.toString());
                            }
                            // element has been removed
                            modified();
                            parent.children.remove(element);
                            return null;
                        }
                    } else {
                        if (ApiPlugin.DEBUG_API_DESCRIPTION) {
                            StringBuffer buffer = new StringBuffer();
                            buffer.append("Failed to look up compilation unit for "); //$NON-NLS-1$
                            buffer.append(fType);
                            buffer.append(" refreshing type manifest node: "); //$NON-NLS-1$
                            buffer.append(this);
                            System.out.println(buffer.toString());
                        }
                        // TODO: binary type
                    }
                } else {
                    // don't scan internal types
                }
            } finally {
                fRefreshing = false;
            }
            return this;
        }

        /*
         * (non-Javadoc)
         * @see
         * org.eclipse.pde.api.tools.internal.ApiDescription.ManifestNode#persistXML
         * (org.w3c.dom.Document, org.w3c.dom.Element, java.lang.String)
         */
        @Override
        void persistXML(Document document, Element parentElement) {
            if (hasApiVisibility(this)) {
                Element type = document.createElement(IApiXmlConstants.ELEMENT_TYPE);
                type.setAttribute(IApiXmlConstants.ATTR_HANDLE, fType.getHandleIdentifier());
                persistAnnotations(type);
                type.setAttribute(IApiXmlConstants.ATTR_MODIFICATION_STAMP, Long.toString(fTimeStamp));
                persistChildren(document, type, children);
                parentElement.appendChild(type);
            }
        }

        /*
         * (non-Javadoc)
         * @see
         * org.eclipse.pde.api.tools.internal.ApiDescription.ManifestNode#toString
         * ()
         */
        @Override
        public String toString() {
            StringBuffer buffer = new StringBuffer();
            buffer.append("Type Node: ").append(fType.getFullyQualifiedName()); //$NON-NLS-1$
            buffer.append("\nVisibility: ").append(VisibilityModifiers.getVisibilityName(visibility)); //$NON-NLS-1$
            buffer.append("\nRestrictions: ").append(RestrictionModifiers.getRestrictionText(restrictions)); //$NON-NLS-1$
            if (parent != null) {
                String pname = parent.element.getElementType() == IElementDescriptor.PACKAGE
                        ? ((IPackageDescriptor) parent.element).getName()
                        : ((IReferenceTypeDescriptor) parent.element).getQualifiedName();
                buffer.append("\nParent: ").append(pname); //$NON-NLS-1$
            }
            return buffer.toString();
        }
    }

    /**
     * Constructs a new API description for the given project API component.
     * 
     * @param component
     */
    public ProjectApiDescription(IJavaProject project) {
        super(project.getElementName());
        fProject = project;
    }

    /*
     * (non-Javadoc)
     * @see
     * org.eclipse.pde.api.tools.internal.provisional.IApiDescription#accept
     * (org.eclipse.pde.api.tools.internal.provisional.ApiDescriptionVisitor)
     */
    @Override
    public synchronized void accept(ApiDescriptionVisitor visitor, IProgressMonitor monitor) {
        boolean completeVisit = true;
        if (fInSynch) {
            super.accept(visitor, monitor);
        } else {
            try {
                IPackageFragment[] fragments = getLocalPackageFragments();
                IJavaElement[] children = null;
                IJavaElement child = null;
                ICompilationUnit unit = null;
                for (int j = 0; j < fragments.length; j++) {
                    if (ApiPlugin.DEBUG_API_DESCRIPTION) {
                        System.out.println("\t" + fragments[j].getElementName()); //$NON-NLS-1$
                    }
                    IPackageDescriptor packageDescriptor = Factory.packageDescriptor(fragments[j].getElementName());
                    // visit package
                    ManifestNode pkgNode = findNode(packageDescriptor, false);
                    if (pkgNode != null) {
                        IApiAnnotations annotations = resolveAnnotations(pkgNode, packageDescriptor);
                        if (visitor.visitElement(packageDescriptor, annotations)) {
                            children = fragments[j].getChildren();
                            for (int k = 0; k < children.length; k++) {
                                child = children[k];
                                if (child instanceof ICompilationUnit) {
                                    unit = (ICompilationUnit) child;
                                    String cuName = unit.getElementName();
                                    String tName = cuName.substring(0, cuName.length() - ".java".length()); //$NON-NLS-1$
                                    visit(visitor, unit.getType(tName));
                                } else if (child instanceof IClassFile) {
                                    visit(visitor, ((IClassFile) child).getType());
                                }
                            }
                        } else {
                            completeVisit = false;
                        }
                        visitor.endVisitElement(packageDescriptor, annotations);
                    }
                }
            } catch (JavaModelException e) {
                completeVisit = false;
                ApiPlugin.log(e.getStatus());
            } finally {
                if (completeVisit) {
                    fInSynch = true;
                }
            }
        }
    }

    /**
     * Visits a type.
     * 
     * @param visitor
     * @param owningComponent
     * @param type
     */
    private void visit(ApiDescriptionVisitor visitor, IType type) {
        IElementDescriptor element = getElementDescriptor(type);
        ManifestNode typeNode = findNode(element, false);
        if (typeNode != null) {
            visitType(typeNode, visitor);
        }
    }

    void visitType(ManifestNode node, ApiDescriptionVisitor visitor) {
        IApiAnnotations annotations = resolveAnnotations(node, node.element);
        if (visitor.visitElement(node.element, annotations)) {
            // children
            if (node.children != null) {
                visitChildren(visitor, node.children, null);
            }
        }
        visitor.endVisitElement(node.element, annotations);
    }

    /*
     * (non-Javadoc)
     * @see
     * org.eclipse.pde.api.tools.internal.ApiDescription#isInsertOnResolve(org
     * .eclipse
     * .pde.api.tools.internal.provisional.descriptors.IElementDescriptor)
     */
    @Override
    protected boolean isInsertOnResolve(IElementDescriptor elementDescriptor) {
        switch (elementDescriptor.getElementType()) {
        case IElementDescriptor.METHOD:
        case IElementDescriptor.FIELD:
            return false;
        case IElementDescriptor.TYPE:
            // no need to insert member types
            return ((IReferenceTypeDescriptor) elementDescriptor).getEnclosingType() == null;
        default:
            return true;
        }
    }

    /*
     * (non-Javadoc)
     * @see
     * org.eclipse.pde.api.tools.internal.ApiDescription#createNode(org.eclipse
     * .pde.api.tools.internal.ApiDescription.ManifestNode,
     * org.eclipse.pde.api.tools
     * .internal.provisional.descriptors.IElementDescriptor)
     */
    @Override
    protected ManifestNode createNode(ManifestNode parentNode, IElementDescriptor element) {
        switch (element.getElementType()) {
        case IElementDescriptor.PACKAGE:
            try {
                IPackageDescriptor pkg = (IPackageDescriptor) element;
                IPackageFragmentRoot[] roots = getJavaProject().getPackageFragmentRoots();
                List<IPackageFragment> fragments = new ArrayList<IPackageFragment>(1);
                for (int i = 0; i < roots.length; i++) {
                    IPackageFragmentRoot root = roots[i];
                    IClasspathEntry entry = root.getRawClasspathEntry();
                    switch (entry.getEntryKind()) {
                    case IClasspathEntry.CPE_SOURCE:
                    case IClasspathEntry.CPE_LIBRARY:
                        IPackageFragment fragment = root.getPackageFragment(pkg.getName());
                        if (fragment.exists()) {
                            fragments.add(fragment);
                        }
                        break;
                    default:
                        if (!root.isArchive() && root.getKind() == IPackageFragmentRoot.K_BINARY) {
                            // class file folder
                            fragment = root.getPackageFragment(pkg.getName());
                            if (fragment.exists()) {
                                fragments.add(fragment);
                            }
                        }
                    }
                }
                if (fragments.isEmpty()) {
                    return null;
                } else {
                    return newPackageNode(fragments.toArray(new IPackageFragment[fragments.size()]), parentNode,
                            element, VisibilityModifiers.PRIVATE, RestrictionModifiers.NO_RESTRICTIONS);
                }

            } catch (CoreException e) {
                return null;
            }
        case IElementDescriptor.TYPE:
            IReferenceTypeDescriptor descriptor = (IReferenceTypeDescriptor) element;
            try {
                IType type = null;
                String name = descriptor.getName();
                if (parentNode instanceof PackageNode) {
                    IPackageFragment[] fragments = ((PackageNode) parentNode).fFragments;
                    for (int i = 0; i < fragments.length; i++) {
                        IPackageFragment fragment = fragments[i];
                        if (fragment.getKind() == IPackageFragmentRoot.K_SOURCE) {
                            ICompilationUnit unit = fragment.getCompilationUnit(name + ".java"); //$NON-NLS-1$
                            try {
                                IResource resource = unit.getUnderlyingResource();
                                if (resource != null) {
                                    type = unit.getType(name);
                                }
                            } catch (JavaModelException jme) {
                                // exception if the resource does not exist
                                if (!jme.getJavaModelStatus().isDoesNotExist()) {
                                    throw jme;
                                }
                            }
                        } else {
                            IClassFile file = fragment.getClassFile(name + ".class"); //$NON-NLS-1$
                            if (file.exists()) {
                                type = file.getType();
                            }
                        }
                    }
                } else if (parentNode instanceof TypeNode) {
                    type = ((TypeNode) parentNode).fType.getType(name);
                }
                if (type != null) {
                    return newTypeNode(type, parentNode, element, VISIBILITY_INHERITED,
                            RestrictionModifiers.NO_RESTRICTIONS);
                }
            } catch (CoreException e) {
                return null;
            }
            return null;
        default:
            break;
        }
        return super.createNode(parentNode, element);
    }

    /**
     * Constructs and returns a new node for the given package fragment.
     * 
     * @param fragment
     * @param parent
     * @param descriptor
     * @param vis
     * @param res
     * @return
     */
    public PackageNode newPackageNode(IPackageFragment[] fragments, ManifestNode parent,
            IElementDescriptor descriptor, int vis, int res) {
        return new PackageNode(fragments, parent, descriptor, vis, res);
    }

    /**
     * Constructs and returns a new node for the given type.
     * 
     * @param type
     * @param parent
     * @param descriptor
     * @param vis
     * @param res
     * @return
     */
    TypeNode newTypeNode(IType type, ManifestNode parent, IElementDescriptor descriptor, int vis, int res) {
        return new TypeNode(type, parent, descriptor, vis, res);
    }

    /**
     * Constructs a new manifest node.
     * 
     * @param parent
     * @param element
     * @param vis
     * @param res
     * @return
     */
    ManifestNode newNode(ManifestNode parent, IElementDescriptor element, int vis, int res) {
        return new ManifestNode(parent, element, vis, res);
    }

    /**
     * Refreshes package nodes if required.
     */
    synchronized void refreshPackages() {
        if (fRefreshingInProgress) {
            if (ApiPlugin.DEBUG_API_DESCRIPTION) {
                StringBuffer buffer = new StringBuffer();
                buffer.append("Refreshing manifest node: "); //$NON-NLS-1$
                buffer.append(this);
                buffer.append(" aborted because a refresh is already in progress"); //$NON-NLS-1$
                System.out.println(buffer.toString());
            }
            return;
        }
        // check if in synch
        if (fManifestFile == null || (fManifestFile.getModificationStamp() != fPackageTimeStamp)) {
            try {
                modified();
                fRefreshingInProgress = true;
                // set all existing packages to PRIVATE (could clear
                // the map, but it would be less efficient)
                Iterator<ManifestNode> iterator = fPackageMap.values().iterator();
                while (iterator.hasNext()) {
                    PackageNode node = (PackageNode) iterator.next();
                    node.visibility = VisibilityModifiers.PRIVATE;
                }
                fManifestFile = getJavaProject().getProject().getFile(JarFile.MANIFEST_NAME);
                if (fManifestFile.exists()) {
                    try {
                        IPackageFragment[] fragments = getLocalPackageFragments();
                        Set<String> names = new HashSet<String>();
                        for (int i = 0; i < fragments.length; i++) {
                            names.add(fragments[i].getElementName());
                        }
                        ProjectComponent component = getApiComponent();
                        BundleComponent.initializeApiDescription(this, component.getBundleDescription(), names);
                        fPackageTimeStamp = fManifestFile.getModificationStamp();
                    } catch (CoreException e) {
                        ApiPlugin.log(e.getStatus());
                    }
                }
            } finally {
                fRefreshingInProgress = false;
            }
        }
    }

    private IElementDescriptor getElementDescriptor(IJavaElement element) {
        switch (element.getElementType()) {
        case IJavaElement.PACKAGE_FRAGMENT:
            return Factory.packageDescriptor(element.getElementName());
        case IJavaElement.TYPE:
            return Factory.typeDescriptor(((IType) element).getFullyQualifiedName('$'));
        default:
            return null;
        }
    }

    /**
     * Returns the Java project associated with this component.
     * 
     * @return associated Java project
     */
    private IJavaProject getJavaProject() {
        return fProject;
    }

    /**
     * Returns a class file container for the given package fragment root.
     * 
     * @param root package fragment root
     * @return class file container
     * @exception CoreException if container cannot be located
     */
    synchronized IApiTypeContainer getApiTypeContainer(IPackageFragmentRoot root) throws CoreException {
        IApiTypeContainer container = getApiComponent().getTypeContainer(root);
        if (container == null) {
            throw new CoreException(new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID,
                    "Unable to resolve type conatiner for package fragment root")); //$NON-NLS-1$
        }
        return container;
    }

    /**
     * Returns all package fragments that originate from this project.
     * 
     * @return all package fragments that originate from this project
     */
    private IPackageFragment[] getLocalPackageFragments() {
        List<IJavaElement> local = new ArrayList<IJavaElement>();
        try {
            IPackageFragmentRoot[] roots = getJavaProject().getPackageFragmentRoots();
            for (int i = 0; i < roots.length; i++) {
                IPackageFragmentRoot root = roots[i];
                // only care about roots originating from this project (binary
                // or source)
                IResource resource = root.getCorrespondingResource();
                if (resource != null && resource.getProject().equals(getJavaProject().getProject())) {
                    IJavaElement[] children = root.getChildren();
                    for (int j = 0; j < children.length; j++) {
                        local.add(children[j]);
                    }
                }
            }
        } catch (JavaModelException e) {
            // ignore
        }
        return local.toArray(new IPackageFragment[local.size()]);
    }

    /**
     * Returns this API description as XML.
     * 
     * @throws CoreException
     */
    public synchronized String getXML() throws CoreException {
        Document document = Util.newDocument();
        Element component = document.createElement(IApiXmlConstants.ELEMENT_COMPONENT);
        component.setAttribute(IApiXmlConstants.ATTR_ID, getJavaProject().getElementName());
        component.setAttribute(IApiXmlConstants.ATTR_MODIFICATION_STAMP, Long.toString(fPackageTimeStamp));
        component.setAttribute(IApiXmlConstants.ATTR_VERSION, IApiXmlConstants.API_DESCRIPTION_CURRENT_VERSION);
        document.appendChild(component);
        persistChildren(document, component, fPackageMap);
        return Util.serializeDocument(document);
    }

    /**
     * Persists the elements in the given map as XML elements, appended to the
     * given xmlElement.
     * 
     * @param document XML document
     * @param xmlElement node to append children no
     * @param elementMap elements to persist
     */
    void persistChildren(Document document, Element xmlElement, Map<IElementDescriptor, ManifestNode> elementMap) {
        Iterator<ManifestNode> iterator = elementMap.values().iterator();
        while (iterator.hasNext()) {
            ManifestNode node = iterator.next();
            node.persistXML(document, xmlElement);
        }
    }

    /**
     * Cleans this API description so it will be re-populated with fresh data.
     */
    public synchronized void clean() {
        fPackageMap.clear();
        fPackageTimeStamp = -1L;
        fInSynch = false;
        modified();
    }

    /**
     * Notes that the underlying project has changed in some way and that the
     * description cache is no longer in synch with the project.
     */
    public synchronized void projectChanged() {
        fInSynch = false;
    }

    /**
     * Notes that the underlying project classpath has changed in some way and
     * that the description cache is no longer in synch with the project.
     */
    public synchronized void projectClasspathChanged() {
        fInSynch = false;
        // we want to flush the packages cache to "reload" all packages using
        // the new package fragment roots
        fPackageTimeStamp = -1L;
    }

    /*
     * (non-Javadoc)
     * @see org.eclipse.pde.api.tools.internal.ApiDescription#toString()
     */
    @Override
    public String toString() {
        StringBuffer buffer = new StringBuffer();
        buffer.append("Project API description for: ").append(getJavaProject().getElementName()); //$NON-NLS-1$
        return buffer.toString();
    }

    /**
     * Returns the API component associated with this API description
     * 
     * @return API component
     * @exception CoreException if the API component cannot be located
     */
    private ProjectComponent getApiComponent() throws CoreException {
        IApiBaseline baseline = ApiBaselineManager.getManager().getWorkspaceBaseline();
        ProjectComponent component = (ProjectComponent) baseline.getApiComponent(getJavaProject().getProject());
        if (component == null) {
            throw new CoreException(new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID,
                    "Unable to resolve project API component for API description")); //$NON-NLS-1$
        }
        return component;
    }

    /**
     * Resolves annotations based on inheritance for the given node and element.
     * 
     * @param node manifest node
     * @param element the element annotations are being resolved for
     * @return annotations
     */
    @Override
    protected IApiAnnotations resolveAnnotations(ManifestNode node, IElementDescriptor element) {
        IApiAnnotations ann = super.resolveAnnotations(node, element);
        if (node instanceof TypeNode) {
            return new TypeAnnotations(ann, ((TypeNode) node).fBuildStamp);
        }
        return ann;

    }
}