com.siteview.mde.internal.core.project.ProjectModifyOperation.java Source code

Java tutorial

Introduction

Here is the source code for com.siteview.mde.internal.core.project.ProjectModifyOperation.java

Source

/*******************************************************************************
 * Copyright (c) 2010 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 com.siteview.mde.internal.core.project;

import java.util.*;
import java.util.Map.Entry;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.jdt.core.*;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.osgi.service.resolver.VersionRange;
import com.siteview.mde.core.build.*;
import com.siteview.mde.core.monitor.*;
import com.siteview.mde.core.project.*;
import com.siteview.mde.internal.core.*;
import com.siteview.mde.internal.core.build.WorkspaceBuildModel;
import com.siteview.mde.internal.core.bundle.*;
import com.siteview.mde.internal.core.ibundle.*;
import com.siteview.mde.internal.core.monitor.WorkspaceMonitorModelBase;
import com.siteview.mde.internal.core.text.bundle.*;
import com.siteview.mde.internal.core.util.CoreUtility;
import org.osgi.framework.Constants;
import org.osgi.framework.Version;
import org.osgi.service.prefs.BackingStoreException;

/**
 * Operation to create or modify a PDE project based on a bundle project description.
 * 
 * @since 3.6
 */
public class ProjectModifyOperation {

    private WorkspaceMonitorModelBase fModel;

    private final static IPath[] EXCLUDE_NONE = {};

    /**
     * Creates or modifies a project based on the given description.
     * 
     * @param monitor progress monitor or <code>null</code>
     * @param description project description
     * @throws CoreException if project creation fails
     */
    public void execute(IProgressMonitor monitor, IBundleProjectDescription description) throws CoreException {
        // retrieve current description of the project to detect differences
        IProject project = description.getProject();
        IBundleProjectService service = (IBundleProjectService) MDECore.getDefault()
                .acquireService(IBundleProjectService.class.getName());
        IBundleProjectDescription before = service.getDescription(project);
        boolean considerRoot = !project.exists();
        String taskName = null;
        boolean jpExisted = false;
        if (project.exists()) {
            taskName = Messages.ProjectModifyOperation_0;
            jpExisted = before.hasNature(JavaCore.NATURE_ID);
        } else {
            taskName = Messages.ProjectModifyOperation_1;
            // new bundle projects get Java and Plug-in natures
            if (description.getNatureIds().length == 0) {
                description
                        .setNatureIds(new String[] { IBundleProjectDescription.PLUGIN_NATURE, JavaCore.NATURE_ID });
            }
        }
        boolean becomeBundle = !before.hasNature(IBundleProjectDescription.PLUGIN_NATURE)
                && description.hasNature(IBundleProjectDescription.PLUGIN_NATURE);

        // set default values when migrating from Java project to bundle project
        if (jpExisted && becomeBundle) {
            if (description.getExecutionEnvironments() == null) {
                // use EE from Java project when unspecified in the description, and a bundle nature is being added
                IJavaProject jp = JavaCore.create(project);
                if (jp.exists()) {
                    IClasspathEntry[] classpath = jp.getRawClasspath();
                    for (int i = 0; i < classpath.length; i++) {
                        IClasspathEntry entry = classpath[i];
                        if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER) {
                            String id = JavaRuntime.getExecutionEnvironmentId(entry.getPath());
                            if (id != null) {
                                description.setExecutionEnvironments(new String[] { id });
                                break;
                            }
                        }
                    }
                }
            }
        }
        // other default values when becoming a bundle
        if (becomeBundle) {
            // set default values for where unspecified
            if (description.getBundleVersion() == null) {
                description.setBundleVersion(new Version(1, 0, 0, "qualifier")); //$NON-NLS-1$
            }
        }

        SubMonitor sub = SubMonitor.convert(monitor, taskName, 6);
        // create and open project
        createProject(description);
        // set bundle root for new projects
        if (considerRoot) {
            IFolder folder = null;
            IPath root = description.getBundleRoot();
            if (root != null && !root.isEmpty()) {
                folder = project.getFolder(root);
                CoreUtility.createFolder(folder);
            }
            PDEProject.setBundleRoot(project, folder);
        }
        sub.worked(1);
        configureNatures(description);
        sub.worked(1);
        if (project.hasNature(JavaCore.NATURE_ID)) {
            configureJavaProject(description, before, jpExisted);
        }
        sub.worked(1);
        configureManifest(description, before);
        sub.worked(1);
        configureBuildPropertiesFile(description, before);
        sub.worked(1);

