/*******************************************************************************
* Copyright (c) 2004, 2006
* Thomas Hallgren, Kenneth Olwing, Mitch Sonies
* Pontus Rydin, Nils Unden, Peer Torngren
* The code, documentation and other materials contained herein have been
* licensed under the Eclipse Public License - v 1.0 by the individual
* copyright holders listed above, as Initial Contributors under such license.
* The text of such license is available at www.eclipse.org.
*******************************************************************************/
package org.eclipse.buckminster.pde.internal.model;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.regex.Pattern;
import org.eclipse.buckminster.pde.Messages;
import org.eclipse.buckminster.pde.PDEPlugin;
import org.eclipse.buckminster.runtime.BuckminsterException;
import org.eclipse.buckminster.runtime.IOUtils;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.osgi.service.resolver.BundleDescription;
import org.eclipse.osgi.service.resolver.HostSpecification;
import org.eclipse.osgi.service.resolver.VersionRange;
import org.eclipse.osgi.util.NLS;
import org.eclipse.pde.core.IEditableModel;
import org.eclipse.pde.core.plugin.IMatchRules;
import org.eclipse.pde.core.plugin.IPluginBase;
import org.eclipse.pde.core.plugin.IPluginImport;
import org.eclipse.pde.core.plugin.IPluginModelBase;
import org.eclipse.pde.core.plugin.PluginRegistry;
import org.eclipse.pde.internal.core.feature.ExternalFeatureModel;
import org.eclipse.pde.internal.core.ifeature.IFeatureImport;
import org.eclipse.pde.internal.core.ifeature.IFeaturePlugin;
import org.eclipse.pde.internal.core.plugin.PluginBase;
import org.eclipse.pde.internal.core.util.VersionUtil;
import org.osgi.framework.Version;
/**
* The Eclipse external model is not editable but this subclass is.
*
* @author Thomas Hallgren
*/
@SuppressWarnings("restriction")
public class EditableFeatureModel extends ExternalFeatureModel implements IEditableModel {
private static final long serialVersionUID = 5818223312516456482L;
public static int getContextQualifierLength(InputStream input) {
int ctxQualLen = -1;
try (Scanner scanner = new Scanner(input)) {
if (scanner.findWithinHorizon(ctxQualLenPattern, 100) != null)
ctxQualLen = Integer.parseInt(scanner.match().group(1));
}
return ctxQualLen;
}
private static IPluginModelBase findModel(String id, String version) {
IPluginModelBase unversioned = null;
for (IPluginModelBase model : PluginRegistry.getActiveModels()) {
BundleDescription desc = model.getBundleDescription();
if (desc == null)
continue;
if (desc.getSymbolicName().equals(id)) {
Version v = desc.getVersion();
if (v == null) {
if (version == null)
return model;
unversioned = model;
continue;
}
if (version == null || version.equals(v.toString()))
return model;
}
}
return unversioned;
}
private int contextQualifierLength = -1;
private boolean dirty;
private final File externalFile;
private static final Pattern ctxQualLenPattern = Pattern.compile("\\scontextQualifierLength\\s*=\\s*(\\d+)\\s"); //$NON-NLS-1$
public EditableFeatureModel() {
this.externalFile = null;
}
public EditableFeatureModel(File externalFile) {
this.externalFile = externalFile;
}
public void computeRequiredPlugins() throws CoreException {
ArrayList<IFeatureImport> seen = new ArrayList<IFeatureImport>(feature.getImports().length);
for (IFeaturePlugin fp : feature.getPlugins()) {
IPluginModelBase model = findModel(fp.getId(), fp.getVersion());
if (model == null)
continue;
addRequirements(seen, model.getPluginBase());
if (model.isFragmentModel()) {
HostSpecification hostSpec = model.getBundleDescription().getHost();
String id = hostSpec.getName();
String version = null;
int match = IMatchRules.NONE;
VersionRange versionRange = hostSpec.getVersionRange();
if (!(versionRange == null || VersionRange.emptyRange.equals(versionRange))) {
version = versionRange.getMinimum() != null ? versionRange.getMinimum().toString() : null;
match = PluginBase.getMatchRule(versionRange);
}
addRequirement(id, version, match, seen);
}
}
}
public int getContextQualifierLength() {
return contextQualifierLength;
}
@Override
public boolean isDirty() {
return dirty;
}
@Override
public boolean isEditable() {
return true;
}
@Override
public void load() throws CoreException {
InputStream input = null;
try {
input = new BufferedInputStream(new FileInputStream(externalFile));
load(input, true);
} catch (FileNotFoundException e) {
throw BuckminsterException.wrap(e);
} finally {
IOUtils.close(input);
}
if (feature == null) {
// Parsing failed but AbstractFeatureParser silently ignores
// SAXExceptions
//
throw BuckminsterException.fromMessage(NLS.bind(Messages.unable_to_parse_feature_0, externalFile));
}
int ctxQualLen = -1;
String version = feature.getVersion();
if (version != null && version.indexOf('-') > 0) {
try {
input = new FileInputStream(externalFile);
ctxQualLen = getContextQualifierLength(input);
} catch (FileNotFoundException e) {
throw BuckminsterException.wrap(e);
} finally {
IOUtils.close(input);
}
contextQualifierLength = ctxQualLen;
}
}
@Override
public void save() {
if (externalFile == null) {
PDEPlugin.getLogger().error(Messages.unable_to_save_feature_model);
return;
}
try {
save(externalFile);
} catch (FileNotFoundException e) {
PDEPlugin.getLogger().error(e, Messages.unable_to_save_feature_model);
}
}
public void save(File outputFile) throws FileNotFoundException {
OutputStream output = null;
try {
output = new FileOutputStream(outputFile);
this.save(output);
} finally {
IOUtils.close(output);
}
}
public void save(OutputStream output) {
try {
PrintWriter writer = new PrintWriter(new OutputStreamWriter(output, "UTF-8")); //$NON-NLS-1$
save(writer);
writer.flush();
} catch (UnsupportedEncodingException e) {
PDEPlugin.getLogger().error(e, Messages.utf8_not_supported);
throw new RuntimeException(e);
}
}
@Override
public void save(PrintWriter writer) {
writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); //$NON-NLS-1$
if (getFeature().getVersion().indexOf('-') > 0 && contextQualifierLength != -1)
writer.println("<!-- contextQualifierLength=" + contextQualifierLength + " -->"); //$NON-NLS-1$ //$NON-NLS-2$
feature.write("", writer); //$NON-NLS-1$
setDirty(false);
}
public void setContextQualifierLength(int contextQualifierLength) {
this.contextQualifierLength = contextQualifierLength;
}
@Override
public void setDirty(boolean dirty) {
this.dirty = dirty;
}
private void addRequirement(String id, String version, int match, List<IFeatureImport> seen) throws CoreException {
for (IFeatureImport bundle : seen)
if (bundle.getId().equals(id) && (version == null || (version.equals(bundle.getVersion()) && match == bundle.getMatch())))
return;
for (IFeaturePlugin bundle : feature.getPlugins())
if (VersionUtil.compare(bundle.getId(), bundle.getVersion(), id, version, match))
return;
IFeatureImport bundle = feature.getModel().getFactory().createImport();
bundle.setId(id);
bundle.setVersion(version);
bundle.setMatch(match);
feature.addImports(new IFeatureImport[] { bundle });
seen.add(bundle);
}
private void addRequirements(List<IFeatureImport> seen, IPluginBase plugin) throws CoreException {
for (IPluginImport bundle : plugin.getImports())
addRequirement(bundle.getId(), bundle.getVersion(), bundle.getMatch(), seen);
}
}