/**********************************************************************
* Copyright (c) 2005-2009 ant4eclipse project team.
*
* 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:
* Nils Hartmann, Daniel Kasmeroglu, Gerd Wuetherich
**********************************************************************/
package org.ant4eclipse.ant.pde;
import java.io.File;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.ant4eclipse.ant.platform.core.GetPathComponent;
import org.ant4eclipse.ant.platform.core.delegate.GetPathDelegate;
import org.ant4eclipse.ant.platform.core.task.AbstractProjectPathTask;
import org.ant4eclipse.lib.core.Assure;
import org.ant4eclipse.lib.core.exception.Ant4EclipseException;
import org.ant4eclipse.lib.core.osgi.BundleLayoutResolver;
import org.ant4eclipse.lib.core.util.Utilities;
import org.ant4eclipse.lib.pde.PdeExceptionCode;
import org.ant4eclipse.lib.pde.internal.tools.BundleDependenciesResolver;
import org.ant4eclipse.lib.pde.internal.tools.BundleDependenciesResolver.BundleDependency;
import org.ant4eclipse.lib.pde.internal.tools.TargetPlatformImpl;
import org.ant4eclipse.lib.pde.internal.tools.UnresolvedBundleException;
import org.ant4eclipse.lib.pde.model.pluginproject.BundleSource;
import org.apache.tools.ant.BuildException;
import org.eclipse.osgi.service.resolver.BundleDescription;
import org.osgi.framework.Version;
/**
* <p>
* The {@link GetRequiredBundlesTask} task can be used to resolve the required bundles for a given set of bundles.
* </p>
*
* @author Gerd Wütherich (gerd@gerd-wuetherich.de)
*/
public class GetRequiredBundlesTask extends AbstractProjectPathTask implements TargetPlatformAwareComponent,
GetPathComponent {
/** the target platform delegate */
private TargetPlatformAwareDelegate _targetPlatformAwareDelegate;
/** */
private GetPathComponent _getPathComponent;
// /** indicates if optional dependencies should be resolved */
// private boolean _includeOptionalDependencies = true;
// /** indicates if the specified bundles should be part of the result */
// private boolean _includeSpecifiedBundles = true;
/** indicates if workspace bundles should be part of the result */
private boolean _includeWorkspaceBundles = true;
/** indicates if the bundle class path should be resolved */
private boolean _resolveBundleClasspath = true;
/** the bundle symbolic name */
private String _bundleSymbolicName;
/** the bundle version */
private String _bundleVersion;
/** the bundle specifications */
private LinkedList<BundleSpecification> _bundleSpecifications;
/** */
private Set<BundleDescription> _resolvedBundleDescriptions;
/**
* <p>
* Creates a new instance of type GetRequiredBundles.
* </p>
*/
public GetRequiredBundlesTask() {
// // create the delegates
this._getPathComponent = new GetPathDelegate(this);
this._targetPlatformAwareDelegate = new TargetPlatformAwareDelegate();
this._bundleSpecifications = new LinkedList<BundleSpecification>();
this._resolvedBundleDescriptions = new HashSet<BundleDescription>();
}
/**
* {@inheritDoc}
*/
public final String getTargetPlatformId() {
return this._targetPlatformAwareDelegate.getTargetPlatformId();
}
/**
* {@inheritDoc}
*/
public final boolean isTargetPlatformIdSet() {
return this._targetPlatformAwareDelegate.isTargetPlatformIdSet();
}
/**
* {@inheritDoc}
*/
public final void requireTargetPlatformIdSet() {
this._targetPlatformAwareDelegate.requireTargetPlatformIdSet();
}
/**
* {@inheritDoc}
*/
public final void setTargetPlatformId(String targetPlatformId) {
this._targetPlatformAwareDelegate.setTargetPlatformId(targetPlatformId);
}
/**
* {@inheritDoc}
*/
public String getPlatformConfigurationId() {
return this._targetPlatformAwareDelegate.getPlatformConfigurationId();
}
/**
* {@inheritDoc}
*/
public boolean isPlatformConfigurationIdSet() {
return this._targetPlatformAwareDelegate.isPlatformConfigurationIdSet();
}
/**
* {@inheritDoc}
*/
public void setPlatformConfigurationId(String platformConfigurationId) {
this._targetPlatformAwareDelegate.setPlatformConfigurationId(platformConfigurationId);
}
/**
* {@inheritDoc}
*/
public String getPathId() {
return this._getPathComponent.getPathId();
}
/**
* {@inheritDoc}
*/
public String getProperty() {
return this._getPathComponent.getProperty();
}
/**
* {@inheritDoc}
*/
public File[] getResolvedPath() {
return this._getPathComponent.getResolvedPath();
}
/**
* {@inheritDoc}
*/
public boolean isPathIdSet() {
return this._getPathComponent.isPathIdSet();
}
/**
* {@inheritDoc}
*/
public boolean isPropertySet() {
return this._getPathComponent.isPropertySet();
}
/**
* {@inheritDoc}
*/
public boolean isRelative() {
return this._getPathComponent.isRelative();
}
/**
* {@inheritDoc}
*/
public void populatePathId() {
this._getPathComponent.populatePathId();
}
/**
* {@inheritDoc}
*/
public void populateProperty() {
this._getPathComponent.populateProperty();
}
/**
* {@inheritDoc}
*/
public void requirePathIdOrPropertySet() {
this._getPathComponent.requirePathIdOrPropertySet();
}
/**
* {@inheritDoc}
*/
public void setPathId(String id) {
this._getPathComponent.setPathId(id);
}
/**
* {@inheritDoc}
*/
public void setProperty(String property) {
this._getPathComponent.setProperty(property);
}
/**
* {@inheritDoc}
*/
public void setRelative(boolean relative) {
this._getPathComponent.setRelative(relative);
}
/**
* {@inheritDoc}
*/
public void setResolvedPath(File[] resolvedPath) {
this._getPathComponent.setResolvedPath(resolvedPath);
}
/**
* <p>
* </p>
*
* @return the includeWorkspaceBundles
*/
public boolean isIncludeWorkspaceBundles() {
return this._includeWorkspaceBundles;
}
/**
* <p>
* </p>
*
* @param includeWorkspaceBundles
* the includeWorkspaceBundles to set
*/
public void setIncludeWorkspaceBundles(boolean includeWorkspaceBundles) {
this._includeWorkspaceBundles = includeWorkspaceBundles;
}
/**
* <p>
* Returns the bundle symbolic name
* </p>
*
* @return the bundle symbolic name
*/
public String getBundleSymbolicName() {
return this._bundleSymbolicName;
}
/**
* <p>
* Sets the bundle symbolic name
* </p>
*
* @param bundleSymbolicName
* the bundleSymbolicName to set
*/
public void setBundleSymbolicName(String bundleSymbolicName) {
this._bundleSymbolicName = bundleSymbolicName;
}
/**
* <p>
* Returns the bundle version
* </p>
*
* @return the bundleVersion
*/
public String getBundleVersion() {
return this._bundleVersion;
}
/**
* <p>
* Sets the bundle version
* </p>
*
* @param bundleVersion
* the bundleVersion to set
*/
public void setBundleVersion(String bundleVersion) {
this._bundleVersion = bundleVersion;
}
/**
* <p>
* Adds the {@link BundleSpecification} to the list of root bundles.
* </p>
*
* @param specification
* the bundle specification
*/
public void addConfiguredBundle(BundleSpecification specification) {
// assert not null
Assure.notNull("specification", specification);
// assert symbolic name is set
if (Utilities.hasText(specification._symbolicName)) {
throw new Ant4EclipseException(PdeExceptionCode.ANT_ATTRIBUTE_NOT_SET, "bundleSymbolicName");
}
// assert valid version
try {
specification.getVersion();
} catch (Exception e) {
throw new Ant4EclipseException(PdeExceptionCode.INVALID_VERSION, "bundleSymbolicName", "bundle");
}
// add specification
this._bundleSpecifications.add(specification);
}
/**
* <p>
* Creates a new {@link BundleSpecification} instance.
* </p>
*
* @return a new {@link BundleSpecification} instance.
*/
public BundleSpecification createBundle() {
return new BundleSpecification();
}
/**
* {@inheritDoc}
*/
@Override
protected void doExecute() {
// step 1: clear the result list
this._resolvedBundleDescriptions.clear();
// step 2: add the bundle specification attribute to the the list of
// (root) bundle specifications
if (Utilities.hasText(this._bundleSymbolicName)) {
this._bundleSpecifications.add(new BundleSpecification(this._bundleSymbolicName, this._bundleVersion));
}
// step 3: resolve required bundles for all bundle specifications
for (BundleSpecification bundleSpecification : this._bundleSpecifications) {
// get the resolved bundle description from the target platform
BundleDescription bundleDescription = this._targetPlatformAwareDelegate.getTargetPlatform(getWorkspace())
.getResolvedBundle(bundleSpecification.getSymbolicName(), bundleSpecification.getVersion());
// if not resolved bundle description is found, throw an exception
if (bundleDescription == null) {
throw new Ant4EclipseException(PdeExceptionCode.SPECIFIED_BUNDLE_NOT_FOUND,
bundleSpecification.getSymbolicName(), bundleSpecification.getVersion());
}
// resolve the required ones
resolveReferencedBundles(bundleDescription);
}
// step 4: resolve the path
List<File> result = new LinkedList<File>();
for (BundleDescription bundleDescription : this._resolvedBundleDescriptions) {
// don't add the bundle if bundle source is an eclipse project and
// _includeWorkspaceBundles == false
BundleSource bundleSource = (BundleSource) bundleDescription.getUserObject();
if (this._includeWorkspaceBundles || !(bundleSource.isEclipseProject())) {
// get the layout resolver
BundleLayoutResolver layoutResolver = BundleDependenciesResolver.getBundleLayoutResolver(bundleDescription);
// add the files
if (this._resolveBundleClasspath) {
File[] files = layoutResolver.resolveBundleClasspathEntries();
result.addAll(Arrays.asList(files));
} else {
result.add(layoutResolver.getLocation());
}
}
}
// set the resolved path
setResolvedPath(result.toArray(new File[0]));
// set the path
if (isPathIdSet()) {
populatePathId();
}
// set the property
if (isPropertySet()) {
populateProperty();
}
}
/**
* <p>
* Resolves the referenced bundles for the given bundle description.
* </p>
*
* @param bundleDescription
* the referenced bundles for the given bundle description.
*/
private void resolveReferencedBundles(BundleDescription bundleDescription) {
// step 1: add the bundle description to the list of resolved descriptions or
// return if the description already has been resolved
// TODO: maybe we have to check if the bundle description has attached fragments (in case it is indirectly
// referenced?)
if (this._resolvedBundleDescriptions.contains(bundleDescription) /* || _excludedBundles.contains(bundleDescription) */) {
return;
} else {
this._resolvedBundleDescriptions.add(bundleDescription);
}
// step 2: resolve bundle dependencies
List<BundleDependency> bundleDependencies = null;
try {
bundleDependencies = new BundleDependenciesResolver().resolveBundleClasspath(bundleDescription);
} catch (UnresolvedBundleException e) {
// throw a BUNDLE_NOT_RESOLVED_EXCEPTION
throw new Ant4EclipseException(PdeExceptionCode.BUNDLE_NOT_RESOLVED_EXCEPTION,
TargetPlatformImpl.dumpResolverErrors(bundleDescription, true));
}
// step 3: resolve the referenced bundles
for (BundleDependency bundleDependency : bundleDependencies) {
// resolve the host
resolveReferencedBundles(bundleDependency.getHost());
// resolve the fragments
for (BundleDescription fragment : bundleDependency.getFragments()) {
resolveReferencedBundles(fragment);
}
}
}
/**
* {@inheritDoc}
*/
@Override
protected void preconditions() throws BuildException {
// require fields
requirePathIdOrPropertySet();
requireTargetPlatformIdSet();
// if attribute 'bundleSymbolicName' is set, no 'bundle' element is
// allowed
if (Utilities.hasText(this._bundleSymbolicName) && !this._bundleSpecifications.isEmpty()) {
throw new Ant4EclipseException(PdeExceptionCode.ANT_ATTRIBUTE_X_OR_ELEMENT_Y, "bundleSymbolicName", "bundle");
}
// if attribute 'bundleVersion' is set, 'bundleSymbolicName' must be
// specified
if (!Utilities.hasText(this._bundleSymbolicName) && Utilities.hasText(this._bundleVersion)) {
throw new Ant4EclipseException(PdeExceptionCode.ANT_ATTRIBUTE_X_WITHOUT_ATTRIBUTE_Y, "bundleVersion",
"bundleSymbolicName");
}
}
/**
* <p>
* Encapsulates a bundle specification.
* </p>
*
* @author Gerd Wütherich (gerd@gerd-wuetherich.de)
*/
public static class BundleSpecification {
/** the symbolicName */
private String _symbolicName;
/** the version */
private String _version;
/**
* <p>
* Creates a new instance of type {@link BundleSpecification}.
* </p>
*/
public BundleSpecification() {
// nothing to do here...
}
/**
* <p>
* Creates a new instance of type {@link BundleSpecification}.
* </p>
*
* @param symbolicName
* the symbolic name
* @param version
* the version
*/
public BundleSpecification(String symbolicName, String version) {
this._symbolicName = symbolicName;
this._version = version;
}
/**
* <p>
* Returns the symbolic name.
* </p>
*
* @return the symbolicName
*/
public String getSymbolicName() {
return this._symbolicName;
}
/**
* <p>
* Sets the symbolic name.
* </p>
*
* @param symbolicName
* the symbolicName to set
*/
public void setSymbolicName(String symbolicName) {
this._symbolicName = symbolicName;
}
/**
* <p>
* Returns the bundle version.
* </p>
*
* @return the version
*/
public Version getVersion() {
return this._version != null ? new Version(this._version) : null;
}
/**
* <p>
* Sets the bundle version.
* </p>
*
* @param version
* the version to set
*/
public void setVersion(String version) {
this._version = version;
}
/**
* <p>
* Returns <code>true</code> if the bundle version is set.
* </p>
*
* @return <code>true</code> if the bundle version is set, <code>false</code> otherwise.
*/
public boolean hasBundleVersion() {
return this._version != null;
}
}
}