        // project settings for Equinox, Extension Registry, Automated dependency policy,
        // manifest editor launch shortcuts and export wizard
        IEclipsePreferences pref = new ProjectScope(project).getNode(MDECore.PLUGIN_ID);
        if (pref != null) {
            // best guess for automated dependency management: Equinox + Extensions = use required bundle
            if (description.isEquinox() && description.isExtensionRegistry()) {
                pref.remove(ICoreConstants.RESOLVE_WITH_REQUIRE_BUNDLE); // i.e. use required bundle
            } else {
                pref.putBoolean(ICoreConstants.RESOLVE_WITH_REQUIRE_BUNDLE, false);
            }
            if (description.isExtensionRegistry()) {
                pref.remove(ICoreConstants.EXTENSIONS_PROPERTY); // i.e. support extensions
            } else {
                pref.putBoolean(ICoreConstants.EXTENSIONS_PROPERTY, false);
            }
            if (description.isEquinox()) {
                pref.remove(ICoreConstants.EQUINOX_PROPERTY); // i.e. using Equinox
            } else {
                pref.putBoolean(ICoreConstants.EQUINOX_PROPERTY, false);
            }
            String[] shorts = description.getLaunchShortcuts();
            if (shorts == null || shorts.length == 0) {
                pref.remove(ICoreConstants.MANIFEST_LAUNCH_SHORTCUTS); // use defaults
            } else {
                StringBuffer value = new StringBuffer();
                for (int i = 0; i < shorts.length; i++) {
                    if (i > 0) {
                        value.append(',');
                    }
                    value.append(shorts[i]);
                }
                pref.put(ICoreConstants.MANIFEST_LAUNCH_SHORTCUTS, value.toString());
            }
            if (description.getExportWizardId() == null) {
                pref.remove(ICoreConstants.MANIFEST_EXPORT_WIZARD);
            } else {
                pref.put(ICoreConstants.MANIFEST_EXPORT_WIZARD, description.getExportWizardId());
            }
            try {
                pref.flush();
            } catch (BackingStoreException e) {
                throw new CoreException(
                        new Status(IStatus.ERROR, MDECore.PLUGIN_ID, Messages.ProjectModifyOperation_2, e));
            }
        }

