Sync with latest bnd code for testing purposes
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1354104 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/Container.java b/bundleplugin/src/main/java/aQute/bnd/build/Container.java
index f726527..394a14a 100644
--- a/bundleplugin/src/main/java/aQute/bnd/build/Container.java
+++ b/bundleplugin/src/main/java/aQute/bnd/build/Container.java
@@ -106,8 +106,7 @@
public boolean equals(Object other) {
if (other instanceof Container)
return file.equals(((Container) other).file);
- else
- return false;
+ return false;
}
public int hashCode() {
@@ -126,8 +125,7 @@
public String toString() {
if (getError() != null)
return "/error/" + getError();
- else
- return getFile().getAbsolutePath();
+ return getFile().getAbsolutePath();
}
public Map<String,String> getAttributes() {
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/Project.java b/bundleplugin/src/main/java/aQute/bnd/build/Project.java
index b55727a..54c2436 100644
--- a/bundleplugin/src/main/java/aQute/bnd/build/Project.java
+++ b/bundleplugin/src/main/java/aQute/bnd/build/Project.java
@@ -55,7 +55,7 @@
boolean delayRunDependencies = false;
final ProjectMessages msgs = ReporterMessages.base(this, ProjectMessages.class);
- public Project(Workspace workspace, File projectDir, File buildFile) throws Exception {
+ public Project(Workspace workspace, @SuppressWarnings("unused") File projectDir, File buildFile) throws Exception {
super(workspace);
this.workspace = workspace;
setFileMustExist(false);
@@ -121,7 +121,8 @@
builder = new ProjectBuilder(parent);
builder.setBase(getBase());
-
+ builder.setPedantic(isPedantic());
+ builder.setTrace(isTrace());
return builder;
}
@@ -1057,8 +1058,7 @@
}
if (f.getName().endsWith("lib"))
return new Container(this, bsn, range, Container.TYPE.LIBRARY, f, null, attrs);
- else
- return new Container(this, bsn, range, Container.TYPE.REPO, f, null, attrs);
+ return new Container(this, bsn, range, Container.TYPE.REPO, f, null, attrs);
}
/**
@@ -1258,7 +1258,7 @@
}
if (isStale()) {
- trace("Building " + this);
+ trace("building " + this);
files = buildLocal(underTest);
}
@@ -1366,8 +1366,8 @@
rdr.close();
f.delete();
break;
- } else
- files.add(ff);
+ }
+ files.add(ff);
}
return this.files = files.toArray(new File[files.size()]);
}
@@ -1377,8 +1377,7 @@
}
if (buildIfAbsent)
return files = buildLocal(false);
- else
- return files = null;
+ return files = null;
}
/**
@@ -1428,8 +1427,8 @@
}
getWorkspace().changedFile(bfs);
return files;
- } else
- return null;
+ }
+ return null;
}
finally {
builder.close();
@@ -1664,7 +1663,7 @@
return jar;
}
- public String _project(String args[]) {
+ public String _project(@SuppressWarnings("unused") String args[]) {
return getBase().getAbsolutePath();
}
@@ -1753,7 +1752,7 @@
/**
* Run all before command plugins
*/
- void before(Project p, String a) {
+ void before(@SuppressWarnings("unused") Project p, String a) {
List<CommandPlugin> testPlugins = getPlugins(CommandPlugin.class);
for (CommandPlugin testPlugin : testPlugins) {
testPlugin.before(this, a);
@@ -1763,7 +1762,7 @@
/**
* Run all after command plugins
*/
- void after(Project p, String a, Throwable t) {
+ void after(@SuppressWarnings("unused") Project p, String a, Throwable t) {
List<CommandPlugin> testPlugins = getPlugins(CommandPlugin.class);
for (int i = testPlugins.size() - 1; i >= 0; i--) {
testPlugins.get(i).after(this, a, t);
@@ -1800,7 +1799,7 @@
}
@SuppressWarnings("unchecked")
- public void script(String type, String script) throws Exception {
+ public void script(@SuppressWarnings("unused") String type, String script) throws Exception {
// TODO check tyiping
List<Scripter> scripters = getPlugins(Scripter.class);
if (scripters.isEmpty()) {
@@ -1812,7 +1811,7 @@
scripters.get(0).eval(x, new StringReader(script));
}
- public String _repos(String args[]) throws Exception {
+ public String _repos(@SuppressWarnings("unused") String args[]) throws Exception {
List<RepositoryPlugin> repos = getPlugins(RepositoryPlugin.class);
List<String> names = new ArrayList<String>();
for (RepositoryPlugin rp : repos)
@@ -1906,7 +1905,7 @@
* @return null or the builder for a sub file.
* @throws Exception
*/
- public Container getDeliverable(String bsn, Map<String,String> attrs) throws Exception {
+ public Container getDeliverable(String bsn, @SuppressWarnings("unused") Map<String,String> attrs) throws Exception {
Collection< ? extends Builder> builders = getSubBuilders();
for (Builder sub : builders) {
if (sub.getBsn().equals(bsn))
@@ -2040,19 +2039,18 @@
if (newVersion.compareTo(oldVersion) == 0) {
return;
- } else {
- PrintWriter pw = IO.writer(file);
- pw.println("version " + newVersion);
- pw.flush();
- pw.close();
-
- String path = packageName.replace('.', '/') + "/packageinfo";
- File binary = IO.getFile(getOutput(), path);
- binary.getParentFile().mkdirs();
- IO.copy(file, binary);
-
- refresh();
}
+ PrintWriter pw = IO.writer(file);
+ pw.println("version " + newVersion);
+ pw.flush();
+ pw.close();
+
+ String path = packageName.replace('.', '/') + "/packageinfo";
+ File binary = IO.getFile(getOutput(), path);
+ binary.getParentFile().mkdirs();
+ IO.copy(file, binary);
+
+ refresh();
}
File getPackageInfoFile(String packageName) {
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/Workspace.java b/bundleplugin/src/main/java/aQute/bnd/build/Workspace.java
index c74687f..50619c8 100644
--- a/bundleplugin/src/main/java/aQute/bnd/build/Workspace.java
+++ b/bundleplugin/src/main/java/aQute/bnd/build/Workspace.java
@@ -154,7 +154,7 @@
}
}
- public String _workspace(String args[]) {
+ public String _workspace(@SuppressWarnings("unused") String args[]) {
return getBase().getAbsolutePath();
}
@@ -279,7 +279,6 @@
if (in != null)
unzip(in, root);
else {
- System.err.println("!!!! Couldn't find embedded-repo.jar in bundle ");
error("Couldn't find embedded-repo.jar in bundle ");
}
}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/WorkspaceRepository.java b/bundleplugin/src/main/java/aQute/bnd/build/WorkspaceRepository.java
index 9e71975..9a1753c 100644
--- a/bundleplugin/src/main/java/aQute/bnd/build/WorkspaceRepository.java
+++ b/bundleplugin/src/main/java/aQute/bnd/build/WorkspaceRepository.java
@@ -22,7 +22,7 @@
this.workspace = workspace;
}
- public File[] get(String bsn, String range) throws Exception {
+ private File[] get(String bsn, String range) throws Exception {
Collection<Project> projects = workspace.getAllProjects();
SortedMap<Version,File> foundVersion = new TreeMap<Version,File>();
for (Project project : projects) {
@@ -45,15 +45,13 @@
result = foundVersion.values().toArray(result);
if (!"latest".equals(range)) {
return result;
- } else {
- if (result.length > 0) {
- return new File[] {
- result[0]
- };
- } else {
- return new File[0];
- }
}
+ if (result.length > 0) {
+ return new File[] {
+ result[0]
+ };
+ }
+ return new File[0];
}
public File get(String bsn, String range, Strategy strategy, Map<String,String> properties) throws Exception {
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/BndEditModel.java b/bundleplugin/src/main/java/aQute/bnd/build/model/BndEditModel.java
new file mode 100644
index 0000000..8dee7e2
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/BndEditModel.java
@@ -0,0 +1,719 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Neil Bartlett.
+ * 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:
+ * Neil Bartlett - initial API and implementation
+ *******************************************************************************/
+package aQute.bnd.build.model;
+
+import java.beans.*;
+import java.io.*;
+import java.util.*;
+import java.util.Map.Entry;
+
+import org.osgi.framework.*;
+
+import aQute.bnd.build.model.clauses.*;
+import aQute.bnd.build.model.conversions.*;
+import aQute.lib.properties.*;
+import aQute.libg.header.*;
+import aQute.libg.tuple.*;
+import aQute.libg.version.Version;
+
+/**
+ * A model for a Bnd file. In the first iteration, use a simple Properties
+ * object; this will need to be enhanced to additionally record formatting, e.g.
+ * line breaks and empty lines, and comments.
+ *
+ * @author Neil Bartlett
+ */
+public class BndEditModel {
+
+ public static final String LINE_SEPARATOR = " \\\n\t";
+ public static final String LIST_SEPARATOR = ",\\\n\t";
+
+ protected static final String ISO_8859_1 = "ISO-8859-1"; //$NON-NLS-1$
+
+ protected static String[] KNOWN_PROPERTIES = new String[] {
+ Constants.BUNDLE_SYMBOLICNAME, Constants.BUNDLE_VERSION, Constants.BUNDLE_ACTIVATOR,
+ Constants.EXPORT_PACKAGE, Constants.IMPORT_PACKAGE, aQute.lib.osgi.Constants.PRIVATE_PACKAGE,
+ aQute.lib.osgi.Constants.SOURCES,
+ aQute.lib.osgi.Constants.SERVICE_COMPONENT, aQute.lib.osgi.Constants.CLASSPATH,
+ aQute.lib.osgi.Constants.BUILDPATH, aQute.lib.osgi.Constants.BUILDPACKAGES,
+ aQute.lib.osgi.Constants.RUNBUNDLES, aQute.lib.osgi.Constants.RUNPROPERTIES, aQute.lib.osgi.Constants.SUB,
+ // BndConstants.RUNFRAMEWORK,
+ aQute.lib.osgi.Constants.RUNVM,
+ // BndConstants.RUNVMARGS,
+ // BndConstants.TESTSUITES,
+ aQute.lib.osgi.Constants.TESTCASES, aQute.lib.osgi.Constants.PLUGIN, aQute.lib.osgi.Constants.PLUGINPATH,
+ aQute.lib.osgi.Constants.RUNREPOS,
+ // BndConstants.RUNREQUIRE,
+ // BndConstants.RUNEE,
+ // BndConstants.RESOLVE_MODE
+ };
+
+ public static final String BUNDLE_VERSION_MACRO = "${"
+ + Constants.BUNDLE_VERSION
+ + "}";
+
+ protected final Map<String,Converter< ? extends Object,String>> converters = new HashMap<String,Converter< ? extends Object,String>>();
+ protected final Map<String,Converter<String, ? extends Object>> formatters = new HashMap<String,Converter<String, ? extends Object>>();
+ // private final DataModelHelper obrModelHelper = new DataModelHelperImpl();
+
+ private final PropertyChangeSupport propChangeSupport = new PropertyChangeSupport(
+ this);
+ private final Properties properties = new Properties();
+
+ private File bndResource;
+ private boolean projectFile;
+ private final Map<String,Object> objectProperties = new HashMap<String,Object>();
+ private final Map<String,String> changesToSave = new HashMap<String,String>();
+
+ // CONVERTERS
+ protected Converter<List<VersionedClause>,String> buildPathConverter = new ClauseListConverter<VersionedClause>(
+ new Converter<VersionedClause,Pair<String,Attrs>>() {
+ public VersionedClause convert(
+ Pair<String,Attrs> input)
+ throws IllegalArgumentException {
+ return new VersionedClause(
+ input.getFirst(),
+ input.getSecond());
+ }
+ });
+ protected Converter<List<VersionedClause>,String> buildPackagesConverter = new ClauseListConverter<VersionedClause>(
+ new Converter<VersionedClause,Pair<String,Attrs>>() {
+ public VersionedClause convert(
+ Pair<String,Attrs> input)
+ throws IllegalArgumentException {
+ return new VersionedClause(
+ input.getFirst(),
+ input.getSecond());
+ }
+ });
+ protected Converter<List<VersionedClause>,String> clauseListConverter = new ClauseListConverter<VersionedClause>(
+ new VersionedClauseConverter());
+ protected Converter<String,String> stringConverter = new NoopConverter<String>();
+ protected Converter<Boolean,String> includedSourcesConverter = new Converter<Boolean,String>() {
+ public Boolean convert(
+ String string)
+ throws IllegalArgumentException {
+ return Boolean
+ .valueOf(string);
+ }
+ };
+ protected Converter<VersionPolicy,String> versionPolicyConverter = new Converter<VersionPolicy,String>() {
+ public VersionPolicy convert(
+ String string)
+ throws IllegalArgumentException {
+ return VersionPolicy
+ .parse(string);
+ }
+ };
+ protected Converter<List<String>,String> listConverter = SimpleListConverter
+ .create();
+ protected Converter<List<HeaderClause>,String> headerClauseListConverter = new HeaderClauseListConverter();
+ protected ClauseListConverter<ExportedPackage> exportPackageConverter = new ClauseListConverter<ExportedPackage>(
+ new Converter<ExportedPackage,Pair<String,Attrs>>() {
+ public ExportedPackage convert(
+ Pair<String,Attrs> input) {
+ return new ExportedPackage(
+ input.getFirst(),
+ input.getSecond());
+ }
+ });
+ protected Converter<List<ServiceComponent>,String> serviceComponentConverter = new ClauseListConverter<ServiceComponent>(
+ new Converter<ServiceComponent,Pair<String,Attrs>>() {
+ public ServiceComponent convert(
+ Pair<String,Attrs> input)
+ throws IllegalArgumentException {
+ return new ServiceComponent(
+ input.getFirst(),
+ input.getSecond());
+ }
+ });
+ protected Converter<List<ImportPattern>,String> importPatternConverter = new ClauseListConverter<ImportPattern>(
+ new Converter<ImportPattern,Pair<String,Attrs>>() {
+ public ImportPattern convert(
+ Pair<String,Attrs> input)
+ throws IllegalArgumentException {
+ return new ImportPattern(
+ input.getFirst(),
+ input.getSecond());
+ }
+ });
+
+ protected Converter<Map<String,String>,String> propertiesConverter = new PropertiesConverter();
+
+ // Converter<List<Requirement>, String> requirementListConverter =
+ // SimpleListConverter.create(new Converter<Requirement, String>() {
+ // public Requirement convert(String input) throws IllegalArgumentException
+ // {
+ // int index = input.indexOf(":");
+ // if (index < 0)
+ // throw new IllegalArgumentException("Invalid format for OBR requirement");
+ //
+ // String name = input.substring(0, index);
+ // String filter = input.substring(index + 1);
+ //
+ // return new Requirement(name, filter);
+ // }
+ // });
+ // Converter<EE, String> eeConverter = new Converter<EE, String>() {
+ // public EE convert(String input) throws IllegalArgumentException {
+ // return EE.parse(input);
+ // }
+ // };
+ //
+ // Converter<ResolveMode, String> resolveModeConverter =
+ // EnumConverter.create(ResolveMode.class, ResolveMode.manual);
+
+ // FORMATTERS
+ protected Converter<String,Object> defaultFormatter = new DefaultFormatter();
+ protected Converter<String,String> newlineEscapeFormatter = new NewlineEscapedStringFormatter();
+ protected Converter<String,Boolean> defaultFalseBoolFormatter = new DefaultBooleanFormatter(
+ false);
+ protected Converter<String,Collection< ? >> stringListFormatter = new CollectionFormatter<Object>(
+ LIST_SEPARATOR,
+ (String) null);
+ protected Converter<String,Collection< ? extends HeaderClause>> headerClauseListFormatter = new CollectionFormatter<HeaderClause>(
+ LIST_SEPARATOR,
+ new HeaderClauseFormatter(),
+ null);
+ protected Converter<String,Map<String,String>> propertiesFormatter = new MapFormatter(
+ LIST_SEPARATOR,
+ new PropertiesEntryFormatter(),
+ null);
+ // Converter<String, Collection<? extends Requirement>>
+ // requirementListFormatter = new
+ // CollectionFormatter<Requirement>(LIST_SEPARATOR, new Converter<String,
+ // Requirement>() {
+ // public String convert(Requirement input) throws IllegalArgumentException
+ // {
+ // return new
+ // StringBuilder().append(input.getName()).append(':').append(input.getFilter()).toString();
+ // }
+ // }, null);
+ // Converter<String, EE> eeFormatter = new Converter<String, EE>() {
+ // public String convert(EE input) throws IllegalArgumentException {
+ // return input != null ? input.getEEName() : null;
+ // }
+ // };
+ Converter<String,Collection< ? extends String>> runReposFormatter = new CollectionFormatter<String>(
+ LIST_SEPARATOR,
+ aQute.lib.osgi.Constants.EMPTY_HEADER);
+
+ // Converter<String, ResolveMode> resolveModeFormatter =
+ // EnumFormatter.create(ResolveMode.class, ResolveMode.manual);
+
+ @SuppressWarnings("deprecation")
+ public BndEditModel() {
+ // register converters
+ converters.put(aQute.lib.osgi.Constants.BUILDPATH, buildPathConverter);
+ converters.put(aQute.lib.osgi.Constants.BUILDPACKAGES, buildPackagesConverter);
+ converters.put(aQute.lib.osgi.Constants.RUNBUNDLES, clauseListConverter);
+ converters.put(Constants.BUNDLE_SYMBOLICNAME, stringConverter);
+ converters.put(Constants.BUNDLE_VERSION, stringConverter);
+ converters.put(Constants.BUNDLE_ACTIVATOR, stringConverter);
+ converters.put(aQute.lib.osgi.Constants.OUTPUT, stringConverter);
+ converters.put(aQute.lib.osgi.Constants.SOURCES, includedSourcesConverter);
+ converters.put(aQute.lib.osgi.Constants.PRIVATE_PACKAGE, listConverter);
+ converters.put(aQute.lib.osgi.Constants.CLASSPATH, listConverter);
+ converters.put(Constants.EXPORT_PACKAGE, exportPackageConverter);
+ converters.put(aQute.lib.osgi.Constants.SERVICE_COMPONENT, serviceComponentConverter);
+ converters.put(Constants.IMPORT_PACKAGE, importPatternConverter);
+ // converters.put(BndConstants.RUNFRAMEWORK, stringConverter);
+ converters.put(aQute.lib.osgi.Constants.SUB, listConverter);
+ converters.put(aQute.lib.osgi.Constants.RUNPROPERTIES, propertiesConverter);
+ converters.put(aQute.lib.osgi.Constants.RUNVM, stringConverter);
+ // converters.put(BndConstants.RUNVMARGS, stringConverter);
+ converters.put(aQute.lib.osgi.Constants.TESTSUITES, listConverter);
+ converters.put(aQute.lib.osgi.Constants.TESTCASES, listConverter);
+ converters.put(aQute.lib.osgi.Constants.PLUGIN, headerClauseListConverter);
+ // converters.put(BndConstants.RUNREQUIRE, requirementListConverter);
+ // converters.put(BndConstants.RUNEE, new NoopConverter<String>());
+ // converters.put(BndConstants.RUNREPOS, listConverter);
+ // converters.put(BndConstants.RESOLVE_MODE, resolveModeConverter);
+
+ formatters.put(aQute.lib.osgi.Constants.BUILDPATH, headerClauseListFormatter);
+ formatters.put(aQute.lib.osgi.Constants.BUILDPACKAGES, headerClauseListFormatter);
+ formatters.put(aQute.lib.osgi.Constants.RUNBUNDLES, headerClauseListFormatter);
+ formatters.put(Constants.BUNDLE_SYMBOLICNAME, newlineEscapeFormatter);
+ formatters.put(Constants.BUNDLE_VERSION, newlineEscapeFormatter);
+ formatters.put(Constants.BUNDLE_ACTIVATOR, newlineEscapeFormatter);
+ formatters.put(aQute.lib.osgi.Constants.OUTPUT, newlineEscapeFormatter);
+ formatters.put(aQute.lib.osgi.Constants.SOURCES, defaultFalseBoolFormatter);
+ formatters.put(aQute.lib.osgi.Constants.PRIVATE_PACKAGE, stringListFormatter);
+ formatters.put(aQute.lib.osgi.Constants.CLASSPATH, stringListFormatter);
+ formatters.put(Constants.EXPORT_PACKAGE, headerClauseListFormatter);
+ formatters.put(aQute.lib.osgi.Constants.SERVICE_COMPONENT, headerClauseListFormatter);
+ formatters.put(Constants.IMPORT_PACKAGE, headerClauseListFormatter);
+ // formatters.put(BndConstants.RUNFRAMEWORK, newlineEscapeFormatter);
+ formatters.put(aQute.lib.osgi.Constants.SUB, stringListFormatter);
+ formatters.put(aQute.lib.osgi.Constants.RUNPROPERTIES, propertiesFormatter);
+ formatters.put(aQute.lib.osgi.Constants.RUNVM, newlineEscapeFormatter);
+ // formatters.put(BndConstants.RUNVMARGS, newlineEscapeFormatter);
+ // formatters.put(BndConstants.TESTSUITES, stringListFormatter);
+ formatters.put(aQute.lib.osgi.Constants.TESTCASES, stringListFormatter);
+ formatters.put(aQute.lib.osgi.Constants.PLUGIN, headerClauseListFormatter);
+ // formatters.put(BndConstants.RUNREQUIRE, requirementListFormatter);
+ // formatters.put(BndConstants.RUNEE, new NoopConverter<String>());
+ // formatters.put(BndConstants.RUNREPOS, runReposFormatter);
+ // formatters.put(BndConstants.RESOLVE_MODE, resolveModeFormatter);
+ }
+
+ public void loadFrom(IDocument document) throws IOException {
+ InputStream inputStream = new ByteArrayInputStream(document.get().getBytes(ISO_8859_1));
+ loadFrom(inputStream);
+ }
+
+ public void loadFrom(File file) throws IOException {
+ loadFrom(new BufferedInputStream(new FileInputStream(file)));
+ }
+
+ public void loadFrom(InputStream inputStream) throws IOException {
+ try {
+ // Clear and load
+ properties.clear();
+ properties.load(inputStream);
+ objectProperties.clear();
+ changesToSave.clear();
+
+ // Fire property changes on all known property names
+ for (String prop : KNOWN_PROPERTIES) {
+ // null values for old and new forced the change to be fired
+ propChangeSupport.firePropertyChange(prop, null, null);
+ }
+ }
+ finally {
+ inputStream.close();
+ }
+
+ }
+
+ public void saveChangesTo(IDocument document) {
+ for (Iterator<Entry<String,String>> iter = changesToSave.entrySet().iterator(); iter.hasNext();) {
+ Entry<String,String> entry = iter.next();
+ iter.remove();
+
+ String propertyName = entry.getKey();
+ String stringValue = entry.getValue();
+
+ updateDocument(document, propertyName, stringValue);
+ }
+ }
+
+ protected static IRegion findEntry(IDocument document, String name) throws Exception {
+ PropertiesLineReader reader = new PropertiesLineReader(document);
+ LineType type = reader.next();
+ while (type != LineType.eof) {
+ if (type == LineType.entry) {
+ String key = reader.key();
+ if (name.equals(key))
+ return reader.region();
+ }
+ type = reader.next();
+ }
+ return null;
+ }
+
+ protected static void updateDocument(IDocument document, String name, String value) {
+ String newEntry;
+ if (value != null) {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append(name).append(": ").append(value);
+ newEntry = buffer.toString();
+ } else {
+ newEntry = "";
+ }
+
+ try {
+ IRegion region = findEntry(document, name);
+ if (region != null) {
+ // Replace an existing entry
+ int offset = region.getOffset();
+ int length = region.getLength();
+
+ // If the replacement is empty, remove one extra character to
+ // the right, i.e. the following newline,
+ // unless this would take us past the end of the document
+ if (newEntry.length() == 0 && offset + length + 1 < document.getLength()) {
+ length++;
+ }
+ document.replace(offset, length, newEntry);
+ } else if (newEntry.length() > 0) {
+ // This is a new entry, put it at the end of the file
+
+ // Does the last line of the document have a newline? If not,
+ // we need to add one.
+ if (document.getLength() > 0 && document.getChar(document.getLength() - 1) != '\n')
+ newEntry = "\n" + newEntry;
+ document.replace(document.getLength(), 0, newEntry);
+ }
+ }
+ catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ public List<String> getAllPropertyNames() {
+ List<String> result = new ArrayList<String>(properties.size());
+
+ Enumeration<String> names = (Enumeration<String>) properties.propertyNames();
+
+ while (names.hasMoreElements()) {
+ result.add(names.nextElement());
+ }
+ return result;
+ }
+
+ public Object genericGet(String propertyName) {
+ Converter< ? extends Object,String> converter = converters.get(propertyName);
+ if (converter == null)
+ converter = new NoopConverter<String>();
+ return doGetObject(propertyName, converter);
+ }
+
+ public void genericSet(String propertyName, Object value) {
+ Object oldValue = genericGet(propertyName);
+ Converter<String,Object> formatter = (Converter<String,Object>) formatters.get(propertyName);
+ if (formatter == null)
+ formatter = new DefaultFormatter();
+ doSetObject(propertyName, oldValue, value, formatter);
+ }
+
+ public String getBundleSymbolicName() {
+ return doGetObject(Constants.BUNDLE_SYMBOLICNAME, stringConverter);
+ }
+
+ public void setBundleSymbolicName(String bundleSymbolicName) {
+ doSetObject(Constants.BUNDLE_SYMBOLICNAME, getBundleSymbolicName(), bundleSymbolicName, newlineEscapeFormatter);
+ }
+
+ public String getBundleVersionString() {
+ return doGetObject(Constants.BUNDLE_VERSION, stringConverter);
+ }
+
+ public void setBundleVersion(String bundleVersion) {
+ doSetObject(Constants.BUNDLE_VERSION, getBundleVersionString(), bundleVersion, newlineEscapeFormatter);
+ }
+
+ public String getBundleActivator() {
+ return doGetObject(Constants.BUNDLE_ACTIVATOR, stringConverter);
+ }
+
+ public void setBundleActivator(String bundleActivator) {
+ doSetObject(Constants.BUNDLE_ACTIVATOR, getBundleActivator(), bundleActivator, newlineEscapeFormatter);
+ }
+
+ public String getOutputFile() {
+ return doGetObject(aQute.lib.osgi.Constants.OUTPUT, stringConverter);
+ }
+
+ public void setOutputFile(String name) {
+ doSetObject(aQute.lib.osgi.Constants.OUTPUT, getOutputFile(), name, newlineEscapeFormatter);
+ }
+
+ public boolean isIncludeSources() {
+ return doGetObject(aQute.lib.osgi.Constants.SOURCES, includedSourcesConverter);
+ }
+
+ public void setIncludeSources(boolean includeSources) {
+ boolean oldValue = isIncludeSources();
+ doSetObject(aQute.lib.osgi.Constants.SOURCES, oldValue, includeSources, defaultFalseBoolFormatter);
+ }
+
+ public List<String> getPrivatePackages() {
+ return doGetObject(aQute.lib.osgi.Constants.PRIVATE_PACKAGE, listConverter);
+ }
+
+ public void setPrivatePackages(List< ? extends String> packages) {
+ List<String> oldPackages = getPrivatePackages();
+ doSetObject(aQute.lib.osgi.Constants.PRIVATE_PACKAGE, oldPackages, packages, stringListFormatter);
+ }
+
+ public List<ExportedPackage> getSystemPackages() {
+ return doGetObject(aQute.lib.osgi.Constants.RUNSYSTEMPACKAGES, exportPackageConverter);
+ }
+
+ public void setSystemPackages(List< ? extends ExportedPackage> packages) {
+ List<ExportedPackage> oldPackages = getSystemPackages();
+ doSetObject(aQute.lib.osgi.Constants.RUNSYSTEMPACKAGES, oldPackages, packages, headerClauseListFormatter);
+ }
+
+ public List<String> getClassPath() {
+ return doGetObject(aQute.lib.osgi.Constants.CLASSPATH, listConverter);
+ }
+
+ public void addPrivatePackage(String packageName) {
+ List<String> packages = getPrivatePackages();
+ if (packages == null)
+ packages = new ArrayList<String>();
+ else
+ packages = new ArrayList<String>(packages);
+ packages.add(packageName);
+ setPrivatePackages(packages);
+ }
+
+ public void setClassPath(List< ? extends String> classPath) {
+ List<String> oldClassPath = getClassPath();
+ doSetObject(aQute.lib.osgi.Constants.CLASSPATH, oldClassPath, classPath, stringListFormatter);
+ }
+
+ public List<ExportedPackage> getExportedPackages() {
+ return doGetObject(Constants.EXPORT_PACKAGE, exportPackageConverter);
+ }
+
+ public void setExportedPackages(List< ? extends ExportedPackage> exports) {
+ boolean referencesBundleVersion = false;
+
+ if (exports != null) {
+ for (ExportedPackage pkg : exports) {
+ String versionString = pkg.getVersionString();
+ if (versionString != null && versionString.indexOf(BUNDLE_VERSION_MACRO) > -1) {
+ referencesBundleVersion = true;
+ }
+ }
+ }
+ List<ExportedPackage> oldValue = getExportedPackages();
+ doSetObject(Constants.EXPORT_PACKAGE, oldValue, exports, headerClauseListFormatter);
+
+ if (referencesBundleVersion && getBundleVersionString() == null) {
+ setBundleVersion(new Version(0, 0, 0).toString());
+ }
+ }
+
+ public void addExportedPackage(ExportedPackage export) {
+ List<ExportedPackage> exports = getExportedPackages();
+ exports = (exports == null) ? new ArrayList<ExportedPackage>() : new ArrayList<ExportedPackage>(exports);
+ exports.add(export);
+ setExportedPackages(exports);
+ }
+
+ public List<String> getDSAnnotationPatterns() {
+ return doGetObject(aQute.lib.osgi.Constants.DSANNOTATIONS, listConverter);
+ }
+
+ public void setDSAnnotationPatterns(List< ? extends String> patterns) {
+ List<String> oldValue = getDSAnnotationPatterns();
+ doSetObject(aQute.lib.osgi.Constants.DSANNOTATIONS, oldValue, patterns, stringListFormatter);
+ }
+
+ public List<ServiceComponent> getServiceComponents() {
+ return doGetObject(aQute.lib.osgi.Constants.SERVICE_COMPONENT, serviceComponentConverter);
+ }
+
+ public void setServiceComponents(List< ? extends ServiceComponent> components) {
+ List<ServiceComponent> oldValue = getServiceComponents();
+ doSetObject(aQute.lib.osgi.Constants.SERVICE_COMPONENT, oldValue, components, headerClauseListFormatter);
+ }
+
+ public List<ImportPattern> getImportPatterns() {
+ return doGetObject(Constants.IMPORT_PACKAGE, importPatternConverter);
+ }
+
+ public void setImportPatterns(List< ? extends ImportPattern> patterns) {
+ List<ImportPattern> oldValue = getImportPatterns();
+ doSetObject(Constants.IMPORT_PACKAGE, oldValue, patterns, headerClauseListFormatter);
+ }
+
+ public List<VersionedClause> getBuildPath() {
+ return doGetObject(aQute.lib.osgi.Constants.BUILDPATH, buildPathConverter);
+ }
+
+ public void setBuildPath(List< ? extends VersionedClause> paths) {
+ List<VersionedClause> oldValue = getBuildPath();
+ doSetObject(aQute.lib.osgi.Constants.BUILDPATH, oldValue, paths, headerClauseListFormatter);
+ }
+
+ public List<VersionedClause> getBuildPackages() {
+ return doGetObject(aQute.lib.osgi.Constants.BUILDPACKAGES, buildPackagesConverter);
+ }
+
+ public void setBuildPackages(List< ? extends VersionedClause> paths) {
+ List<VersionedClause> oldValue = getBuildPackages();
+ doSetObject(aQute.lib.osgi.Constants.BUILDPACKAGES, oldValue, paths, headerClauseListFormatter);
+ }
+
+ public List<VersionedClause> getRunBundles() {
+ return doGetObject(aQute.lib.osgi.Constants.RUNBUNDLES, clauseListConverter);
+ }
+
+ public void setRunBundles(List< ? extends VersionedClause> paths) {
+ List<VersionedClause> oldValue = getBuildPath();
+ doSetObject(aQute.lib.osgi.Constants.RUNBUNDLES, oldValue, paths, headerClauseListFormatter);
+ }
+
+ public boolean isIncludedPackage(String packageName) {
+ final Collection<String> privatePackages = getPrivatePackages();
+ if (privatePackages != null) {
+ if (privatePackages.contains(packageName))
+ return true;
+ }
+ final Collection<ExportedPackage> exportedPackages = getExportedPackages();
+ if (exportedPackages != null) {
+ for (ExportedPackage pkg : exportedPackages) {
+ if (packageName.equals(pkg.getName())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public List<String> getSubBndFiles() {
+ return doGetObject(aQute.lib.osgi.Constants.SUB, listConverter);
+ }
+
+ public void setSubBndFiles(List<String> subBndFiles) {
+ List<String> oldValue = getSubBndFiles();
+ doSetObject(aQute.lib.osgi.Constants.SUB, oldValue, subBndFiles, stringListFormatter);
+ }
+
+ public Map<String,String> getRunProperties() {
+ return doGetObject(aQute.lib.osgi.Constants.RUNPROPERTIES, propertiesConverter);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see bndtools.editor.model.IBndModel#setRunProperties(java.util.Map)
+ */
+ public void setRunProperties(Map<String,String> props) {
+ Map<String,String> old = getRunProperties();
+ doSetObject(aQute.lib.osgi.Constants.RUNPROPERTIES, old, props, propertiesFormatter);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see bndtools.editor.model.IBndModel#getRunVMArgs()
+ */
+ public String getRunVMArgs() {
+ return doGetObject(aQute.lib.osgi.Constants.RUNVM, stringConverter);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see bndtools.editor.model.IBndModel#setRunVMArgs(java.lang.String)
+ */
+ public void setRunVMArgs(String args) {
+ String old = getRunVMArgs();
+ doSetObject(aQute.lib.osgi.Constants.RUNVM, old, args, newlineEscapeFormatter);
+ }
+
+ @SuppressWarnings("deprecation")
+ public List<String> getTestSuites() {
+ List<String> testCases = doGetObject(aQute.lib.osgi.Constants.TESTCASES, listConverter);
+ testCases = testCases != null ? testCases : Collections.<String> emptyList();
+
+ List<String> testSuites = doGetObject(aQute.lib.osgi.Constants.TESTSUITES, listConverter);
+ testSuites = testSuites != null ? testSuites : Collections.<String> emptyList();
+
+ List<String> result = new ArrayList<String>(testCases.size() + testSuites.size());
+ result.addAll(testCases);
+ result.addAll(testSuites);
+ return result;
+ }
+
+ @SuppressWarnings("deprecation")
+ public void setTestSuites(List<String> suites) {
+ List<String> old = getTestSuites();
+ doSetObject(aQute.lib.osgi.Constants.TESTCASES, old, suites, stringListFormatter);
+ doSetObject(aQute.lib.osgi.Constants.TESTSUITES, null, null, stringListFormatter);
+ }
+
+ public List<HeaderClause> getPlugins() {
+ return doGetObject(aQute.lib.osgi.Constants.PLUGIN, headerClauseListConverter);
+ }
+
+ public void setPlugins(List<HeaderClause> plugins) {
+ List<HeaderClause> old = getPlugins();
+ doSetObject(aQute.lib.osgi.Constants.PLUGIN, old, plugins, headerClauseListFormatter);
+ }
+
+ public List<String> getPluginPath() {
+ return doGetObject(aQute.lib.osgi.Constants.PLUGINPATH, listConverter);
+ }
+
+ public void setPluginPath(List<String> pluginPath) {
+ List<String> old = getPluginPath();
+ doSetObject(aQute.lib.osgi.Constants.PLUGINPATH, old, pluginPath, stringListFormatter);
+ }
+
+ public List<String> getRunRepos() {
+ return doGetObject(aQute.lib.osgi.Constants.RUNREPOS, listConverter);
+ }
+
+ public void setRunRepos(List<String> repos) {
+ List<String> old = getRunRepos();
+ doSetObject(aQute.lib.osgi.Constants.RUNREPOS, old, repos, runReposFormatter);
+ }
+
+ public String getRunFramework() {
+ return doGetObject(aQute.lib.osgi.Constants.RUNFRAMEWORK, stringConverter);
+ }
+
+ public void setRunFramework(String clause) {
+ String oldValue = getRunFramework();
+ doSetObject(aQute.lib.osgi.Constants.RUNFRAMEWORK, oldValue, clause, newlineEscapeFormatter);
+ }
+
+
+ protected <R> R doGetObject(String name, Converter< ? extends R, ? super String> converter) {
+ R result;
+ if (objectProperties.containsKey(name)) {
+ R temp = (R) objectProperties.get(name);
+ result = temp;
+ } else if (changesToSave.containsKey(name)) {
+ result = converter.convert(changesToSave.get(name));
+ objectProperties.put(name, result);
+ } else if (properties.containsKey(name)) {
+ result = converter.convert(properties.getProperty(name));
+ objectProperties.put(name, result);
+ } else {
+ result = null;
+ }
+ return result;
+ }
+
+ protected <T> void doSetObject(String name, T oldValue, T newValue, Converter<String, ? super T> formatter) {
+ objectProperties.put(name, newValue);
+ changesToSave.put(name, formatter.convert(newValue));
+ propChangeSupport.firePropertyChange(name, oldValue, newValue);
+ }
+
+ public void setProjectFile(boolean projectFile) {
+ this.projectFile = projectFile;
+ }
+
+ public boolean isProjectFile() {
+ return this.projectFile;
+ }
+
+ public void addPropertyChangeListener(PropertyChangeListener listener) {
+ propChangeSupport.addPropertyChangeListener(listener);
+ }
+
+ public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
+ propChangeSupport.addPropertyChangeListener(propertyName, listener);
+ }
+
+ public void removePropertyChangeListener(PropertyChangeListener listener) {
+ propChangeSupport.removePropertyChangeListener(listener);
+ }
+
+ public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
+ propChangeSupport.removePropertyChangeListener(propertyName, listener);
+ }
+
+ public void setBndResource(File bndResource) {
+ this.bndResource = bndResource;
+ }
+
+ public File getBndResource() {
+ return bndResource;
+ }
+}
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/LowerVersionMatchType.java b/bundleplugin/src/main/java/aQute/bnd/build/model/LowerVersionMatchType.java
new file mode 100644
index 0000000..dcdf969
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/LowerVersionMatchType.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Neil Bartlett.
+ * 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:
+ * Neil Bartlett - initial API and implementation
+ *******************************************************************************/
+package aQute.bnd.build.model;
+
+public enum LowerVersionMatchType {
+ Exact("${@}"), Micro("${version;===;${@}}"), Minor("${version;==;${@}}"), Major("${version;=;${@}}");
+
+ private final String representation;
+
+ private LowerVersionMatchType(String representation) {
+ this.representation = representation;
+ }
+
+ public String getRepresentation() {
+ return representation;
+ }
+
+ public static LowerVersionMatchType parse(String string) throws IllegalArgumentException {
+ for (LowerVersionMatchType type : LowerVersionMatchType.class.getEnumConstants()) {
+ if (type.getRepresentation().equals(string)) {
+ return type;
+ }
+ }
+ throw new IllegalArgumentException("Failed to parse version match type.");
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/UpperVersionMatchType.java b/bundleplugin/src/main/java/aQute/bnd/build/model/UpperVersionMatchType.java
new file mode 100644
index 0000000..91d97ca
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/UpperVersionMatchType.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Neil Bartlett.
+ * 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:
+ * Neil Bartlett - initial API and implementation
+ *******************************************************************************/
+package aQute.bnd.build.model;
+
+public enum UpperVersionMatchType {
+ Exact("${@}"), NextMicro("${version;==+;${@}}"), NextMinor("${version;=+;${@}}"), NextMajor("${version;+;${@}}");
+
+ private final String representation;
+
+ private UpperVersionMatchType(String representation) {
+ this.representation = representation;
+ }
+
+ public String getRepresentation() {
+ return representation;
+ }
+
+ public static UpperVersionMatchType parse(String string) throws IllegalArgumentException {
+ for (UpperVersionMatchType type : UpperVersionMatchType.class.getEnumConstants()) {
+ if (type.getRepresentation().equals(string)) {
+ return type;
+ }
+ }
+ throw new IllegalArgumentException("Failed to parse version match type.");
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/VersionPolicy.java b/bundleplugin/src/main/java/aQute/bnd/build/model/VersionPolicy.java
new file mode 100644
index 0000000..3367c9f
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/VersionPolicy.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Neil Bartlett.
+ * 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:
+ * Neil Bartlett - initial API and implementation
+ *******************************************************************************/
+package aQute.bnd.build.model;
+
+public class VersionPolicy {
+ private final LowerVersionMatchType lowerMatch;
+ private final UpperVersionMatchType upperMatch;
+ private final boolean upperInclusive;
+
+ public VersionPolicy(LowerVersionMatchType lowerMatch, UpperVersionMatchType upperMatch, boolean upperInclusive) {
+ assert lowerMatch != null;
+ this.lowerMatch = lowerMatch;
+ this.upperMatch = upperMatch;
+ this.upperInclusive = upperInclusive;
+ }
+
+ static VersionPolicy parse(String string) throws IllegalArgumentException {
+ String lowerSegment;
+ String upperSegment;
+ boolean upperInclusive;
+
+ if (string.charAt(0) == '[') {
+ int commaIndex = string.indexOf(',');
+ if (commaIndex < 0)
+ throw new IllegalArgumentException("Failed to parse version policy.");
+ lowerSegment = string.substring(1, commaIndex);
+
+ char lastChar = string.charAt(string.length() - 1);
+ if (lastChar == ')')
+ upperInclusive = false;
+ else if (lastChar == ']')
+ upperInclusive = true;
+ else
+ throw new IllegalArgumentException("Failed to parse version policy.");
+
+ upperSegment = string.substring(commaIndex + 1, string.length() - 1);
+ } else {
+ lowerSegment = string;
+ upperSegment = null;
+ upperInclusive = true;
+ }
+
+ LowerVersionMatchType lower = LowerVersionMatchType.parse(lowerSegment);
+ UpperVersionMatchType upper = upperSegment != null ? UpperVersionMatchType.parse(upperSegment) : null;
+
+ return new VersionPolicy(lower, upper, upperInclusive);
+ }
+
+ public LowerVersionMatchType getLowerMatch() {
+ return lowerMatch;
+ }
+
+ public UpperVersionMatchType getUpperMatch() {
+ return upperMatch;
+ }
+
+ public boolean isUpperInclusive() {
+ return upperInclusive;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buffer = new StringBuilder();
+
+ if (upperMatch != null) {
+ buffer.append('[');
+ buffer.append(lowerMatch.getRepresentation());
+ buffer.append(',');
+ buffer.append(upperMatch.getRepresentation());
+ buffer.append(upperInclusive ? ']' : ')');
+ } else {
+ buffer.append(lowerMatch.getRepresentation());
+ }
+
+ return buffer.toString();
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ComponentSvcReference.java b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ComponentSvcReference.java
new file mode 100644
index 0000000..7c6700e
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ComponentSvcReference.java
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Neil Bartlett.
+ * 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:
+ * Neil Bartlett - initial API and implementation
+ *******************************************************************************/
+package aQute.bnd.build.model.clauses;
+
+public class ComponentSvcReference implements Cloneable {
+
+ private String name;
+ private String bind;
+ private String unbind;
+ private String serviceClass;
+ private boolean optional;
+ private boolean multiple;
+ private boolean dynamic;
+ private String targetFilter;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getBind() {
+ return bind;
+ }
+
+ public void setBind(String bind) {
+ this.bind = bind;
+ }
+
+ public String getUnbind() {
+ return unbind;
+ }
+
+ public void setUnbind(String unbind) {
+ this.unbind = unbind;
+ }
+
+ public String getServiceClass() {
+ return serviceClass;
+ }
+
+ public void setServiceClass(String serviceClass) {
+ this.serviceClass = serviceClass;
+ }
+
+ public boolean isOptional() {
+ return optional;
+ }
+
+ public void setOptional(boolean optional) {
+ this.optional = optional;
+ }
+
+ public boolean isMultiple() {
+ return multiple;
+ }
+
+ public void setMultiple(boolean multiple) {
+ this.multiple = multiple;
+ }
+
+ public boolean isDynamic() {
+ return dynamic;
+ }
+
+ public void setDynamic(boolean dynamic) {
+ this.dynamic = dynamic;
+ }
+
+ public String getTargetFilter() {
+ return targetFilter;
+ }
+
+ public void setTargetFilter(String targetFilter) {
+ this.targetFilter = targetFilter;
+ }
+
+ @Override
+ public ComponentSvcReference clone() {
+ ComponentSvcReference copy = new ComponentSvcReference();
+ copy.name = this.name;
+ copy.serviceClass = this.serviceClass;
+ copy.bind = this.bind;
+ copy.unbind = this.unbind;
+ copy.optional = this.optional;
+ copy.multiple = this.multiple;
+ copy.dynamic = this.dynamic;
+ copy.targetFilter = this.targetFilter;
+ return copy;
+ }
+
+ public void copyFrom(ComponentSvcReference other) {
+ this.name = other.name;
+ this.serviceClass = other.serviceClass;
+ this.bind = other.bind;
+ this.unbind = other.unbind;
+ this.optional = other.optional;
+ this.multiple = other.multiple;
+ this.dynamic = other.dynamic;
+ this.targetFilter = other.targetFilter;
+ }
+}
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ExportedPackage.java b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ExportedPackage.java
new file mode 100644
index 0000000..0e9be04
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ExportedPackage.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Neil Bartlett.
+ * 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:
+ * Neil Bartlett - initial API and implementation
+ *******************************************************************************/
+package aQute.bnd.build.model.clauses;
+
+import org.osgi.framework.Constants;
+
+import aQute.libg.header.Attrs;
+
+public class ExportedPackage extends HeaderClause {
+
+ public ExportedPackage(String packageName, Attrs attribs) {
+ super(packageName, attribs);
+ }
+
+ @Override
+ protected boolean newlinesBetweenAttributes() {
+ return false;
+ }
+
+ public void setVersionString(String version) {
+ attribs.put(Constants.VERSION_ATTRIBUTE, version);
+ }
+
+ public String getVersionString() {
+ return attribs.get(Constants.VERSION_ATTRIBUTE);
+ }
+
+ public boolean isProvided() {
+ return Boolean.valueOf(attribs.get(aQute.lib.osgi.Constants.PROVIDE_DIRECTIVE));
+ }
+
+ public void setProvided(boolean provided) {
+ if (provided)
+ attribs.put(aQute.lib.osgi.Constants.PROVIDE_DIRECTIVE, Boolean.toString(true));
+ else
+ attribs.remove(aQute.lib.osgi.Constants.PROVIDE_DIRECTIVE);
+ }
+
+ @Override
+ public ExportedPackage clone() {
+ return new ExportedPackage(this.name, new Attrs(this.attribs));
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/HeaderClause.java b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/HeaderClause.java
new file mode 100644
index 0000000..b0391a6
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/HeaderClause.java
@@ -0,0 +1,170 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Neil Bartlett.
+ * 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:
+ * Neil Bartlett - initial API and implementation
+ *******************************************************************************/
+package aQute.bnd.build.model.clauses;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+
+import aQute.libg.header.Attrs;
+
+public class HeaderClause implements Cloneable, Comparable<HeaderClause> {
+
+ private static final String INTERNAL_LIST_SEPARATOR = ";";
+ private static final String INTERNAL_LIST_SEPARATOR_NEWLINES = INTERNAL_LIST_SEPARATOR + "\\\n\t\t";
+
+ protected String name;
+ protected Attrs attribs;
+
+ public HeaderClause(String name, Attrs attribs) {
+ assert name != null;
+ assert attribs != null;
+
+ this.name = name;
+ this.attribs = attribs;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Attrs getAttribs() {
+ return attribs;
+ }
+
+ public List<String> getListAttrib(String attrib) {
+ String string = attribs.get(attrib);
+ if (string == null)
+ return null;
+
+ List<String> result = new ArrayList<String>();
+ StringTokenizer tokenizer = new StringTokenizer(string, ",");
+ while (tokenizer.hasMoreTokens()) {
+ result.add(tokenizer.nextToken().trim());
+ }
+
+ return result;
+ }
+
+ public void setListAttrib(String attrib, Collection< ? extends String> value) {
+ if (value == null || value.isEmpty())
+ attribs.remove(attrib);
+ else {
+ StringBuilder buffer = new StringBuilder();
+ boolean first = true;
+ for (String string : value) {
+ if (!first)
+ buffer.append(',');
+ buffer.append(string);
+ first = false;
+ }
+ attribs.put(attrib, buffer.toString());
+ }
+ }
+
+ public void formatTo(StringBuilder buffer) {
+ formatTo(buffer, null);
+ }
+
+ public void formatTo(StringBuilder buffer, Comparator<Entry<String,String>> sorter) {
+ String separator = newlinesBetweenAttributes() ? INTERNAL_LIST_SEPARATOR_NEWLINES : INTERNAL_LIST_SEPARATOR;
+ buffer.append(name);
+ if (attribs != null) {
+ Set<Entry<String,String>> set;
+ if (sorter != null) {
+ set = new TreeSet<Map.Entry<String,String>>(sorter);
+ set.addAll(attribs.entrySet());
+ } else {
+ set = attribs.entrySet();
+ }
+
+ for (Iterator<Entry<String,String>> iter = set.iterator(); iter.hasNext();) {
+ Entry<String,String> entry = iter.next();
+ String name = entry.getKey();
+ String value = entry.getValue();
+
+ if (value != null && value.length() > 0) {
+ buffer.append(separator);
+
+ // If the value contains any comma or equals, then quote the
+ // whole thing
+ if (value.indexOf(',') > -1 || value.indexOf('=') > -1)
+ value = "'" + value + "'";
+
+ buffer.append(name).append('=').append(value);
+ }
+ }
+ }
+ }
+
+ protected boolean newlinesBetweenAttributes() {
+ return false;
+ }
+
+ @Override
+ public HeaderClause clone() {
+ try {
+ HeaderClause clone = (HeaderClause) super.clone();
+ clone.name = this.name;
+ clone.attribs = new Attrs(this.attribs);
+ return clone;
+ }
+ catch (CloneNotSupportedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public int compareTo(HeaderClause other) {
+ return this.name.compareTo(other.name);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((attribs == null) ? 0 : attribs.hashCode());
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ HeaderClause other = (HeaderClause) obj;
+ if (attribs == null) {
+ if (other.attribs != null)
+ return false;
+ } else if (!attribs.isEqual(other.attribs))
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ return true;
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ImportPattern.java b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ImportPattern.java
new file mode 100644
index 0000000..5e0854c
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ImportPattern.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Neil Bartlett.
+ * 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:
+ * Neil Bartlett - initial API and implementation
+ *******************************************************************************/
+package aQute.bnd.build.model.clauses;
+
+import org.osgi.framework.Constants;
+
+import aQute.libg.header.Attrs;
+
+public class ImportPattern extends VersionedClause implements Cloneable {
+
+ public ImportPattern(String pattern, Attrs attributes) {
+ super(pattern, attributes);
+ }
+
+ public boolean isOptional() {
+ String resolution = attribs.get(aQute.lib.osgi.Constants.RESOLUTION_DIRECTIVE);
+ return Constants.RESOLUTION_OPTIONAL.equals(resolution);
+ }
+
+ public void setOptional(boolean optional) {
+ if (optional)
+ attribs.put(aQute.lib.osgi.Constants.RESOLUTION_DIRECTIVE, Constants.RESOLUTION_OPTIONAL);
+ else
+ attribs.remove(aQute.lib.osgi.Constants.RESOLUTION_DIRECTIVE);
+ }
+
+ @Override
+ public ImportPattern clone() {
+ return new ImportPattern(this.name, new Attrs(this.attribs));
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ServiceComponent.java b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ServiceComponent.java
new file mode 100644
index 0000000..69f8610
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ServiceComponent.java
@@ -0,0 +1,266 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Neil Bartlett.
+ * 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:
+ * Neil Bartlett - initial API and implementation
+ *******************************************************************************/
+package aQute.bnd.build.model.clauses;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import aQute.libg.header.Attrs;
+
+public class ServiceComponent extends HeaderClause implements Cloneable {
+
+ // v1.0.0 attributes
+ // public final static String COMPONENT_NAME = "name:";
+ public final static String COMPONENT_FACTORY = "factory:";
+ public final static String COMPONENT_SERVICEFACTORY = "servicefactory:";
+ public final static String COMPONENT_IMMEDIATE = "immediate:";
+ public final static String COMPONENT_ENABLED = "enabled:";
+
+ public final static String COMPONENT_DYNAMIC = "dynamic:";
+ public final static String COMPONENT_MULTIPLE = "multiple:";
+ public final static String COMPONENT_PROVIDE = "provide:";
+ public final static String COMPONENT_OPTIONAL = "optional:";
+ public final static String COMPONENT_PROPERTIES = "properties:";
+ // public final static String COMPONENT_IMPLEMENTATION = "implementation:";
+
+ // v1.1.0 attributes
+ public final static String COMPONENT_VERSION = "version:";
+ public final static String COMPONENT_CONFIGURATION_POLICY = "configuration-policy:";
+ public final static String COMPONENT_MODIFIED = "modified:";
+ public final static String COMPONENT_ACTIVATE = "activate:";
+ public final static String COMPONENT_DEACTIVATE = "deactivate:";
+
+ private final static Pattern REFERENCE_PATTERN = Pattern.compile("([^(]+)(\\(.+\\))?");
+
+ public ServiceComponent(String name, Attrs attribs) {
+ super(name, attribs);
+ }
+
+ public boolean isPath() {
+ return name.indexOf('/') >= 0 || name.endsWith(".xml");
+ }
+
+ private Set<String> getStringSet(String attrib) {
+ List<String> list = getListAttrib(attrib);
+ return list != null ? new HashSet<String>(list) : new HashSet<String>();
+ }
+
+ public void setPropertiesMap(Map<String,String> properties) {
+ List<String> strings = new ArrayList<String>(properties.size());
+ for (Entry<String,String> entry : properties.entrySet()) {
+ String line = new StringBuilder().append(entry.getKey()).append("=").append(entry.getValue()).toString();
+ strings.add(line);
+ }
+ setListAttrib(COMPONENT_PROPERTIES, strings);
+ }
+
+ public Map<String,String> getPropertiesMap() {
+ Map<String,String> result = new LinkedHashMap<String,String>();
+
+ List<String> list = getListAttrib(COMPONENT_PROPERTIES);
+ if (list != null) {
+ for (String entryStr : list) {
+ String name;
+ String value;
+
+ int index = entryStr.lastIndexOf('=');
+ if (index == -1) {
+ name = entryStr;
+ value = null;
+ } else {
+ name = entryStr.substring(0, index);
+ value = entryStr.substring(index + 1);
+ }
+
+ result.put(name, value);
+ }
+ }
+
+ return result;
+ }
+
+ public void setSvcRefs(List< ? extends ComponentSvcReference> refs) {
+ // First remove all existing references, i.e. non-directives
+ for (Iterator<String> iter = attribs.keySet().iterator(); iter.hasNext();) {
+ String name = iter.next();
+ if (!name.endsWith(":")) {
+ iter.remove();
+ }
+ }
+
+ // Add in the references
+ Set<String> dynamic = new HashSet<String>();
+ Set<String> optional = new HashSet<String>();
+ Set<String> multiple = new HashSet<String>();
+ for (ComponentSvcReference ref : refs) {
+ // Build the reference name with bind and unbind
+ String expandedRefName = ref.getName();
+ if (ref.getBind() != null) {
+ expandedRefName += "/" + ref.getBind();
+ if (ref.getUnbind() != null) {
+ expandedRefName += "/" + ref.getUnbind();
+ }
+ }
+
+ // Start building the map value
+ StringBuilder buffer = new StringBuilder();
+ buffer.append(ref.getServiceClass());
+
+ // Add the target filter
+ if (ref.getTargetFilter() != null) {
+ buffer.append('(').append(ref.getTargetFilter()).append(')');
+ }
+
+ // Work out the cardinality suffix (i.e. *, +, ? org ~).
+ // Adding to the dynamic/multiple/optional lists for non-standard
+ // cases
+ String cardinalitySuffix;
+ if (ref.isDynamic()) {
+ if (ref.isOptional()) {
+ if (ref.isMultiple()) // 0..n dynamic
+ cardinalitySuffix = "*";
+ else
+ // 0..1 dynamic
+ cardinalitySuffix = "?";
+ } else {
+ if (ref.isMultiple()) // 1..n dynamic
+ cardinalitySuffix = "+";
+ else { // 1..1 dynamic, not a normal combination
+ cardinalitySuffix = null;
+ dynamic.add(ref.getName());
+ }
+ }
+ } else {
+ if (ref.isOptional()) {
+ if (ref.isMultiple()) { // 0..n static, not a normal
+ // combination
+ cardinalitySuffix = null;
+ optional.add(ref.getName());
+ multiple.add(ref.getName());
+ } else { // 0..1 static
+ cardinalitySuffix = "~";
+ }
+ } else {
+ if (ref.isMultiple()) { // 1..n static, not a normal
+ // combination
+ multiple.add(ref.getName());
+ cardinalitySuffix = null;
+ } else { // 1..1 static
+ cardinalitySuffix = null;
+ }
+ }
+ }
+
+ if (cardinalitySuffix != null)
+ buffer.append(cardinalitySuffix);
+
+ // Write to the map
+ attribs.put(expandedRefName, buffer.toString());
+ }
+ setListAttrib(COMPONENT_OPTIONAL, optional);
+ setListAttrib(COMPONENT_MULTIPLE, multiple);
+ setListAttrib(COMPONENT_DYNAMIC, dynamic);
+ }
+
+ public List<ComponentSvcReference> getSvcRefs() {
+ List<ComponentSvcReference> result = new ArrayList<ComponentSvcReference>();
+
+ Set<String> dynamicSet = getStringSet(COMPONENT_DYNAMIC);
+ Set<String> optionalSet = getStringSet(COMPONENT_OPTIONAL);
+ Set<String> multipleSet = getStringSet(COMPONENT_MULTIPLE);
+
+ for (Entry<String,String> entry : attribs.entrySet()) {
+ String referenceName = entry.getKey();
+
+ // Skip directives
+ if (referenceName.endsWith(":"))//$NON-NLS-1$
+ continue;
+
+ ComponentSvcReference svcRef = new ComponentSvcReference();
+
+ String bind = null;
+ String unbind = null;
+
+ if (referenceName.indexOf('/') >= 0) {
+ String parts[] = referenceName.split("/");
+ referenceName = parts[0];
+ bind = parts[1];
+ if (parts.length > 2)
+ unbind = parts[2];
+ /*
+ * else if (bind.startsWith("add")) unbind =
+ * bind.replaceAll("add(.+)", "remove$1"); else unbind = "un" +
+ * bind; } else if
+ * (Character.isLowerCase(referenceName.charAt(0))) { bind =
+ * "set" + Character.toUpperCase(referenceName.charAt(0)) +
+ * referenceName.substring(1); unbind = "un" + bind;
+ */
+ }
+ svcRef.setName(referenceName);
+ svcRef.setBind(bind);
+ svcRef.setUnbind(unbind);
+
+ String interfaceName = entry.getValue();
+ if (interfaceName == null || interfaceName.length() == 0) {
+ continue;
+ }
+ svcRef.setServiceClass(interfaceName);
+
+ // Check the cardinality by looking at the last
+ // character of the value
+ char c = interfaceName.charAt(interfaceName.length() - 1);
+ if ("?+*~".indexOf(c) >= 0) {
+ if (c == '?' || c == '*' || c == '~')
+ optionalSet.add(referenceName);
+ if (c == '+' || c == '*')
+ multipleSet.add(referenceName);
+ if (c == '+' || c == '*' || c == '?')
+ dynamicSet.add(referenceName);
+ interfaceName = interfaceName.substring(0, interfaceName.length() - 1);
+ }
+ svcRef.setOptional(optionalSet.contains(referenceName));
+ svcRef.setMultiple(multipleSet.contains(referenceName));
+ svcRef.setDynamic(dynamicSet.contains(referenceName));
+
+ // Parse the target from the interface name
+ // The target is a filter.
+ String target = null;
+ Matcher m = REFERENCE_PATTERN.matcher(interfaceName);
+ if (m.matches()) {
+ interfaceName = m.group(1);
+ target = m.group(2);
+ }
+ svcRef.setTargetFilter(target);
+
+ result.add(svcRef);
+ }
+
+ return result;
+ }
+
+ @Override
+ public ServiceComponent clone() {
+ return new ServiceComponent(this.name, new Attrs(this.attribs));
+ }
+
+ @Override
+ protected boolean newlinesBetweenAttributes() {
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/VersionedClause.java b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/VersionedClause.java
new file mode 100644
index 0000000..a6e79a9
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/VersionedClause.java
@@ -0,0 +1,27 @@
+package aQute.bnd.build.model.clauses;
+
+import org.osgi.framework.Constants;
+
+import aQute.libg.header.Attrs;
+
+public class VersionedClause extends HeaderClause implements Cloneable {
+ public VersionedClause(String name, Attrs attribs) {
+ super(name, attribs);
+ }
+
+ public String getVersionRange() {
+ return attribs.get(Constants.VERSION_ATTRIBUTE);
+ }
+
+ public void setVersionRange(String versionRangeString) {
+ attribs.put(Constants.VERSION_ATTRIBUTE, versionRangeString);
+ }
+
+ @Override
+ public VersionedClause clone() {
+ VersionedClause clone = (VersionedClause) super.clone();
+ clone.name = this.name;
+ clone.attribs = new Attrs(this.attribs);
+ return clone;
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/packageinfo b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/packageinfo
new file mode 100644
index 0000000..55af8e5
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/packageinfo
@@ -0,0 +1 @@
+version 2
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/ClauseListConverter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/ClauseListConverter.java
new file mode 100644
index 0000000..130b60b
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/ClauseListConverter.java
@@ -0,0 +1,31 @@
+package aQute.bnd.build.model.conversions;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+import aQute.lib.osgi.*;
+import aQute.libg.header.*;
+import aQute.libg.tuple.*;
+
+public class ClauseListConverter<R> implements Converter<List<R>,String> {
+
+ private final Converter< ? extends R, ? super Pair<String,Attrs>> itemConverter;
+
+ public ClauseListConverter(Converter< ? extends R, ? super Pair<String,Attrs>> itemConverter) {
+ this.itemConverter = itemConverter;
+ }
+
+ public List<R> convert(String input) throws IllegalArgumentException {
+ List<R> result = new ArrayList<R>();
+
+ Parameters header = new Parameters(input);
+ for (Entry<String,Attrs> entry : header.entrySet()) {
+ String key = Processor.removeDuplicateMarker(entry.getKey());
+ Pair<String,Attrs> pair = Pair.newInstance(key, entry.getValue());
+ result.add(itemConverter.convert(pair));
+ }
+
+ return result;
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/CollectionFormatter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/CollectionFormatter.java
new file mode 100644
index 0000000..d9f345a
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/CollectionFormatter.java
@@ -0,0 +1,47 @@
+package aQute.bnd.build.model.conversions;
+
+import java.util.*;
+
+public class CollectionFormatter<T> implements Converter<String,Collection< ? extends T>> {
+
+ private final String separator;
+ private final Converter<String, ? super T> itemFormatter;
+ private final String emptyOutput;
+
+ public CollectionFormatter(String separator) {
+ this(separator, (String) null);
+ }
+
+ public CollectionFormatter(String separator, String emptyOutput) {
+ this(separator, new DefaultFormatter(), emptyOutput);
+ }
+
+ public CollectionFormatter(String separator, Converter<String, ? super T> itemFormatter) {
+ this(separator, itemFormatter, null);
+ }
+
+ public CollectionFormatter(String separator, Converter<String, ? super T> itemFormatter, String emptyOutput) {
+ this.separator = separator;
+ this.itemFormatter = itemFormatter;
+ this.emptyOutput = emptyOutput;
+ }
+
+ public String convert(Collection< ? extends T> input) throws IllegalArgumentException {
+ String result = null;
+ if (input != null) {
+ if (input.isEmpty()) {
+ result = emptyOutput;
+ } else {
+ StringBuilder buffer = new StringBuilder();
+ for (Iterator< ? extends T> iter = input.iterator(); iter.hasNext();) {
+ T item = iter.next();
+ buffer.append(itemFormatter.convert(item));
+ if (iter.hasNext())
+ buffer.append(separator);
+ }
+ result = buffer.toString();
+ }
+ }
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/Converter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/Converter.java
new file mode 100644
index 0000000..8513f52
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/Converter.java
@@ -0,0 +1,5 @@
+package aQute.bnd.build.model.conversions;
+
+public interface Converter<R, T> {
+ R convert(T input) throws IllegalArgumentException;
+}
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/DefaultBooleanFormatter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/DefaultBooleanFormatter.java
new file mode 100644
index 0000000..f188569
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/DefaultBooleanFormatter.java
@@ -0,0 +1,26 @@
+package aQute.bnd.build.model.conversions;
+
+/**
+ * Formatter for booleans with a default value; if the input value matches the
+ * default then it is formatted to <code>null</code>.
+ *
+ * @author Neil Bartlett
+ */
+public class DefaultBooleanFormatter implements Converter<String,Boolean> {
+
+ private final boolean defaultValue;
+
+ public DefaultBooleanFormatter(boolean defaultValue) {
+ this.defaultValue = defaultValue;
+ }
+
+ public String convert(Boolean input) throws IllegalArgumentException {
+ String result = null;
+
+ if (input != null && input.booleanValue() != defaultValue)
+ result = input.toString();
+
+ return result;
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/DefaultFormatter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/DefaultFormatter.java
new file mode 100644
index 0000000..a552c0d
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/DefaultFormatter.java
@@ -0,0 +1,9 @@
+package aQute.bnd.build.model.conversions;
+
+public class DefaultFormatter implements Converter<String,Object> {
+
+ public String convert(Object input) throws IllegalArgumentException {
+ return input == null ? null : input.toString();
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/EnumConverter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/EnumConverter.java
new file mode 100644
index 0000000..4c94d3b
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/EnumConverter.java
@@ -0,0 +1,27 @@
+package aQute.bnd.build.model.conversions;
+
+public class EnumConverter<E extends Enum<E>> implements Converter<E,String> {
+
+ private final Class<E> enumType;
+ private final E defaultValue;
+
+ public static <E extends Enum<E>> EnumConverter<E> create(Class<E> enumType) {
+ return new EnumConverter<E>(enumType, null);
+ }
+
+ public static <E extends Enum<E>> EnumConverter<E> create(Class<E> enumType, E defaultValue) {
+ return new EnumConverter<E>(enumType, defaultValue);
+ }
+
+ private EnumConverter(Class<E> enumType, E defaultValue) {
+ this.enumType = enumType;
+ this.defaultValue = defaultValue;
+ }
+
+ public E convert(String input) throws IllegalArgumentException {
+ if (input == null)
+ return defaultValue;
+ return Enum.valueOf(enumType, input);
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/EnumFormatter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/EnumFormatter.java
new file mode 100644
index 0000000..8500a25
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/EnumFormatter.java
@@ -0,0 +1,53 @@
+package aQute.bnd.build.model.conversions;
+
+/**
+ * Formats an enum type. Outputs {@code null} when the value of the enum is
+ * equal to a default value.
+ *
+ * @param <E>
+ * @author Neil Bartlett
+ */
+public class EnumFormatter<E extends Enum<E>> implements Converter<String,E> {
+
+ private final E defaultValue;
+
+ /**
+ * Construct a new formatter with no default value, i.e. any non-null value
+ * of the enum will print that value.
+ *
+ * @param enumType
+ * The enum type.
+ * @return
+ */
+ public static <E extends Enum<E>> EnumFormatter<E> create(Class<E> enumType) {
+ return new EnumFormatter<E>(null);
+ }
+
+ /**
+ * Construct a new formatter with the specified default value.
+ *
+ * @param enumType
+ * The enum type.
+ * @param defaultValue
+ * The default value, which will never be output.
+ * @return
+ */
+ public static <E extends Enum<E>> EnumFormatter<E> create(Class<E> enumType, E defaultValue) {
+ return new EnumFormatter<E>(defaultValue);
+ }
+
+ private EnumFormatter(E defaultValue) {
+ this.defaultValue = defaultValue;
+ }
+
+ public String convert(E input) throws IllegalArgumentException {
+ String result;
+ if (input == defaultValue || input == null)
+ result = null;
+ else {
+ result = input.toString();
+ }
+ return result;
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/HeaderClauseConverter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/HeaderClauseConverter.java
new file mode 100644
index 0000000..dfb3a5d
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/HeaderClauseConverter.java
@@ -0,0 +1,13 @@
+package aQute.bnd.build.model.conversions;
+
+import aQute.bnd.build.model.clauses.*;
+import aQute.libg.header.*;
+import aQute.libg.tuple.*;
+
+public class HeaderClauseConverter implements Converter<HeaderClause,Pair<String,Attrs>> {
+
+ public HeaderClause convert(Pair<String,Attrs> input) throws IllegalArgumentException {
+ return new HeaderClause(input.getFirst(), input.getSecond());
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/HeaderClauseFormatter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/HeaderClauseFormatter.java
new file mode 100644
index 0000000..f4161f5
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/HeaderClauseFormatter.java
@@ -0,0 +1,11 @@
+package aQute.bnd.build.model.conversions;
+
+import aQute.bnd.build.model.clauses.*;
+
+public class HeaderClauseFormatter implements Converter<String,HeaderClause> {
+ public String convert(HeaderClause input) throws IllegalArgumentException {
+ StringBuilder buffer = new StringBuilder();
+ input.formatTo(buffer);
+ return buffer.toString();
+ }
+}
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/HeaderClauseListConverter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/HeaderClauseListConverter.java
new file mode 100644
index 0000000..65d8471
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/HeaderClauseListConverter.java
@@ -0,0 +1,11 @@
+package aQute.bnd.build.model.conversions;
+
+import aQute.bnd.build.model.clauses.*;
+
+public class HeaderClauseListConverter extends ClauseListConverter<HeaderClause> {
+
+ public HeaderClauseListConverter() {
+ super(new HeaderClauseConverter());
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/MapFormatter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/MapFormatter.java
new file mode 100644
index 0000000..03d6f71
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/MapFormatter.java
@@ -0,0 +1,18 @@
+package aQute.bnd.build.model.conversions;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+public class MapFormatter implements Converter<String,Map<String,String>> {
+
+ private CollectionFormatter<Entry<String,String>> entrySetFormatter;
+
+ public MapFormatter(String listSeparator, Converter<String, ? super Entry<String,String>> entryFormatter,
+ String emptyOutput) {
+ entrySetFormatter = new CollectionFormatter<Entry<String,String>>(listSeparator, entryFormatter, emptyOutput);
+ }
+
+ public String convert(Map<String,String> input) throws IllegalArgumentException {
+ return entrySetFormatter.convert(input.entrySet());
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/NewlineEscapedStringFormatter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/NewlineEscapedStringFormatter.java
new file mode 100644
index 0000000..790cda0
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/NewlineEscapedStringFormatter.java
@@ -0,0 +1,33 @@
+package aQute.bnd.build.model.conversions;
+
+import aQute.bnd.build.model.*;
+
+public class NewlineEscapedStringFormatter implements Converter<String,String> {
+
+ public String convert(String input) throws IllegalArgumentException {
+ if (input == null)
+ return null;
+
+ // Shortcut the result for the majority of cases where there is no
+ // newline
+ if (input.indexOf('\n') == -1)
+ return input;
+
+ // Build a new string with newlines escaped
+ StringBuilder result = new StringBuilder();
+ int position = 0;
+ while (position < input.length()) {
+ int newlineIndex = input.indexOf('\n', position);
+ if (newlineIndex == -1) {
+ result.append(input.substring(position));
+ break;
+ }
+ result.append(input.substring(position, newlineIndex));
+ result.append(BndEditModel.LINE_SEPARATOR);
+ position = newlineIndex + 1;
+ }
+
+ return result.toString();
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/NoopConverter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/NoopConverter.java
new file mode 100644
index 0000000..7a9f39d
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/NoopConverter.java
@@ -0,0 +1,7 @@
+package aQute.bnd.build.model.conversions;
+
+public class NoopConverter<T> implements Converter<T,T> {
+ public T convert(T input) throws IllegalArgumentException {
+ return input;
+ }
+}
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/PropertiesConverter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/PropertiesConverter.java
new file mode 100644
index 0000000..a1ecf5f
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/PropertiesConverter.java
@@ -0,0 +1,13 @@
+package aQute.bnd.build.model.conversions;
+
+import java.util.*;
+
+import aQute.libg.header.*;
+
+public class PropertiesConverter implements Converter<Map<String,String>,String> {
+
+ public Map<String,String> convert(String input) throws IllegalArgumentException {
+ return OSGiHeader.parseProperties(input);
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/PropertiesEntryFormatter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/PropertiesEntryFormatter.java
new file mode 100644
index 0000000..b487190
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/PropertiesEntryFormatter.java
@@ -0,0 +1,26 @@
+package aQute.bnd.build.model.conversions;
+
+import java.util.Map.Entry;
+
+public class PropertiesEntryFormatter implements Converter<String,Entry<String,String>> {
+ public String convert(Entry<String,String> entry) {
+ StringBuilder buffer = new StringBuilder();
+
+ String name = entry.getKey();
+ buffer.append(name).append('=');
+
+ String value = entry.getValue();
+ if (value != null && value.length() > 0) {
+ int quotableIndex = value.indexOf(',');
+ if (quotableIndex == -1)
+ quotableIndex = value.indexOf('=');
+
+ if (quotableIndex >= 0) {
+ buffer.append('\'').append(value).append('\'');
+ } else {
+ buffer.append(value);
+ }
+ }
+ return buffer.toString();
+ }
+}
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/SimpleListConverter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/SimpleListConverter.java
new file mode 100644
index 0000000..2291be3
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/SimpleListConverter.java
@@ -0,0 +1,41 @@
+package aQute.bnd.build.model.conversions;
+
+import java.util.*;
+
+import aQute.lib.osgi.*;
+import aQute.libg.qtokens.*;
+
+public class SimpleListConverter<R> implements Converter<List<R>,String> {
+
+ private Converter< ? extends R, ? super String> itemConverter;
+
+ public static <R> Converter<List<R>,String> create(Converter<R, ? super String> itemConverter) {
+ return new SimpleListConverter<R>(itemConverter);
+ }
+
+ public static Converter<List<String>,String> create() {
+ return new SimpleListConverter<String>(new NoopConverter<String>());
+ }
+
+ private SimpleListConverter(Converter< ? extends R, ? super String> itemConverter) {
+ this.itemConverter = itemConverter;
+ }
+
+ public List<R> convert(String input) throws IllegalArgumentException {
+ List<R> result = new ArrayList<R>();
+
+ if (Constants.EMPTY_HEADER.equalsIgnoreCase(input.trim()))
+ return result;
+
+ QuotedTokenizer qt = new QuotedTokenizer(input, ",");
+ String token = qt.nextToken();
+
+ while (token != null) {
+ result.add(itemConverter.convert(token.trim()));
+ token = qt.nextToken();
+ }
+
+ return result;
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/StringEntryConverter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/StringEntryConverter.java
new file mode 100644
index 0000000..c857ddc
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/StringEntryConverter.java
@@ -0,0 +1,11 @@
+package aQute.bnd.build.model.conversions;
+
+import java.util.Map.Entry;
+
+public class StringEntryConverter implements Converter<String,Entry<String, ? >> {
+
+ public String convert(Entry<String, ? > input) throws IllegalArgumentException {
+ return input.getKey();
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/VersionedClauseConverter.java b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/VersionedClauseConverter.java
new file mode 100644
index 0000000..5aead1c
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/VersionedClauseConverter.java
@@ -0,0 +1,11 @@
+package aQute.bnd.build.model.conversions;
+
+import aQute.bnd.build.model.clauses.*;
+import aQute.libg.header.*;
+import aQute.libg.tuple.*;
+
+public class VersionedClauseConverter implements Converter<VersionedClause,Pair<String,Attrs>> {
+ public VersionedClause convert(Pair<String,Attrs> input) throws IllegalArgumentException {
+ return new VersionedClause(input.getFirst(), input.getSecond());
+ }
+}
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/packageinfo b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/packageinfo
new file mode 100644
index 0000000..55af8e5
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/conversions/packageinfo
@@ -0,0 +1 @@
+version 2
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/packageinfo b/bundleplugin/src/main/java/aQute/bnd/build/model/packageinfo
new file mode 100644
index 0000000..55af8e5
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/packageinfo
@@ -0,0 +1 @@
+version 2
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/packageinfo b/bundleplugin/src/main/java/aQute/bnd/build/packageinfo
index 5035fd2..0ff7674 100644
--- a/bundleplugin/src/main/java/aQute/bnd/build/packageinfo
+++ b/bundleplugin/src/main/java/aQute/bnd/build/packageinfo
@@ -1 +1 @@
-version 1.44.0
+version 1.45.0