blob: 86c119bf956471e73177f143e62c68e9f60f579e [file] [log] [blame]
package org.eclipse.scout.sdk.util.pde;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.adaptor.EclipseStarter;
import org.eclipse.osgi.service.resolver.BundleDescription;
import org.eclipse.osgi.service.resolver.State;
import org.eclipse.pde.core.plugin.IPluginModelBase;
import org.eclipse.pde.core.plugin.PluginRegistry;
import org.eclipse.pde.internal.core.PDECore;
import org.eclipse.pde.internal.core.TargetPlatformHelper;
import org.eclipse.pde.internal.core.ifeature.IFeatureModel;
import org.eclipse.pde.internal.core.ifeature.IFeaturePlugin;
import org.eclipse.pde.internal.core.iproduct.IProduct;
import org.eclipse.pde.internal.core.iproduct.IProductFeature;
import org.eclipse.pde.internal.core.iproduct.IProductModel;
import org.eclipse.pde.internal.core.iproduct.IProductPlugin;
import org.eclipse.pde.internal.core.product.ProductFeature;
import org.eclipse.pde.internal.core.product.ProductPlugin;
import org.osgi.framework.Version;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
* Helper class for basic product file operations.
*
* @author Matthias Villiger
* @since 3.8.0
*/
@SuppressWarnings("restriction")
public final class ProductFileModelHelper {
private final LazyProductFileModel m_model;
/**
* Class for operations executed on the configuration file of the product.
*/
public final ConfigurationFilePart ConfigurationFile;
/**
* Class for operations executed on the product file.
*/
public final ProductFilePart ProductFile;
/**
* Creates a new helper that operates on the given product file.
*
* @param project
* The project in which the given path should be used.
* @param projRelPathToProduct
* The path relative to the given project root pointing to the product file
* @throws CoreException
* @throws IllegalArgumentException
* when the given path is not valid or the file does not exist
*/
public ProductFileModelHelper(IProject project, IPath projRelPathToProduct) throws CoreException {
this(project.getFile(projRelPathToProduct));
}
/**
* Creates a new helper that operates on the given product file.
*
* @param project
* The project in which the given path should be used.
* @param projRelPathToProduct
* The path relative to the given project root pointing to the product file
* @throws CoreException
* @throws IllegalArgumentException
* when the given path is not valid or the file does not exist
*/
public ProductFileModelHelper(IProject project, String projRelPathToProduct) throws CoreException {
this(project.getFile(projRelPathToProduct));
}
/**
* Creates a new helper that operates on the given product file.
*
* @param productFile
* The product file
* @throws CoreException
* @throws IllegalArgumentException
* when the given path is not valid or the file does not exist
*/
public ProductFileModelHelper(IFile productFile) throws CoreException {
m_model = new LazyProductFileModel(productFile);
ConfigurationFile = new ConfigurationFilePart(m_model);
ProductFile = new ProductFilePart(m_model);
}
/**
* <h3>{@link DependencyType}</h3>Specifies the type of dependency in a .product file.
*
* @author Matthias Villiger
* @since 3.10.0 19.09.2013
*/
public static enum DependencyType {
/**
* Specifies a normal plug-in dependency.
*/
Plugin,
/**
* Specifies a fragment dependency.
*/
Fragment,
/**
* Specifies a dependency to a feature.
*/
Feature
}
public static class ProductFilePart {
private final LazyProductFileModel m_model;
private ProductFilePart(LazyProductFileModel model) {
m_model = model;
}
/**
* Adds the given plug-in id to the dependencies of this product.<br>
* If the given plug-in is already in the list, this method does nothing.
*
* @param pluginId
* The plug-in id.
* @throws CoreException
*/
public void addDependency(String pluginId) throws CoreException {
addDependency(pluginId, DependencyType.Plugin);
}
/**
* Adds the given id to the dependencies of this product.<br>
* If the given dependency already exists, this method does nothing.
*
* @param id
* The id to add (feature id, fragment or plug-in symbolic name).
* @param type
* specifies the type of dependency.
* @throws CoreException
* @see DependencyType
*/
public synchronized void addDependency(String id, DependencyType type) throws CoreException {
if (!existsDependency(id)) {
if (DependencyType.Feature.equals(type)) {
ProductFeature pf = createFeature(id);
m_model.getWorkspaceProductModel().getProduct().addFeatures(new IProductFeature[]{pf});
}
else {
P_ProductPlugin newPlugin = createPlugin(id, DependencyType.Fragment.equals(type));
m_model.getWorkspaceProductModel().getProduct().addPlugins(new IProductPlugin[]{newPlugin});
}
}
}
private ProductFeature createFeature(String id) throws CoreException {
ProductFeature pf = new ProductFeature(m_model.getWorkspaceProductModel());
pf.setId(id);
return pf;
}
/**
* TODO can be eliminated when Eclipse 3.8 is the oldest version that the SDK supports (the bug was solved for
* Eclipse 3.8. Must be verified though).<br>
* Then use m_model.getWorkspaceProductModel().getFactory().createPlugin() to create instances.
*
* @see <a href="http://bugs.eclipse.org/bugs/show_bug.cgi?id=362398">Bugzilla #362398</a>
*/
private P_ProductPlugin createPlugin(String pluginId, boolean isFragment) throws CoreException {
return new P_ProductPlugin(m_model.getWorkspaceProductModel(), pluginId, isFragment);
}
/**
* Checks if the given id is already in the dependencies of this product.<br>
* The id can either be a feature id (for feature based products) or a bundle symbolic name (for plug-in based
* products). If a plug-in id is given, this plug-in is also searched within the features of feature based products.
*
* @param pluginOrFeatureId
* The feature id or plug-in symbolic name
* @return true if the given dependency is already in the product.
* @throws CoreException
*/
public boolean existsDependency(String pluginOrFeatureId) throws CoreException {
// search in plug-ins
for (IProductPlugin p : m_model.getWorkspaceProductModel().getProduct().getPlugins()) {
if (p.getId().equals(pluginOrFeatureId)) {
return true;
}
}
IProductFeature[] features = m_model.getWorkspaceProductModel().getProduct().getFeatures();
if (features != null && features.length > 0) {
// search directly in features
for (IProductFeature feature : features) {
if (feature.getId().equals(pluginOrFeatureId)) {
return true;
}
}
// search plug-ins within features
Map<String, ? extends Set<String>> allBundlesInFeatures = getBundlesInFeatures();
for (IProductFeature f : features) {
Set<String> bundlesInCurrentFeature = allBundlesInFeatures.get(f.getId());
if (bundlesInCurrentFeature != null && bundlesInCurrentFeature.contains(pluginOrFeatureId)) {
return true;
}
}
}
return false;
}
private Map<String /*feature id*/, ? extends Set<String /*bundle symbolic name*/>> getBundlesInFeatures() {
IFeatureModel[] features = PDECore.getDefault().getFeatureModelManager().getModels();
HashMap<String, HashSet<String>> allBundlesInFeatures = new HashMap<String, HashSet<String>>(features.length);
for (IFeatureModel feature : features) {
for (IFeaturePlugin bundle : feature.getFeature().getPlugins()) {
HashSet<String> bundlesInFeature = allBundlesInFeatures.get(feature.getFeature().getId());
if (bundlesInFeature == null) {
bundlesInFeature = new HashSet<String>();
allBundlesInFeatures.put(feature.getFeature().getId(), bundlesInFeature);
}
bundlesInFeature.add(bundle.getId());
}
}
return allBundlesInFeatures;
}
/**
* Removes the given dependency (feature id or plug-in symbolic name) from the dependency list of this product.
*
* @param id
* The feature id or plug-in symbolic name.
* @throws CoreException
*/
public void removeDependency(String id) throws CoreException {
m_model.getWorkspaceProductModel().getProduct().removePlugins(new IProductPlugin[]{createPlugin(id, false)});
m_model.getWorkspaceProductModel().getProduct().removeFeatures(new IProductFeature[]{createFeature(id)});
}
/**
* Checks if the product is valid or not.
*
* @return true if the product is valid, false otherwise.
* @throws CoreException
*/
public boolean isValid() throws CoreException {
return m_model.getWorkspaceProductModel().isValid();
}
/**
* Gets the plug-in models of all plug-ins of the product associated with this helper.<br>
* <br>
* Plug-ins that exist in the product file but cannot be found in the current target platform
* (marked red in the product file editor) will not be part of the result.<br>
* Use {@link ProductFilePart#getPluginSymbolicNames()} to get the names of all plugins referenced in the product
* file independent if they can be found or not.<br>
* <br>
* If the product file contains version constraints for the plug-ins, only models that fulfill the version
* constraints are returned.<br>
* <br>
* If the product is feature based, this method returns an empty array.
*
* @return the plug-in models of all plug-ins this product is dependent of.
* @throws CoreException
*/
public BundleDescription[] getPluginModels() throws CoreException {
ArrayList<BundleDescription> list = new ArrayList<BundleDescription>();
State state = TargetPlatformHelper.getState();
IProductPlugin[] plugins = m_model.getWorkspaceProductModel().getProduct().getPlugins();
for (int i = 0; i < plugins.length; i++) {
BundleDescription bundle = null;
String v = plugins[i].getVersion();
if (v != null && v.length() > 0) {
bundle = state.getBundle(plugins[i].getId(), Version.parseVersion(v));
}
if (bundle == null) {
bundle = state.getBundle(plugins[i].getId(), null);
}
if (bundle != null) {
list.add(bundle);
}
}
return list.toArray(new BundleDescription[list.size()]);
}
/**
* Gets the symbolic names of all plug-ins of the product associated with this helper.<br>
* If the product is feature based, this method returns an empty array.
*
* @return The symbolic names of all plug-ins in the product.
* @throws CoreException
*/
public String[] getPluginSymbolicNames() throws CoreException {
IProductPlugin[] plugins = m_model.getWorkspaceProductModel().getProduct().getPlugins();
String[] result = new String[plugins.length];
for (int i = 0; i < result.length; i++) {
result[i] = plugins[i].getId();
}
return result;
}
/**
* Gets the {@link IProduct} model for this product.
*
* @return the product model.
* @throws CoreException
*/
public IProduct getProduct() throws CoreException {
return m_model.getWorkspaceProductModel().getProduct();
}
}
/**
* saves all changes of all underlying models that require saving.
*
* @throws CoreException
*/
public void save() throws CoreException {
m_model.save();
}
public static class ConfigurationFilePart {
private final LazyProductFileModel m_model;
private ConfigurationFilePart(LazyProductFileModel model) {
m_model = model;
}
/**
* Gets the value of the config file entry with the given key.
*
* @param key
* The key to search.
* @return The value of the given key in the config file (or null if it does not exist).
* @throws CoreException
*/
public String getEntry(String key) throws CoreException {
return m_model.getConfigFileProperties().getProperty(key);
}
/**
* Checks if the given key exists in the config file.
*
* @param key
* The key to search.
* @return true if the key exists, false otherwise.
* @throws CoreException
*/
public boolean existsEntry(String key) throws CoreException {
return m_model.getConfigFileProperties().containsProperty(key);
}
/**
* Sets the value of the entry with the given key.<br>
* If the key does not exist, it is created.
*
* @param key
* The key to create or update.
* @param value
* The value associated with the given key.
* @throws CoreException
*/
public void setEntry(String key, String value) throws CoreException {
m_model.getConfigFileProperties().setProperty(key, value);
}
/**
* Removes the given key from the configuration file.
*
* @param key
* The key to remove
* @throws CoreException
*/
public void removeEntry(String key) throws CoreException {
m_model.getConfigFileProperties().removeProperty(key);
}
/**
* Gets the value of the "osgi.bundles" entry of the config file.
*
* @return The value of the "osgi.bundles" entry.
* @throws CoreException
*/
public String getOsgiBundlesEntry() throws CoreException {
return getEntry(EclipseStarter.PROP_BUNDLES);
}
/**
* Sets the value of the "osgi.bundles" entry of the config file.
*
* @param value
* The new value of the entry.
* @throws CoreException
*/
public void setOsgiBundlesEntry(String value) throws CoreException {
setEntry(EclipseStarter.PROP_BUNDLES, value);
}
/**
* Gets the configuration file of this product.
*
* @return The config file or null if this product has no config file specified.
* @throws CoreException
*/
public IFile getFile() throws CoreException {
return m_model.getConfigIniFile();
}
/**
* Gets a map of all key-value-pairs that exist in the config file.<br>
* Changes to the map have no effect to the config file.
*
* @return A map with all key-value-pairs.
* @throws CoreException
*/
public Map<String, String> getEntries() throws CoreException {
return m_model.getConfigFileProperties().getEntries();
}
}
/**
* TODO can be eliminated when Eclipse 3.8 is the oldest version that the SDK supports (the bug was solved for Eclipse
* 3.8. Must be verified though).<br>
* Then use m_model.getWorkspaceProductModel().getFactory().createPlugin() to create instances.
*
* @see <a href="http://bugs.eclipse.org/bugs/show_bug.cgi?id=362398">Bugzilla #362398</a>
*/
private static class P_ProductPlugin extends ProductPlugin {
private static final long serialVersionUID = 1L;
private boolean m_fragment;
private P_ProductPlugin(IProductModel model, String id) {
this(model, id, false);
}
private P_ProductPlugin(IProductModel model, String id, boolean isFragment) {
super(model);
m_fragment = isFragment;
setId(id);
}
@Override
public void parse(Node node) {
super.parse(node);
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) node;
setFragment(Boolean.parseBoolean(element.getAttribute("fragment")));
}
}
@Override
public void write(String indent, PrintWriter writer) {
writer.print(indent + "<plugin id=\"" + getId() + "\""); //$NON-NLS-1$ //$NON-NLS-2$
if (getVersion() != null && getVersion().length() > 0 && !getVersion().equals("0.0.0")) { //$NON-NLS-1$
writer.print(" version=\"" + getVersion() + "\""); //$NON-NLS-1$ //$NON-NLS-2$
}
IPluginModelBase modBase = PluginRegistry.findModel(getId());
if (isFragment() || (modBase != null && modBase.isFragmentModel())) {
writer.print(" fragment=\"true\""); //$NON-NLS-1$
}
writer.println("/>"); //$NON-NLS-1$
}
public boolean isFragment() {
return m_fragment;
}
public void setFragment(boolean isFragment) {
m_fragment = isFragment;
}
}
}