        if (fModel.isDirty()) {
            fModel.save();
        }
        sub.worked(1);
        sub.done();
        if (monitor != null) {
            monitor.done();
        }
    }

    /**
     * Returns the model created by this operation.
     * 
     * @return model 
     */
    public WorkspaceMonitorModelBase getModel() {
        return fModel;
    }

    /**
     * Configures the build path and output location of the described Java project.
     * If the Java project existed before this operation, new build path entries are
     * added for the bundle class path, if required, but we don't change the exiting
     * build path.
     * 
     * @param description desired project description
     * @param before state before the operation
     * @param existed whether the Java project existed before the operation
     */
    private void configureJavaProject(IBundleProjectDescription description, IBundleProjectDescription before,
            boolean existed) throws CoreException {
        IProject project = description.getProject();
        IJavaProject javaProject = JavaCore.create(project);
        // create source folders as required
        IBundleClasspathEntry[] bces = description.getBundleClasspath();
        if (bces != null && bces.length > 0) {
            for (int i = 0; i < bces.length; i++) {
                IPath folder = bces[i].getSourcePath();
                if (folder != null) {
                    CoreUtility.createFolder(project.getFolder(folder));
                }
            }
        }
        // Set default output folder
        if (description.getDefaultOutputFolder() != null) {
            IPath path = project.getFullPath().append(description.getDefaultOutputFolder());
            javaProject.setOutputLocation(path, null);
        }

        // merge the class path if the project existed before
        IBundleClasspathEntry[] prev = before.getBundleClasspath();
        if (!isEqual(bces, prev)) {
            if (existed) {
                // add entries not already present
                IClasspathEntry[] entries = getSourceFolderEntries(javaProject, description);
                IClasspathEntry[] rawClasspath = javaProject.getRawClasspath();
                List add = new ArrayList();
                for (int i = 0; i < entries.length; i++) {
                    IClasspathEntry entry = entries[i];
                    boolean present = false;
                    for (int j = 0; j < rawClasspath.length; j++) {
                        IClasspathEntry existingEntry = rawClasspath[j];
                        if (existingEntry.getEntryKind() == entry.getEntryKind()) {
                            if (existingEntry.getPath().equals(entry.getPath())) {
                                present = true;
                                break;
                            }
                        }
                    }
                    if (!present) {
                        add.add(entry);
                    }
                }
                // check if the 'required plug-ins' container is present
                boolean addRequired = false;
                if (description.hasNature(IBundleProjectDescription.PLUGIN_NATURE)) {
                    addRequired = true;
                    for (int i = 0; i < rawClasspath.length; i++) {
                        IClasspathEntry cpe = rawClasspath[i];
                        if (cpe.getEntryKind() == IClasspathEntry.CPE_CONTAINER) {
                            if (MDECore.REQUIRED_PLUGINS_CONTAINER_PATH.equals(cpe.getPath())) {
                                addRequired = false;
                                break;
                            }
                        }
                    }
                }
                if (addRequired) {
                    add.add(ClasspathComputer.createContainerEntry());
                }
                if (!add.isEmpty()) {
                    List all = new ArrayList();
                    for (int i = 0; i < rawClasspath.length; i++) {
                        all.add(rawClasspath[i]);
                    }
                    all.addAll(add);
                    javaProject.setRawClasspath((IClasspathEntry[]) all.toArray(new IClasspathEntry[all.size()]),
                            null);
                }
            } else {
                IClasspathEntry[] entries = getClassPathEntries(javaProject, description);
                javaProject.setRawClasspath(entries, null);
            }
        }
    }

    /**
     * Returns whether the arrays are equal.
     * 
     * @param array1 an object array or <code>null</code> 
     * @param array2 an object array or <code>null</code>
     * @return whether the arrays are equal
     */
    private boolean isEqual(Object[] array1, Object[] array2) {
        if (array1 == null || array1.length == 0) {
            return array2 == null || array2.length == 0;
        }
        if (array2 == null || array2.length == 0) {
            return false;
        }
        if (array1.length != array2.length) {
            return false;
        }
        for (int i = 0; i < array1.length; i++) {
            if (!array1[i].equals(array2[i])) {
                return false;
            }
        }
        return true;
    }

    /**
     * Returns whether the two objects are equal or both <code>null</code>.
     * 
     * @param st1
     * @param st2
     * @return whether equal or both <code>null</code>
     */
    private boolean isEqual(Object st1, Object st2) {
        if (st1 == null) {
            return st2 == null;
        }
        if (st2 == null) {
            return false;
        }
        return st1.equals(st2);
    }

    /**
     * Returns the class path entries to create for the given project.
     * 
     * @param project Java project
     * @param description project description
     * @return class path entries
     * @throws CoreException if class path creation fails
     */
    private IClasspathEntry[] getClassPathEntries(IJavaProject project, IBundleProjectDescription description)
            throws CoreException {
        IClasspathEntry[] internalClassPathEntries = getSourceFolderEntries(project, description);
        IClasspathEntry[] entries = new IClasspathEntry[internalClassPathEntries.length + 2];
        System.arraycopy(internalClassPathEntries, 0, entries, 2, internalClassPathEntries.length);

        String[] ids = description.getExecutionEnvironments();
        String executionEnvironment = null;
        if (ids != null && ids.length > 0) {
            executionEnvironment = ids[0];
        }
        ClasspathComputer.setComplianceOptions(project, executionEnvironment);
        entries[0] = ClasspathComputer.createJREEntry(executionEnvironment);
        entries[1] = ClasspathComputer.createContainerEntry();
        return entries;
    }

    /**
     * Returns source folder class path entries.
     * 
     * @param project Java project
     * @param description project description
     * @return source folder class path entries, possibly empty.
     * @exception CoreException if source folder class path entry creation fails
     */
    private IClasspathEntry[] getSourceFolderEntries(IJavaProject project, IBundleProjectDescription description)
            throws CoreException {
        IBundleClasspathEntry[] folders = description.getBundleClasspath();
        if (folders == null || folders.length == 0) {
            return new IClasspathEntry[0];
        }
        List entries = new ArrayList();
        for (int i = 0; i < folders.length; i++) {
            IBundleClasspathEntry folder = folders[i];
            if (folder.getSourcePath() == null) {
                // no source indicates class file folder or library
                IPath bin = folder.getBinaryPath();
                if (bin == null) {
                    // nested library
                    bin = folder.getLibrary();
                }
                if (bin != null) {
                    IPath output = project.getProject().getFullPath().append(bin);
                    entries.add(JavaCore.newLibraryEntry(output, null, null));
                }
            } else {
                // source folder
                IPath path = project.getProject().getFullPath().append(folder.getSourcePath());
                IPath output = folder.getBinaryPath();
                if (output != null) {
                    output = project.getProject().getFullPath().append(output);
                }
                entries.add(JavaCore.newSourceEntry(path, EXCLUDE_NONE, output));
            }
        }
        return (IClasspathEntry[]) entries.toArray(new IClasspathEntry[entries.size()]);
    }

    /**
     * Create and open the described project, as necessary.
     * 
     * @param description project description
     * @exception CoreException on failure
     */
    private void createProject(IBundleProjectDescription description) throws CoreException {
        IProject project = description.getProject();
        if (!project.exists()) {
            IProjectDescription pd = project.getWorkspace().newProjectDescription(project.getName());
            pd.setLocationURI(description.getLocationURI());
            project.create(pd, null);
        }
        if (!project.isOpen()) {
            project.open(null);
        }
    }

    /**
     * Configures project natures.
     * 
     * @param description description of project to modify
     * @exception CoreException if something goes wrong
     */
    private void configureNatures(IBundleProjectDescription description) throws CoreException {
        IProject project = description.getProject();
        IProjectDescription projectDescription = project.getDescription();
        String[] curr = projectDescription.getNatureIds();
        Set before = new HashSet();
        for (int i = 0; i < curr.length; i++) {
            before.add(curr[i]);
        }
        String[] natureIds = description.getNatureIds();
        Set after = new HashSet();
        for (int i = 0; i < natureIds.length; i++) {
            after.add(natureIds[i]);
        }
        if (!before.equals(after)) {
            projectDescription.setNatureIds(natureIds);
            project.setDescription(projectDescription, null);
        }
    }

    /**
     * Configures the MANIFEST.MF for the given description.
     * 
     * @param description project description
     * @param before description before operation began
     * @throws CoreException
     */
    private void configureManifest(IBundleProjectDescription description, IBundleProjectDescription before)
            throws CoreException {
        IProject project = description.getProject();
        IFile manifest = PDEProject.getManifest(project);
        if (description.getHost() == null) {
            fModel = new WorkspaceBundlePluginModel(manifest, PDEProject.getPluginXml(project));
        } else {
            fModel = new WorkspaceBundleFragmentModel(manifest, PDEProject.getFragmentXml(project));
        }

        IMonitorBase pluginBase = fModel.getMonitorBase();

        // target version
        String targetVersion = getTargetVersion(description.getTargetVersion());
        if (!isEqual(targetVersion, getTargetVersion(before.getTargetVersion()))) {
            String schemaVersion = TargetPlatformHelper.getSchemaVersionForTargetVersion(targetVersion);
            pluginBase.setSchemaVersion(schemaVersion);
        }

        // symbolic name
        if (!isEqual(description.getSymbolicName(), before.getSymbolicName())) {
            pluginBase.setId(description.getSymbolicName());
        }

        // bundle version
        if (!isEqual(description.getBundleVersion(), before.getBundleVersion())) {
            pluginBase.setVersion(description.getBundleVersion().toString());
        }

        // bundle name
        String bundleName = description.getBundleName();
        if (bundleName == null) {
            // for new projects, bundle name must be specified
            bundleName = description.getSymbolicName();
        }
        if (!isEqual(bundleName, before.getBundleName())) {
            pluginBase.setName(bundleName);
        }

        // bundle vendor
        if (!isEqual(description.getBundleVendor(), before.getBundleVendor())) {
            pluginBase.setProviderName(description.getBundleVendor());
        }

        // manifest version
        if (fModel instanceof IBundlePluginModelBase) {
            IBundlePluginModelBase bmodel = ((IBundlePluginModelBase) fModel);
            // Target version is not persisted and does not make the model dirty.
            // It is only used by the new project wizard to determine which templates are available.
            ((IBundlePluginBase) bmodel.getMonitorBase()).setTargetVersion(targetVersion);
            String ver = bmodel.getBundleModel().getBundle().getHeader(Constants.BUNDLE_MANIFESTVERSION);
            if (!isEqual("2", ver)) { //$NON-NLS-1$
                bmodel.getBundleModel().getBundle().setHeader(Constants.BUNDLE_MANIFESTVERSION, "2"); //$NON-NLS-1$
            }
        }
        if (pluginBase instanceof IFragment) {
            // host specification
            IFragment fragment = (IFragment) pluginBase;
            IHostDescription host = description.getHost();
            if (!isEqual(host, before.getHost())) {
                fragment.setPluginId(host.getName());
                if (host.getVersionRange() != null) {
                    fragment.setPluginVersion(host.getVersionRange().toString());
                } else {
                    // must explicitly set to null, else it appears as 0.0.0
                    fragment.setPluginVersion(null);
                }
            }
        } else {
            // bundle activator class
            String activator = description.getActivator();
            if (!isEqual(activator, before.getActivator())) {
                ((IMonitor) pluginBase).setClassName(activator);
            }
        }
        // bundle classpath
        configureBundleClasspath(description, before);

        // required bundles
        IRequiredBundleDescription[] dependencies = description.getRequiredBundles();
        if (!isEqual(dependencies, before.getRequiredBundles())) {
            // remove all existing imports, then add new ones
            IMonitorImport[] imports = pluginBase.getImports();
            if (imports != null && imports.length > 0) {
                for (int i = 0; i < imports.length; i++) {
                    pluginBase.remove(imports[i]);
                }
            }
            if (dependencies != null) {
                for (int i = 0; i < dependencies.length; i++) {
                    IRequiredBundleDescription req = dependencies[i];
                    VersionRange range = req.getVersionRange();
                    IMonitorImport iimport = fModel.getMonitorFactory().createImport();
                    iimport.setId(req.getName());
                    if (range != null) {
                        iimport.setVersion(range.toString());
                        iimport.setMatch(IMatchRules.COMPATIBLE);
                    }
                    iimport.setReexported(req.isExported());
                    iimport.setOptional(req.isOptional());
                    pluginBase.add(iimport);
                }
            }
        }
        // add Bundle Specific fields if applicable
        if (pluginBase instanceof BundlePluginBase) {
            IBundle bundle = ((BundlePluginBase) pluginBase).getBundle();
            BundleModelFactory factory = new BundleModelFactory(bundle.getModel());
            // Remove host specification if no longer a fragment
            if (before.getHost() != null && description.getHost() == null) {
                bundle.setHeader(Constants.FRAGMENT_HOST, null);
            }
            // Singleton
            if (description.isSingleton() != before.isSingleton()) {
                if (description.isSingleton()) {
                    IManifestHeader header = factory.createHeader(Constants.BUNDLE_SYMBOLICNAME,
                            description.getSymbolicName());
                    if (header instanceof BundleSymbolicNameHeader) {
                        ((BundleSymbolicNameHeader) header).setSingleton(description.isSingleton());
                        bundle.setHeader(Constants.BUNDLE_SYMBOLICNAME, header.getValue());
                    }
                }
            }
            // Set required EEs
            String[] ees = description.getExecutionEnvironments();
            if (!isEqual(ees, before.getExecutionEnvironments())) {
                if (ees == null) {
                    bundle.setHeader(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT, null); // remove
                } else {
                    StringBuffer buffer = new StringBuffer();
                    for (int i = 0; i < ees.length; i++) {
                        String id = ees[i];
                        if (buffer.length() > 0) {
                            buffer.append(",\n "); //comma, new-line, space //$NON-NLS-1$
                        }
                        buffer.append(id);
                    }
                    bundle.setHeader(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT, buffer.toString());
                }
            }
            // package imports
            IPackageImportDescription[] packages = description.getPackageImports();
            if (!isEqual(packages, before.getPackageImports())) {
                if (packages == null) {
                    bundle.setHeader(Constants.IMPORT_PACKAGE, null); // remove
                } else {
                    ImportPackageHeader header = (ImportPackageHeader) factory
                            .createHeader(Constants.IMPORT_PACKAGE, ""); //$NON-NLS-1$
                    for (int i = 0; i < packages.length; i++) {
                        IPackageImportDescription pkg = packages[i];
                        ImportPackageObject ip = header.addPackage(pkg.getName());
                        VersionRange range = pkg.getVersionRange();
                        if (range != null) {
                            ip.setVersion(range.toString());
                        }
                        ip.setOptional(pkg.isOptional());
                    }
                    header.update();
                    bundle.setHeader(Constants.IMPORT_PACKAGE, header.getValue());
                }
            }
            // package exports
            IPackageExportDescription[] exports = description.getPackageExports();
            if (!isEqual(exports, before.getPackageExports())) {
                if (exports == null) {
                    bundle.setHeader(Constants.EXPORT_PACKAGE, null); // remove
                } else {
                    ExportPackageHeader header = (ExportPackageHeader) factory
                            .createHeader(Constants.EXPORT_PACKAGE, ""); //$NON-NLS-1$               
                    for (int i = 0; i < exports.length; i++) {
                        IPackageExportDescription pkg = exports[i];
                        ExportPackageObject epo = header.addPackage(pkg.getName());
                        Version version = pkg.getVersion();
                        if (version != null) {
                            epo.setVersion(version.toString());
                        }
                        String[] friends = pkg.getFriends();
                        if (friends != null) {
                            for (int j = 0; j < friends.length; j++) {
                                epo.addFriend(new PackageFriend(epo, friends[j]));
                            }
                        } else {
                            epo.setInternal(!pkg.isApi());
                        }
                    }
                    header.update();
                    bundle.setHeader(Constants.EXPORT_PACKAGE, header.getValue());
                }
            }
            // Activation policy
            boolean removeActivation = false;
            if (!isEqual(description.getActivationPolicy(), before.getActivationPolicy())) {
                if (Constants.ACTIVATION_LAZY.equals(description.getActivationPolicy())) {
                    if (description.isEquinox()) {
                        if (targetVersion.equals(IBundleProjectDescription.VERSION_3_1))
                            bundle.setHeader(ICoreConstants.ECLIPSE_AUTOSTART, "true"); //$NON-NLS-1$
                        else {
                            double version = Double.parseDouble(targetVersion);
                            if (version >= 3.4) {
                                // use OSGi R4.1 header
                                bundle.setHeader(Constants.BUNDLE_ACTIVATIONPOLICY, Constants.ACTIVATION_LAZY);
                            } else {
                                bundle.setHeader(ICoreConstants.ECLIPSE_LAZYSTART, "true"); //$NON-NLS-1$
                            }
                        }
                    } else {
                        // use OSGi R4.1 header
                        bundle.setHeader(Constants.BUNDLE_ACTIVATIONPOLICY, Constants.ACTIVATION_LAZY);
                    }
                } else { // remove activation policy headers
                    removeActivation = true;
                }
            }
            if (description.getHost() != null && before.getHost() == null) {
                // remove activation policy if becoming a fragment
                removeActivation = true;
            }
            if (removeActivation) {
                bundle.setHeader(ICoreConstants.ECLIPSE_AUTOSTART, null);
                bundle.setHeader(Constants.BUNDLE_ACTIVATIONPOLICY, null);
                bundle.setHeader(ICoreConstants.ECLIPSE_LAZYSTART, null);
            }
            // Localization
            IPath localization = description.getLocalization();
            if (!isEqual(localization, before.getLocalization())) {
                if (localization == null) {
                    bundle.setHeader(Constants.BUNDLE_LOCALIZATION, null);
                } else {
                    bundle.setHeader(Constants.BUNDLE_LOCALIZATION, localization.toString());
                }
            }
            // if the bundle model has been made dirty, ensure that propagates back to the root model
            IBundleModel bundleModel = bundle.getModel();
            if (bundleModel instanceof WorkspaceBundleModel) {
                WorkspaceBundleModel wbm = (WorkspaceBundleModel) bundleModel;
                if (wbm.isDirty()) {
                    fModel.setDirty(true);
                }
            }
            // apply any other headers that have been specified
            BundleProjectDescription bpd = (BundleProjectDescription) description;
            Map extraHeaders = bpd.getExtraHeaders();
            Iterator iterator = extraHeaders.entrySet().iterator();
            while (iterator.hasNext()) {
                Entry entry = (Entry) iterator.next();
                String name = (String) entry.getKey();
                String value = (String) entry.getValue();
                // translate empty header to a single space to ensure inclusion of empty headers
                if (value != null && value.trim().length() == 0) {
                    value = " "; //$NON-NLS-1$
                }
                if (!isEqual(value, bundle.getHeader(name))) {
                    bundle.setHeader(name, value);
                }
            }
        }
    }

    /**
     * Returns the specified target version or the latest version when <code>null</code>.
     * @param targetVersion version or <code>null</code>
     * @return non-null target version
     */
    private String getTargetVersion(String targetVersion) {
        if (targetVersion == null) {
            return TargetPlatformHelper.getTargetVersionString();
        }
        return targetVersion;
    }

    /**
     * Returns the names of libraries as they should appear on the Bundle-Classpath header
     * or <code>null</code> if none.
     * 
     * @param description project description
     * @return library names in the order they should appear or <code>null</code>
     */
    protected static String[] getLibraryNames(IBundleProjectDescription description) {
        // collect unique entries
        IBundleClasspathEntry[] libs = description.getBundleClasspath();
        if (libs != null && libs.length > 0) {
            Set names = new LinkedHashSet();
            for (int i = 0; i < libs.length; i++) {
                IPath lib = libs[i].getLibrary();
                String libName = "."; //$NON-NLS-1$
                if (lib != null) {
                    libName = lib.toString();
                }
                names.add(libName);
            }
            return (String[]) names.toArray(new String[names.size()]);
        }
        return null;
    }

    /**
     * Sets the bundle class path entries in the manifest. If there is only 
     * one entry with the default name '.', it is not added to the Bundle-Classpath
     * header.
     * 
     * @param description project description
     * @param before original state of project
     * @throws CoreException
     */
    private void configureBundleClasspath(IBundleProjectDescription description, IBundleProjectDescription before)
            throws CoreException {
        IBundleClasspathEntry[] cp = description.getBundleClasspath();
        if (!isEqual(cp, before.getBundleClasspath())) {
            // remove all libraries and start again
            IMonitorBase pluginBase = fModel.getMonitorBase();
            IMonitorLibrary[] libraries = pluginBase.getLibraries();
            if (libraries != null && libraries.length > 0) {
                for (int i = 0; i < libraries.length; i++) {
                    pluginBase.remove(libraries[i]);
                }
            }
            String[] names = getLibraryNames(description);
            if (names != null) {
                if (names.length == 1 && ".".equals(names[0])) { //$NON-NLS-1$
                    return; // default library does not need to be added
                }
                for (int i = 0; i < names.length; i++) {
                    IMonitorLibrary library = fModel.getMonitorFactory().createLibrary();
                    library.setName(names[i]);
                    library.setExported(false);
                    pluginBase.add(library);
                }
            }
        }
    }

    private void configureBuildPropertiesFile(IBundleProjectDescription description,
            IBundleProjectDescription before) throws CoreException {
        IProject project = description.getProject();
        IFile file = PDEProject.getBuildProperties(project);
        WorkspaceBuildModel model = new WorkspaceBuildModel(file);
        IBuildModelFactory factory = model.getFactory();

        // BIN.INCLUDES
        IBuildEntry binEntry = model.getBuild().getEntry(IBuildEntry.BIN_INCLUDES);
        if (binEntry == null) {
            binEntry = factory.createEntry(IBuildEntry.BIN_INCLUDES);
            model.getBuild().add(binEntry);
        }
        boolean modified = fillBinIncludes(project, binEntry, description, before);
        modified = createSourceOutputBuildEntries(model, factory, description, before) | modified;
        if (modified) {
            model.save();
        }
    }

    /**
     * Configures the bin.includes entry based on the included libraries and explicit entries to add.
     * 
     * @param project
     * @param binEntry
     * @param description
     * @param before
     * @return whether the entry was modified
     * @throws CoreException
     */
    private boolean fillBinIncludes(IProject project, IBuildEntry binEntry, IBundleProjectDescription description,
            IBundleProjectDescription before) throws CoreException {
        boolean modified = false;
        if (!binEntry.contains("META-INF/")) { //$NON-NLS-1$
            modified = true;
            binEntry.addToken("META-INF/"); //$NON-NLS-1$
        }
        // add bundle class path entries
        String[] names = getLibraryNames(description);
        String[] prevNames = getLibraryNames(before);
        if (!isEqual(names, prevNames)) {
            // remove old libraries
            if (prevNames != null) {
                for (int i = 0; i < prevNames.length; i++) {
                    if (binEntry.contains(prevNames[i])) {
                        modified = true;
                        binEntry.removeToken(prevNames[i]);
                    }
                }
            }
            // add new libraries
            if (names != null) {
                for (int i = 0; i < names.length; i++) {
                    if (!binEntry.contains(names[i])) {
                        modified = true;
                        // folders need trailing slash - see bug 306991
                        String name = names[i];
                        IPath path = new Path(names[i]);
                        String extension = path.getFileExtension();
                        if (extension == null) {
                            if (!name.endsWith("/")) { //$NON-NLS-1$
                                name = name + "/"; //$NON-NLS-1$
                            }
                        }
                        binEntry.addToken(name);
                    }
                }
            }
        }

        // extra files be added to build.properties
        IPath[] paths = description.getBinIncludes();
        IPath[] prevPaths = before.getBinIncludes();
        if (!isEqual(paths, prevPaths)) {
            // remove old paths
            if (prevPaths != null) {
                for (int i = 0; i < prevPaths.length; i++) {
                    String token = prevPaths[i].toString();
                    if (binEntry.contains(token)) {
                        binEntry.removeToken(token);
                        modified = true;
                    }
                }
            }
            // add new paths
            if (paths != null) {
                for (int i = 0; i < paths.length; i++) {
                    String name = paths[i].toString();
                    if (!binEntry.contains(name)) {
                        binEntry.addToken(name);
                        modified = true;
                    }
                }
            }
        }
        return modified;
    }

    private boolean createSourceOutputBuildEntries(WorkspaceBuildModel model, IBuildModelFactory factory,
            IBundleProjectDescription description, IBundleProjectDescription before) throws CoreException {
        boolean modified = false;
        IBundleClasspathEntry[] folders = description.getBundleClasspath();
        IBundleClasspathEntry[] prev = before.getBundleClasspath();
        if (!isEqual(folders, prev)) {
            modified = true;
            // remove the old ones
            String[] oldNames = getLibraryNames(before);
            IBuild build = model.getBuild();
            if (oldNames != null) {
                for (int i = 0; i < oldNames.length; i++) {
                    removeBuildEntry(build, IBuildEntry.JAR_PREFIX + oldNames[i]);
                    removeBuildEntry(build, IBuildEntry.OUTPUT_PREFIX + oldNames[i]);
                }
            }
            // configure the new ones
            if (folders != null && folders.length > 0) {
                for (int i = 0; i < folders.length; i++) {
                    String libraryName = null;
                    IPath libPath = folders[i].getLibrary();
                    if (libPath == null) {
                        libraryName = "."; //$NON-NLS-1$
                    } else {
                        libraryName = folders[i].getLibrary().toString();
                    }

                    // SOURCE.<LIBRARY_NAME>
                    IPath srcFolder = folders[i].getSourcePath();
                    if (srcFolder != null) {
                        IBuildEntry entry = getBuildEntry(build, factory, IBuildEntry.JAR_PREFIX + libraryName);
                        if (!srcFolder.isEmpty())
                            entry.addToken(srcFolder.addTrailingSeparator().toString());
                        else
                            entry.addToken("."); //$NON-NLS-1$
                    }

                    // OUTPUT.<LIBRARY_NAME>
                    IPath outFolder = folders[i].getBinaryPath();
                    if (srcFolder != null && outFolder == null) {
                        // default output folder
                        IJavaProject project = JavaCore.create(description.getProject());
                        outFolder = project.getOutputLocation().removeFirstSegments(1);
                    }
                    if (outFolder != null) {
                        IBuildEntry entry = getBuildEntry(build, factory, IBuildEntry.OUTPUT_PREFIX + libraryName);
                        String token = null;
                        if (!outFolder.isEmpty())
                            token = outFolder.addTrailingSeparator().toString();
                        else
                            token = "."; //$NON-NLS-1$
                        if (!entry.contains(token)) {
                            entry.addToken(token);
                        }
                    }

                }
            }
        }
        return modified;
    }

    /**
     * Gets the specified build entry, creating and adding it if not already present.
     * 
     * @param build build
     * @param factory factory to create new entries
     * @param key the entry to create
     * @return build entry
     * @exception CoreException if unable to add the build entry
     */
    private IBuildEntry getBuildEntry(IBuild build, IBuildModelFactory factory, String key) throws CoreException {
        IBuildEntry entry = build.getEntry(key);
        if (entry == null) {
            entry = factory.createEntry(key);
            build.add(entry);
        }
        return entry;
    }

    /**
     * Remove a build entry from the model.
     * 
     * @param build
     * @param key
     * @throws CoreException 
     */
    private void removeBuildEntry(IBuild build, String key) throws CoreException {
        IBuildEntry entry = build.getEntry(key);
        if (entry != null) {
            build.remove(entry);
        }
    }
}