Update to latest bndlib. (FELIX-2176)


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1027238 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundleplugin/pom.xml b/bundleplugin/pom.xml
index 5181806..20c4c5d 100644
--- a/bundleplugin/pom.xml
+++ b/bundleplugin/pom.xml
@@ -56,7 +56,7 @@
   <dependency>
     <groupId>biz.aQute</groupId>
     <artifactId>bndlib</artifactId>
-    <version>0.0.357</version>
+    <version>1.10.0</version>
   </dependency>
   <dependency>
     <groupId>net.sf.kxml</groupId>
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java b/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java
index cb990a1..11c8863 100644
--- a/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java
@@ -3,1132 +3,1113 @@
 import java.io.*;
 import java.util.*;
 import java.util.jar.*;
-import java.util.regex.*;
 import java.util.zip.*;
 
 import aQute.bnd.make.*;
+import aQute.bnd.maven.*;
 import aQute.bnd.service.*;
 
 /**
  * Include-Resource: ( [name '=' ] file )+
- * 
+ *
  * Private-Package: package-decl ( ',' package-decl )*
- * 
+ *
  * Export-Package: package-decl ( ',' package-decl )*
- * 
+ *
  * Import-Package: package-decl ( ',' package-decl )*
- * 
- * @version $Revision: 1.20 $
+ *
+ * @version $Revision: 1.27 $
  */
 public class Builder extends Analyzer {
-    private static final int    SPLIT_MERGE_LAST  = 1;
-    private static final int    SPLIT_MERGE_FIRST = 2;
-    private static final int    SPLIT_ERROR       = 3;
-    private static final int    SPLIT_FIRST       = 4;
-    private static final int    SPLIT_DEFAULT     = 0;
+	private static final int	SPLIT_MERGE_LAST	= 1;
+	private static final int	SPLIT_MERGE_FIRST	= 2;
+	private static final int	SPLIT_ERROR			= 3;
+	private static final int	SPLIT_FIRST			= 4;
+	private static final int	SPLIT_DEFAULT		= 0;
 
-    private static final File[] EMPTY_FILE        = new File[0];
+	List<File>					sourcePath			= new ArrayList<File>();
 
-    List<File>                  sourcePath        = new ArrayList<File>();
-    Pattern                     NAME_URL          = Pattern
-                                                          .compile("(.*)(http://.*)");
+	Make						make				= new Make(this);
 
-    Make                        make              = new Make(this);
+	public Builder(Processor parent) {
+		super(parent);
+	}
 
-    public Builder(Processor parent) {
-        super(parent);
-    }
+	public Builder() {
+	}
 
-    public Builder() {
+    public Map<String, Clazz> analyzeBundleClasspath(Jar dot,
+			Map<String, Map<String, String>> bundleClasspath,
+			Map<String, Map<String, String>> contained,
+			Map<String, Map<String, String>> referred,
+			Map<String, Set<String>> uses) throws IOException {
+        return super.analyzeBundleClasspath(dot, bundleClasspath, contained, referred, uses);
     }
 
     public Jar build() throws Exception {
-        if (getProperty(NOPE) != null)
-            return null;
+		init();
+		if (isTrue(getProperty(NOBUNDLES)))
+			return null;
 
-        String sub = getProperty(SUB);
-        if (sub != null && sub.trim().length() > 0)
-            error("Specified "
-                    + SUB
-                    + " but calls build() instead of builds() (might be a programmer error)");
+		if (getProperty(CONDUIT) != null)
+			error("Specified " + CONDUIT
+					+ " but calls build() instead of builds() (might be a programmer error");
 
-        if (getProperty(CONDUIT) != null)
-            error("Specified "
-                    + CONDUIT
-                    + " but calls build() instead of builds() (might be a programmer error");
+		dot = new Jar("dot");
+		addClose(dot);
+		try {
+			long modified = Long.parseLong(getProperty("base.modified"));
+			dot.updateModified(modified, "Base modified");
+		} catch (Exception e) {
+		}
 
-        dot = new Jar("dot");
-        addClose(dot);
-        try {
-            long modified = Long.parseLong(getProperty("base.modified"));
-            dot.updateModified(modified, "Base modified");
-        } catch (Exception e) {
-        }
+		doExpand(dot);
+		doIncludeResources(dot);
+		doConditional(dot);
+		dot = doWab(dot);
 
-        doExpand(dot);
-        doIncludeResources(dot);
+		// NEW!
+		// Check if we override the calculation of the
+		// manifest. We still need to calculated it because
+		// we need to have analyzed the classpath.
 
-        doConditional(dot);
+		Manifest manifest = calcManifest();
 
-        // NEW!
-        // Check if we override the calculation of the
-        // manifest. We still need to calculated it because
-        // we need to have analyzed the classpath.
+		String mf = getProperty(MANIFEST);
+		if (mf != null) {
+			File mff = getFile(mf);
+			if (mff.isFile()) {
+				try {
+					InputStream in = new FileInputStream(mff);
+					manifest = new Manifest(in);
+					in.close();
+				} catch (Exception e) {
+					error(MANIFEST + " while reading manifest file", e);
+				}
+			} else {
+				error(MANIFEST + ", no such file " + mf);
+			}
+		}
 
-        Manifest manifest = calcManifest();
+		if (getProperty(NOMANIFEST) == null)
+			dot.setManifest(manifest);
+		else
+			dot.setDoNotTouchManifest();
 
-        String mf = getProperty(MANIFEST);
-        if (mf != null) {
-            File mff = getFile(mf);
-            if (mff.isFile()) {
-                try {
-                    InputStream in = new FileInputStream(mff);
-                    manifest = new Manifest(in);
-                    in.close();
-                } catch (Exception e) {
-                    error(MANIFEST + " while reading manifest file", e);
-                }
-            } else {
-                error(MANIFEST + ", no such file " + mf);
-            }
-        }
+		// This must happen after we analyzed so
+		// we know what it is on the classpath
+		addSources(dot);
 
-        if (getProperty(NOMANIFEST) == null)
-            dot.setManifest(manifest);
-        else
-            dot.setNoManifest(true);
+		if (getProperty(POM) != null)
+			dot.putResource("pom.xml", new PomResource(dot.getManifest()));
 
-        // This must happen after we analyzed so
-        // we know what it is on the classpath
-        addSources(dot);
-        if (getProperty(POM) != null)
-            doPom(dot);
+		if (!isNoBundle())
+			doVerify(dot);
 
-        doVerify(dot);
+		if (dot.getResources().isEmpty())
+			error("The JAR is empty: " + dot.getName());
 
-        if (dot.getResources().isEmpty())
-            error("The JAR is empty");
+		dot.updateModified(lastModified(), "Last Modified Processor");
+		dot.setName(getBsn());
 
-        dot.updateModified(lastModified(), "Last Modified Processor");
-        dot.setName(getBsn());
+		sign(dot);
 
-        sign(dot);
-        return dot;
-    }
+		doSaveManifest(dot);
+		return dot;
+	}
 
-    /**
-     * Sign the jar file.
-     * 
-     * -sign : <alias> [ ';' 'password:=' <password> ] [ ';' 'keystore:='
-     * <keystore> ] [ ';' 'sign-password:=' <pw> ] ( ',' ... )*
-     * 
-     * @return
+	/**
+	 * Allow any local initialization by subclasses before we build. Default is
+	 * do nothing.
+	 */
+	public void init() throws Exception {
+
+	}
+
+	/**
+	 * Turn this normal bundle in a web and add any resources.
+	 *
+	 * @throws Exception
+	 */
+	private Jar doWab(Jar dot) throws Exception {
+		String wab = getProperty(WAB);
+		String wablib = getProperty(WABLIB);
+		if (wab == null && wablib == null)
+			return dot;
+
+		setProperty(BUNDLE_CLASSPATH, append("WEB-INF/classes", getProperty(BUNDLE_CLASSPATH)));
+
+		Jar next = new Jar(dot.getName());
+		addClose(next);
+
+		for (Map.Entry<String, Resource> entry : dot.getResources().entrySet()) {
+			String path = entry.getKey();
+			if (path.indexOf('/') > 0 && !Character.isUpperCase(path.charAt(0))) {
+				trace("wab: moving: %s", path);
+				next.putResource("WEB-INF/classes/" + path, entry.getValue());
+			} else {
+				trace("wab: not moving: %s", path);
+				next.putResource(path, entry.getValue());
+			}
+		}
+
+		Map<String, Map<String, String>> clauses = parseHeader(getProperty(WABLIB));
+		for (String key : clauses.keySet()) {
+			File f = getFile(key);
+			addWabLib(next, f);
+		}
+		doIncludeResource(next, wab);
+		return next;
+	}
+
+	/**
+	 * Add a wab lib to the jar.
+	 *
+	 * @param f
+	 */
+	private void addWabLib(Jar dot, File f) throws Exception {
+		if (f.exists()) {
+			Jar jar = new Jar(f);
+			jar.setDoNotTouchManifest();
+			addClose(jar);
+			String path = "WEB-INF/lib/" + f.getName();
+			dot.putResource(path, new JarResource(jar));
+			setProperty(BUNDLE_CLASSPATH, append(getProperty(BUNDLE_CLASSPATH), path));
+
+			Manifest m = jar.getManifest();
+			String cp = m.getMainAttributes().getValue("Class-Path");
+			if (cp != null) {
+				Collection<String> parts = split(cp, ",");
+				for (String part : parts) {
+					File sub = getFile(f.getParentFile(), part);
+					if (!sub.exists() || !sub.getParentFile().equals(f.getParentFile())) {
+						warning(
+								"Invalid Class-Path entry %s in %s, must exist and must reside in same directory",
+								sub, f);
+					} else {
+						addWabLib(dot, sub);
+					}
+				}
+			}
+		} else {
+			error("WAB lib does not exist %s", f);
+		}
+	}
+
+	/**
+	 * Get the manifest and write it out separately if -savemanifest is set
+	 *
+	 * @param dot
+	 */
+	private void doSaveManifest(Jar dot) throws IOException {
+		String output = getProperty(SAVEMANIFEST);
+		if (output == null)
+			return;
+
+		File f = getFile(output);
+		if (f.isDirectory()) {
+			f = new File(f, "MANIFEST.MF");
+		}
+		f.delete();
+		f.getParentFile().mkdirs();
+		OutputStream out = new FileOutputStream(f);
+		try {
+			Jar.writeManifest(dot.getManifest(), out);
+		} finally {
+			out.close();
+		}
+		changedFile(f);
+	}
+
+	protected void changedFile(File f) {
+	}
+
+	/**
+	 * Sign the jar file.
+	 *
+	 * -sign : <alias> [ ';' 'password:=' <password> ] [ ';' 'keystore:='
+	 * <keystore> ] [ ';' 'sign-password:=' <pw> ] ( ',' ... )*
+	 *
+	 * @return
+	 */
+
+	void sign(Jar jar) throws Exception {
+		String signing = getProperty("-sign");
+		if (signing == null)
+			return;
+
+		trace("Signing %s, with %s", getBsn(), signing);
+		List<SignerPlugin> signers = getPlugins(SignerPlugin.class);
+
+		Map<String, Map<String, String>> infos = parseHeader(signing);
+		for (Map.Entry<String, Map<String, String>> entry : infos.entrySet()) {
+			for (SignerPlugin signer : signers) {
+				signer.sign(this, entry.getKey());
+			}
+		}
+	}
+
+	public boolean hasSources() {
+		return isTrue(getProperty(SOURCES));
+	}
+
+	protected String getImportPackages() {
+		String ip = super.getImportPackages();
+		if (ip != null)
+			return ip;
+
+		return "*";
+	}
+
+	private void doConditional(Jar dot) throws Exception {
+		Map<String, Map<String, String>> conditionals = getHeader(CONDITIONAL_PACKAGE);
+		if (conditionals.isEmpty())
+			return;
+
+		int size;
+		do {
+			size = dot.getDirectories().size();
+			analyze();
+			analyzed = false;
+			Map<String, Map<String, String>> imports = getImports();
+
+			// Match the packages specified in conditionals
+			// against the imports. Any match must become a
+			// Private-Package
+			Map<String, Map<String, String>> filtered = merge(CONDITIONAL_PACKAGE, conditionals,
+					imports, new HashSet<String>(), null);
+
+			// Imports can also specify a private import. These
+			// packages must also be copied to the bundle
+			for (Map.Entry<String, Map<String, String>> entry : getImports().entrySet()) {
+				String type = entry.getValue().get(IMPORT_DIRECTIVE);
+				if (type != null && type.equals("private"))
+					filtered.put(entry.getKey(), entry.getValue());
+			}
+
+			// remove existing packages to prevent merge errors
+			filtered.keySet().removeAll(dot.getPackages());
+			doExpand(dot, CONDITIONAL_PACKAGE + " Private imports", Instruction
+					.replaceWithInstruction(filtered), false);
+		} while (dot.getDirectories().size() > size);
+		analyzed = true;
+	}
+
+	/**
+	 * Intercept the call to analyze and cleanup versions after we have analyzed
+	 * the setup. We do not want to cleanup if we are going to verify.
+	 */
+
+	public void analyze() throws Exception {
+		super.analyze();
+		cleanupVersion(imports);
+		cleanupVersion(exports);
+		String version = getProperty(BUNDLE_VERSION);
+		if (version != null)
+			setProperty(BUNDLE_VERSION, cleanupVersion(version));
+	}
+
+	public void cleanupVersion(Map<String, Map<String, String>> mapOfMap) {
+		for (Iterator<Map.Entry<String, Map<String, String>>> e = mapOfMap.entrySet().iterator(); e
+				.hasNext();) {
+			Map.Entry<String, Map<String, String>> entry = e.next();
+			Map<String, String> attributes = entry.getValue();
+			if (attributes.containsKey("version")) {
+				attributes.put("version", cleanupVersion(attributes.get("version")));
+			}
+		}
+	}
+
+	/**
+     *
      */
+	private void addSources(Jar dot) {
+		if (!hasSources())
+			return;
 
-    void sign(Jar jar) throws Exception {
-        String signing = getProperty("-sign");
-        if (signing == null)
-            return;
+		Set<String> packages = new HashSet<String>();
 
-        trace("Signing %s, with %s", getBsn(), signing);
-        List<SignerPlugin> signers = getPlugins(SignerPlugin.class);
+		try {
+			ByteArrayOutputStream out = new ByteArrayOutputStream();
+			getProperties().store(out, "Generated by BND, at " + new Date());
+			dot.putResource("OSGI-OPT/bnd.bnd", new EmbeddedResource(out.toByteArray(), 0));
+			out.close();
+		} catch (Exception e) {
+			error("Can not embed bnd file in JAR: " + e);
+		}
 
-        Map<String, Map<String, String>> infos = parseHeader(signing);
-        for (Map.Entry<String, Map<String, String>> entry : infos.entrySet()) {
-            for (SignerPlugin signer : signers) {
-                signer.sign(this, entry.getKey());
-            }
-        }
-    }
+		for (Iterator<String> cpe = classspace.keySet().iterator(); cpe.hasNext();) {
+			String path = cpe.next();
+			path = path.substring(0, path.length() - ".class".length()) + ".java";
+			String pack = getPackage(path).replace('.', '/');
+			if (pack.length() > 1)
+				pack = pack + "/";
+			boolean found = false;
+			String[] fixed = { "packageinfo", "package.html", "module-info.java",
+					"package-info.java" };
+			for (Iterator<File> i = getSourcePath().iterator(); i.hasNext();) {
+				File root = i.next();
+				File f = getFile(root, path);
+				if (f.exists()) {
+					found = true;
+					if (!packages.contains(pack)) {
+						packages.add(pack);
+						File bdir = getFile(root, pack);
+						for (int j = 0; j < fixed.length; j++) {
+							File ff = getFile(bdir, fixed[j]);
+							if (ff.isFile()) {
+								dot.putResource("OSGI-OPT/src/" + pack + fixed[j],
+										new FileResource(ff));
+							}
+						}
+					}
+					dot.putResource("OSGI-OPT/src/" + path, new FileResource(f));
+				}
+			}
+			if (!found) {
+				for (Jar jar : classpath) {
+					Resource resource = jar.getResource(path);
+					if (resource != null) {
+						dot.putResource("OSGI-OPT/src", resource);
+					} else {
+						resource = jar.getResource("OSGI-OPT/src/" + path);
+						if (resource != null) {
+							dot.putResource("OSGI-OPT/src", resource);
+						}
+					}
+				}
+			}
+			if (getSourcePath().isEmpty())
+				warning("Including sources but " + SOURCEPATH
+						+ " does not contain any source directories ");
+			// TODO copy from the jars where they came from
+		}
+	}
 
-    public boolean hasSources() {
-        return isTrue(getProperty(SOURCES));
-    }
+	boolean	firstUse	= true;
 
-    protected String getImportPackages() {
-        String ip = super.getImportPackages();
-        if (ip != null)
-            return ip;
+	public Collection<File> getSourcePath() {
+		if (firstUse) {
+			firstUse = false;
+			String sp = getProperty(SOURCEPATH);
+			if (sp != null) {
+				Map<String, Map<String, String>> map = parseHeader(sp);
+				for (Iterator<String> i = map.keySet().iterator(); i.hasNext();) {
+					String file = i.next();
+					if (!isDuplicate(file)) {
+						File f = getFile(file);
+						if (!f.isDirectory()) {
+							error("Adding a sourcepath that is not a directory: " + f);
+						} else {
+							sourcePath.add(f);
+						}
+					}
+				}
+			}
+		}
+		return sourcePath;
+	}
 
-        return "*";
-    }
+	private void doVerify(Jar dot) throws Exception {
+		Verifier verifier = new Verifier(dot, getProperties());
+		verifier.setPedantic(isPedantic());
 
-    private void doConditional(Jar dot) throws IOException {
-        Map<String, Map<String, String>> conditionals = getHeader(CONDITIONAL_PACKAGE);
-        if ( conditionals.isEmpty() )
-            return;
-        
-        int size;
-        do {
-            size = dot.getDirectories().size();
-            analyze();
-            analyzed = false;
-            Map<String, Map<String, String>> imports = getImports();
+		// Give the verifier the benefit of our analysis
+		// prevents parsing the files twice
+		verifier.setClassSpace(classspace, contained, referred, uses);
+		verifier.verify();
+		getInfo(verifier);
+	}
 
-            // Match the packages specified in conditionals
-            // against the imports. Any match must become a
-            // Private-Package
-            Map<String, Map<String, String>> filtered = merge(
-                    CONDITIONAL_PACKAGE, conditionals, imports,
-                    new HashSet<String>(), null);
+	private void doExpand(Jar jar) throws IOException {
+		if (getClasspath().size() == 0
+				&& (getProperty(EXPORT_PACKAGE) != null || getProperty(EXPORT_PACKAGE) != null || getProperty(PRIVATE_PACKAGE) != null))
+			warning("Classpath is empty. Private-Package and Export-Package can only expand from the classpath when there is one");
 
-            // Imports can also specify a private import. These
-            // packages must also be copied to the bundle
-            for (Map.Entry<String, Map<String, String>> entry : getImports()
-                    .entrySet()) {
-                String type = entry.getValue().get(IMPORT_DIRECTIVE);
-                if (type != null && type.equals("private"))
-                    filtered.put(entry.getKey(), entry.getValue());
-            }
+		Map<Instruction, Map<String, String>> privateMap = Instruction
+				.replaceWithInstruction(getHeader(PRIVATE_PACKAGE));
+		Map<Instruction, Map<String, String>> exportMap = Instruction
+				.replaceWithInstruction(getHeader(EXPORT_PACKAGE));
 
-            // remove existing packages to prevent merge errors
-            filtered.keySet().removeAll(dot.getPackages());
-            doExpand(dot, CONDITIONAL_PACKAGE + " Private imports",
-                    replaceWitInstruction(filtered, CONDITIONAL_PACKAGE), false);
-        } while (dot.getDirectories().size() > size);
-        analyzed = true;
-    }
+		if (isTrue(getProperty(Constants.UNDERTEST))) {
+			privateMap.putAll(Instruction.replaceWithInstruction(parseHeader(getProperty(
+					Constants.TESTPACKAGES, "test;presence:=optional"))));
+		}
+		if (!privateMap.isEmpty())
+			doExpand(jar, "Private-Package, or -testpackages", privateMap, true);
 
-    /**
-     * Intercept the call to analyze and cleanup versions after we have analyzed
-     * the setup. We do not want to cleanup if we are going to verify.
+		if (!exportMap.isEmpty() ) {
+			Jar exports = new Jar("exports");
+			doExpand(exports, EXPORT_PACKAGE, exportMap, true);
+			jar.addAll(exports);
+			exports.close();
+		}
+
+		if (!isNoBundle()) {
+			if (privateMap.isEmpty() && exportMap.isEmpty() && !isResourceOnly()
+					&& getProperty(EXPORT_CONTENTS) == null) {
+				warning("None of Export-Package, Provide-Package, Private-Package, -testpackages, or -exportcontents is set, therefore no packages will be included");
+			}
+		}
+	}
+
+	/**
+	 *
+	 * @param jar
+	 * @param name
+	 * @param instructions
+	 */
+	private void doExpand(Jar jar, String name, Map<Instruction, Map<String, String>> instructions,
+			boolean mandatory) {
+		Set<Instruction> superfluous = removeMarkedDuplicates(instructions.keySet());
+
+		for (Iterator<Jar> c = getClasspath().iterator(); c.hasNext();) {
+			Jar now = c.next();
+			doExpand(jar, instructions, now, superfluous);
+		}
+
+		if (mandatory && superfluous.size() > 0) {
+			StringBuilder sb = new StringBuilder();
+			String del = "Instructions in " + name + " that are never used: ";
+			for (Iterator<Instruction> i = superfluous.iterator(); i.hasNext();) {
+				Instruction p = i.next();
+				sb.append(del);
+				sb.append(p.toString());
+				del = "\n                ";
+			}
+			sb.append("\nClasspath: ");
+			sb.append(Processor.join(getClasspath()));
+			sb.append("\n");
+
+			warning(sb.toString());
+			if (isPedantic())
+				diagnostics = true;
+		}
+	}
+
+	/**
+	 * Iterate over each directory in the class path entry and check if that
+	 * directory is a desired package.
+	 *
+	 * @param included
+	 * @param classpathEntry
+	 */
+	private void doExpand(Jar jar, Map<Instruction, Map<String, String>> included,
+			Jar classpathEntry, Set<Instruction> superfluous) {
+
+		loop: for (Map.Entry<String, Map<String, Resource>> directory : classpathEntry
+				.getDirectories().entrySet()) {
+			String path = directory.getKey();
+
+			if (doNotCopy.matcher(getName(path)).matches())
+				continue;
+
+			if (directory.getValue() == null)
+				continue;
+
+			String pack = path.replace('/', '.');
+			Instruction instr = matches(included.keySet(), pack, superfluous);
+			if (instr != null) {
+				// System.out.println("Pattern match: " + pack + " " +
+				// instr.getPattern() + " " + instr.isNegated());
+				if (!instr.isNegated()) {
+					Map<String, Resource> contents = directory.getValue();
+
+					// What to do with split packages? Well if this
+					// directory already exists, we will check the strategy
+					// and react accordingly.
+					boolean overwriteResource = true;
+					if (jar.hasDirectory(path)) {
+						Map<String, String> directives = included.get(instr);
+
+						switch (getSplitStrategy((String) directives.get(SPLIT_PACKAGE_DIRECTIVE))) {
+						case SPLIT_MERGE_LAST:
+							overwriteResource = true;
+							break;
+
+						case SPLIT_MERGE_FIRST:
+							overwriteResource = false;
+							break;
+
+						case SPLIT_ERROR:
+							error(diagnostic(pack, getClasspath(), classpathEntry.source));
+							continue loop;
+
+						case SPLIT_FIRST:
+							continue loop;
+
+						default:
+							warning(diagnostic(pack, getClasspath(), classpathEntry.source));
+							overwriteResource = false;
+							break;
+						}
+					}
+
+					jar.addDirectory(contents, overwriteResource);
+
+					String key = path + "/bnd.info";
+					Resource r = jar.getResource(key);
+					if (r != null)
+						jar.putResource(key, new PreprocessResource(this, r));
+
+					if (hasSources()) {
+						String srcPath = "OSGI-OPT/src/" + path;
+						Map<String, Resource> srcContents = classpathEntry.getDirectories().get(
+								srcPath);
+						if (srcContents != null) {
+							jar.addDirectory(srcContents, overwriteResource);
+						}
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * Analyze the classpath for a split package
+	 *
+	 * @param pack
+	 * @param classpath
+	 * @param source
+	 * @return
+	 */
+	private String diagnostic(String pack, List<Jar> classpath, File source) {
+		// Default is like merge-first, but with a warning
+		// Find the culprits
+		pack = pack.replace('.', '/');
+		List<Jar> culprits = new ArrayList<Jar>();
+		for (Iterator<Jar> i = classpath.iterator(); i.hasNext();) {
+			Jar culprit = (Jar) i.next();
+			if (culprit.getDirectories().containsKey(pack)) {
+				culprits.add(culprit);
+			}
+		}
+		return "Split package "
+				+ pack
+				+ "\nUse directive -split-package:=(merge-first|merge-last|error|first) on Export/Private Package instruction to get rid of this warning\n"
+				+ "Package found in   " + culprits + "\n" + "Reference from     " + source + "\n"
+				+ "Classpath          " + classpath;
+	}
+
+	private int getSplitStrategy(String type) {
+		if (type == null)
+			return SPLIT_DEFAULT;
+
+		if (type.equals("merge-last"))
+			return SPLIT_MERGE_LAST;
+
+		if (type.equals("merge-first"))
+			return SPLIT_MERGE_FIRST;
+
+		if (type.equals("error"))
+			return SPLIT_ERROR;
+
+		if (type.equals("first"))
+			return SPLIT_FIRST;
+
+		error("Invalid strategy for split-package: " + type);
+		return SPLIT_DEFAULT;
+	}
+
+	private Instruction matches(Collection<Instruction> instructions, String pack,
+			Set<Instruction> superfluousPatterns) {
+		for (Instruction pattern : instructions) {
+			if (pattern.matches(pack)) {
+				if (superfluousPatterns != null)
+					superfluousPatterns.remove(pattern);
+				return pattern;
+			}
+		}
+		return null;
+	}
+
+	private Map<String, Map<String, String>> getHeader(String string) {
+		if (string == null)
+			return Collections.emptyMap();
+		return parseHeader(getProperty(string));
+	}
+
+	/**
+	 * Parse the Bundle-Includes header. Files in the bundles Include header are
+	 * included in the jar. The source can be a directory or a file.
+	 *
+	 * @throws IOException
+	 * @throws FileNotFoundException
+	 */
+	private void doIncludeResources(Jar jar) throws Exception {
+		String includes = getProperty("Bundle-Includes");
+		if (includes == null) {
+			includes = getProperty(INCLUDERESOURCE);
+			if (includes == null || includes.length() == 0)
+				includes = getProperty("Include-Resource");
+		} else
+			warning("Please use -includeresource instead of Bundle-Includes");
+
+		doIncludeResource(jar, includes);
+
+	}
+
+	private void doIncludeResource(Jar jar, String includes) throws Exception {
+		Map<String, Map<String, String>> clauses = parseHeader(includes);
+		doIncludeResource(jar, clauses);
+	}
+
+	private void doIncludeResource(Jar jar, Map<String, Map<String, String>> clauses)
+			throws ZipException, IOException, Exception {
+		for (Map.Entry<String, Map<String, String>> entry : clauses.entrySet()) {
+			doIncludeResource(jar, entry.getKey(), entry.getValue());
+		}
+	}
+
+	private void doIncludeResource(Jar jar, String name, Map<String, String> extra)
+			throws ZipException, IOException, Exception {
+		boolean preprocess = false;
+		if (name.startsWith("{") && name.endsWith("}")) {
+			preprocess = true;
+			name = name.substring(1, name.length() - 1).trim();
+		}
+
+		String parts[] = name.split("\\s*=\\s*");
+		String source = parts[0];
+		String destination = parts[0];
+		if (parts.length == 2)
+			source = parts[1];
+
+		if (source.startsWith("@")) {
+			extractFromJar(jar, source.substring(1), parts.length == 1 ? "" : destination);
+		} else if (extra.containsKey("literal")) {
+			String literal = (String) extra.get("literal");
+			Resource r = new EmbeddedResource(literal.getBytes("UTF-8"), 0);
+			String x = (String) extra.get("extra");
+			if (x != null)
+				r.setExtra(x);
+			jar.putResource(name, r);
+		} else {
+			File sourceFile;
+			String destinationPath;
+
+			sourceFile = getFile(source);
+			if (parts.length == 1) {
+				// Directories should be copied to the root
+				// but files to their file name ...
+				if (sourceFile.isDirectory())
+					destinationPath = "";
+				else
+					destinationPath = sourceFile.getName();
+			} else {
+				destinationPath = parts[0];
+			}
+			// Handle directories
+			if (sourceFile.isDirectory()) {
+				destinationPath = doResourceDirectory(jar, extra, preprocess, sourceFile,
+						destinationPath);
+				return;
+			}
+
+			// destinationPath = checkDestinationPath(destinationPath);
+
+			if (!sourceFile.exists()) {
+				noSuchFile(jar, name, extra, source, destinationPath);
+			} else
+				copy(jar, destinationPath, sourceFile, preprocess, extra);
+		}
+	}
+
+	private String doResourceDirectory(Jar jar, Map<String, String> extra, boolean preprocess,
+			File sourceFile, String destinationPath) throws Exception {
+		String filter = extra.get("filter:");
+		boolean flatten = isTrue(extra.get("flatten:"));
+		boolean recursive = true;
+		String directive = extra.get("recursive:");
+		if (directive != null) {
+			recursive = isTrue(directive);
+		}
+
+		InstructionFilter iFilter = null;
+		if (filter != null) {
+			iFilter = new InstructionFilter(Instruction.getPattern(filter), recursive);
+		} else {
+			iFilter = new InstructionFilter(null, recursive);
+		}
+
+		Map<String, File> files = newMap();
+		resolveFiles(sourceFile, iFilter, recursive, destinationPath, files, flatten);
+
+		for (Map.Entry<String, File> entry : files.entrySet()) {
+			copy(jar, entry.getKey(), entry.getValue(), preprocess, extra);
+		}
+		return destinationPath;
+	}
+
+	private void resolveFiles(File dir, FileFilter filter, boolean recursive, String path,
+			Map<String, File> files, boolean flatten) {
+
+		if (Analyzer.doNotCopy.matcher(dir.getName()).matches()) {
+			return;
+		}
+
+		File[] fs = dir.listFiles(filter);
+		for (File file : fs) {
+			if (file.isDirectory()) {
+				if (recursive) {
+					String nextPath;
+					if (flatten)
+						nextPath = path;
+					else
+						nextPath = appendPath(path, file.getName());
+
+					resolveFiles(file, filter, recursive, nextPath, files, flatten);
+				}
+				// Directories are ignored otherwise
+			} else {
+				String p = appendPath(path, file.getName());
+				if (files.containsKey(p))
+					warning("Include-Resource overwrites entry %s from file %s", p, file);
+				files.put(p, file);
+			}
+		}
+	}
+
+	private void noSuchFile(Jar jar, String clause, Map<String, String> extra, String source,
+			String destinationPath) throws Exception {
+		Jar src = getJarFromName(source, "Include-Resource " + source);
+		if (src != null) {
+			JarResource jarResource = new JarResource(src);
+			jar.putResource(destinationPath, jarResource);
+		} else {
+			Resource lastChance = make.process(source);
+			if (lastChance != null) {
+				String x = extra.get("extra");
+				if (x != null)
+					lastChance.setExtra(x);
+				jar.putResource(destinationPath, lastChance);
+			} else
+				error("Input file does not exist: " + source);
+		}
+	}
+
+	/**
+	 * Extra resources from a Jar and add them to the given jar. The clause is
+	 * the
+	 *
+	 * @param jar
+	 * @param clauses
+	 * @param i
+	 * @throws ZipException
+	 * @throws IOException
+	 */
+	private void extractFromJar(Jar jar, String source, String destination) throws ZipException,
+			IOException {
+		// Inline all resources and classes from another jar
+		// optionally appended with a modified regular expression
+		// like @zip.jar!/META-INF/MANIFEST.MF
+		int n = source.lastIndexOf("!/");
+		Instruction instr = null;
+		if (n > 0) {
+			instr = Instruction.getPattern(source.substring(n + 2));
+			source = source.substring(0, n);
+		}
+
+		// Pattern filter = null;
+		// if (n > 0) {
+		// String fstring = source.substring(n + 2);
+		// source = source.substring(0, n);
+		// filter = wildcard(fstring);
+		// }
+		Jar sub = getJarFromName(source, "extract from jar");
+		if (sub == null)
+			error("Can not find JAR file " + source);
+		else {
+			jar.addAll(sub, instr, destination);
+		}
+	}
+
+	private void copy(Jar jar, String path, File from, boolean preprocess, Map<String, String> extra)
+			throws Exception {
+		if (doNotCopy.matcher(from.getName()).matches())
+			return;
+
+		if (from.isDirectory()) {
+
+			File files[] = from.listFiles();
+			for (int i = 0; i < files.length; i++) {
+				copy(jar, appendPath(path, files[i].getName()), files[i], preprocess, extra);
+			}
+		} else {
+			if (from.exists()) {
+				Resource resource = new FileResource(from);
+				if (preprocess) {
+					resource = new PreprocessResource(this, resource);
+				}
+				String x = extra.get("extra");
+				if (x != null)
+					resource.setExtra(x);
+				if (path.endsWith("/"))
+					path = path + from.getName();
+				jar.putResource(path, resource);
+
+				if (isTrue(extra.get(LIB_DIRECTIVE))) {
+					setProperty(BUNDLE_CLASSPATH, append(getProperty(BUNDLE_CLASSPATH), path));
+				}
+			} else {
+				error("Input file does not exist: " + from);
+			}
+		}
+	}
+
+	private String getName(String where) {
+		int n = where.lastIndexOf('/');
+		if (n < 0)
+			return where;
+
+		return where.substring(n + 1);
+	}
+
+	public void setSourcepath(File[] files) {
+		for (int i = 0; i < files.length; i++)
+			addSourcepath(files[i]);
+	}
+
+	public void addSourcepath(File cp) {
+		if (!cp.exists())
+			warning("File on sourcepath that does not exist: " + cp);
+
+		sourcePath.add(cp);
+	}
+
+	public void close() {
+		super.close();
+	}
+
+	/**
+	 * Build Multiple jars. If the -sub command is set, we filter the file with
+	 * the given patterns.
+	 *
+	 * @return
+	 * @throws Exception
+	 */
+	public Jar[] builds() throws Exception {
+		begin();
+
+		// Are we acting as a conduit for another JAR?
+		String conduit = getProperty(CONDUIT);
+		if (conduit != null) {
+			Map<String, Map<String, String>> map = parseHeader(conduit);
+			Jar[] result = new Jar[map.size()];
+			int n = 0;
+			for (String file : map.keySet()) {
+				Jar c = new Jar(getFile(file));
+				addClose(c);
+				String name = map.get(file).get("name");
+				if (name != null)
+					c.setName(name);
+
+				result[n++] = c;
+			}
+			return result;
+		}
+
+		List<Jar> result = new ArrayList<Jar>();
+		List<Builder> builders;
+
+		builders = getSubBuilders();
+
+		for (Builder builder : builders) {
+			try {
+				Jar jar = builder.build();
+				jar.setName(builder.getBsn());
+				result.add(jar);
+			} catch (Exception e) {
+				error("Sub Building " + builder.getBsn(), e);
+			}
+			if (builder != this)
+				getInfo(builder, builder.getBsn() + ": ");
+		}
+		return result.toArray(new Jar[result.size()]);
+	}
+
+	/**
+	 * Answer a list of builders that represent this file or a list of files
+	 * specified in -sub. This list can be empty. These builders represents to
+	 * be created artifacts and are each scoped to such an artifacts. The
+	 * builders can be used to build the bundles or they can be used to find out
+	 * information about the to be generated bundles.
+	 *
+	 * @return List of 0..n builders representing artifacts.
+	 * @throws Exception
+	 */
+	public List<Builder> getSubBuilders() throws Exception {
+		String sub = (String) getProperty(SUB);
+		if (sub == null || sub.trim().length() == 0 || EMPTY_HEADER.equals(sub))
+			return Arrays.asList(this);
+
+		List<Builder> builders = new ArrayList<Builder>();
+		if (isTrue(getProperty(NOBUNDLES)))
+			return builders;
+
+		Set<Instruction> subs = Instruction.replaceWithInstruction(parseHeader(sub)).keySet();
+
+		List<File> members = new ArrayList<File>(Arrays.asList(getBase().listFiles()));
+
+		nextFile: while (members.size() > 0) {
+
+			File file = members.remove(0);
+
+			// Check if the file is one of our parents
+			Processor p = this;
+			while (p != null) {
+				if (file.equals(p.getPropertiesFile()))
+					continue nextFile;
+				p = p.getParent();
+			}
+
+			// if
+			// (file.getCanonicalFile().equals(getPropertiesFile().getCanonicalFile()))
+			// continue nextFile;
+
+			for (Iterator<Instruction> i = subs.iterator(); i.hasNext();) {
+
+				Instruction instruction = i.next();
+				if (instruction.matches(file.getName())) {
+
+					if (!instruction.isNegated()) {
+
+						Builder builder = getSubBuilder();
+						if (builder != null) {
+							builder.setProperties(file);
+							addClose(builder);
+							builders.add(builder);
+						}
+					}
+
+					// Because we matched (even though we could be negated)
+					// we skip any remaining searches
+					continue nextFile;
+				}
+			}
+		}
+		return builders;
+	}
+
+	public Builder getSubBuilder() throws Exception {
+		Builder builder = new Builder(this);
+		builder.setBase(getBase());
+
+		for (Jar file : getClasspath()) {
+			builder.addClasspath(file);
+		}
+
+		return builder;
+	}
+
+	/**
+	 * A macro to convert a maven version to an OSGi version
+	 */
+
+	public String _maven_version(String args[]) {
+		if (args.length > 2)
+			error("${maven_version} macro receives too many arguments " + Arrays.toString(args));
+		else if (args.length < 2)
+			error("${maven_version} macro has no arguments, use ${maven_version;1.2.3-SNAPSHOT}");
+		else {
+			return cleanupVersion(args[1]);
+		}
+		return null;
+	}
+
+	public String _permissions(String args[]) throws IOException {
+		StringBuilder sb = new StringBuilder();
+
+		for (String arg : args) {
+			if ("packages".equals(arg) || "all".equals(arg)) {
+				for (String imp : getImports().keySet()) {
+					if (!imp.startsWith("java.")) {
+						sb.append("(org.osgi.framework.PackagePermission \"");
+						sb.append(imp);
+						sb.append("\" \"import\")\r\n");
+					}
+				}
+				for (String exp : getExports().keySet()) {
+					sb.append("(org.osgi.framework.PackagePermission \"");
+					sb.append(exp);
+					sb.append("\" \"export\")\r\n");
+				}
+			} else if ("admin".equals(arg) || "all".equals(arg)) {
+				sb.append("(org.osgi.framework.AdminPermission)");
+			} else if ("permissions".equals(arg))
+				;
+			else
+				error("Invalid option in ${permissions}: %s", arg);
+		}
+		return sb.toString();
+	}
+
+	/**
+     *
      */
+	public void removeBundleSpecificHeaders() {
+		Set<String> set = new HashSet<String>(Arrays.asList(BUNDLE_SPECIFIC_HEADERS));
+		setForceLocal(set);
+	}
 
-    public void analyze() throws IOException {
-        super.analyze();
-        cleanupVersion(imports);
-        cleanupVersion(exports);
-        String version = getProperty(BUNDLE_VERSION);
-        if (version != null)
-            setProperty(BUNDLE_VERSION, cleanupVersion(version));
-    }
+	/**
+	 * Check if the given resource is in scope of this bundle. That is, it
+	 * checks if the Include-Resource includes this resource or if it is a class
+	 * file it is on the class path and the Export-Pacakge or Private-Package
+	 * include this resource.
+	 *
+	 * For now, include resources are skipped.
+	 *
+	 * @param f
+	 * @return
+	 */
+	public boolean isInScope(Collection<File> resources) throws Exception {
+		Map<String, Map<String, String>> clauses = parseHeader(getProperty(Constants.EXPORT_PACKAGE));
+		clauses.putAll(parseHeader(getProperty(Constants.PRIVATE_PACKAGE)));
+		if (isTrue(getProperty(Constants.UNDERTEST))) {
+			clauses.putAll(parseHeader(getProperty(Constants.TESTPACKAGES,
+					"test;presence:=optional")));
+		}
+		Collection<Instruction> instructions = Instruction.replaceWithInstruction(clauses).keySet();
 
-    public void cleanupVersion(Map<String, Map<String, String>> mapOfMap) {
-        for (Iterator<Map.Entry<String, Map<String, String>>> e = mapOfMap
-                .entrySet().iterator(); e.hasNext();) {
-            Map.Entry<String, Map<String, String>> entry = e.next();
-            Map<String, String> attributes = entry.getValue();
-            if (attributes.containsKey("version")) {
-                attributes.put("version", cleanupVersion(attributes
-                        .get("version")));
-            }
-        }
-    }
+		for (File r : resources) {
+			String cpEntry = getClasspathEntrySuffix(r);
+			if (cpEntry != null) {
+				String pack = Clazz.getPackage(cpEntry);
+				Instruction i = matches(instructions, pack, null);
+				if (i != null)
+					return !i.isNegated();
+			}
+		}
+		return false;
+	}
 
-    /**
-     * 
-     */
-    private void addSources(Jar dot) {
-        if (!hasSources())
-            return;
+	/**
+	 * Answer the string of the resource that it has in the container.
+	 *
+	 * @param resource
+	 *            The resource to look for
+	 * @return
+	 * @throws Exception
+	 */
+	public String getClasspathEntrySuffix(File resource) throws Exception {
+		for (Jar jar : getClasspath()) {
+			File source = jar.getSource();
+			if (source != null) {
+				source = source.getCanonicalFile();
+				String sourcePath = source.getAbsolutePath();
+				String resourcePath = resource.getAbsolutePath();
 
-        Set<String> packages = new HashSet<String>();
+				if (resourcePath.startsWith(sourcePath)) {
+					// Make sure that the path name is translated correctly
+					// i.e. on Windows the \ must be translated to /
+					String filePath = resourcePath.substring(sourcePath.length() + 1);
 
-        try {
-            ByteArrayOutputStream out = new ByteArrayOutputStream();
-            getProperties().store(out, "Generated by BND, at " + new Date());
-            dot.putResource("OSGI-OPT/bnd.bnd", new EmbeddedResource(out
-                    .toByteArray(), 0));
-            out.close();
-        } catch (Exception e) {
-            error("Can not embed bnd file in JAR: " + e);
-        }
-
-        for (Iterator<String> cpe = classspace.keySet().iterator(); cpe
-                .hasNext();) {
-            String path = cpe.next();
-            path = path.substring(0, path.length() - ".class".length())
-                    + ".java";
-            String pack = getPackage(path).replace('.', '/');
-            if (pack.length() > 1)
-                pack = pack + "/";
-            boolean found = false;
-            String[] fixed = { "packageinfo", "package.html",
-                    "module-info.java", "package-info.java" };
-            for (Iterator<File> i = getSourcePath().iterator(); i.hasNext();) {
-                File root = i.next();
-                File f = getFile(root, path);
-                if (f.exists()) {
-                    found = true;
-                    if (!packages.contains(pack)) {
-                        packages.add(pack);
-                        File bdir = getFile(root, pack);
-                        for (int j = 0; j < fixed.length; j++) {
-                            File ff = getFile(bdir, fixed[j]);
-                            if (ff.isFile()) {
-                                dot.putResource("OSGI-OPT/src/" + pack
-                                        + fixed[j], new FileResource(ff));
-                            }
-                        }
-                    }
-                    dot
-                            .putResource("OSGI-OPT/src/" + path,
-                                    new FileResource(f));
-                }
-            }
-            if (!found) {
-                for (Jar jar : classpath) {
-                    Resource resource = jar.getResource(path);
-                    if (resource != null) {
-                        dot.putResource("OSGI-OPT/src", resource);
-                    } else {
-                        resource = jar.getResource("OSGI-OPT/src/" + path);
-                        if (resource != null) {
-                            dot.putResource("OSGI-OPT/src", resource);
-                        }
-                    }
-                }
-            }
-            if (getSourcePath().isEmpty())
-                warning("Including sources but " + SOURCEPATH
-                        + " does not contain any source directories ");
-            // TODO copy from the jars where they came from
-        }
-    }
-
-    boolean firstUse = true;
-
-    public Collection<File> getSourcePath() {
-        if (firstUse) {
-            firstUse = false;
-            String sp = getProperty(SOURCEPATH);
-            if (sp != null) {
-                Map<String, Map<String, String>> map = parseHeader(sp);
-                for (Iterator<String> i = map.keySet().iterator(); i.hasNext();) {
-                    String file = i.next();
-                    if (!isDuplicate(file)) {
-                        File f = getFile(file);
-                        if (!f.isDirectory()) {
-                            error("Adding a sourcepath that is not a directory: "
-                                    + f);
-                        } else {
-                            sourcePath.add(f);
-                        }
-                    }
-                }
-            }
-        }
-        return sourcePath;
-    }
-
-    private void doVerify(Jar dot) throws Exception {
-        Verifier verifier = new Verifier(dot, getProperties());
-        verifier.setPedantic(isPedantic());
-
-        // Give the verifier the benefit of our analysis
-        // prevents parsing the files twice
-        verifier.setClassSpace(classspace, contained, referred, uses);
-        verifier.verify();
-        getInfo(verifier);
-    }
-
-    private void doExpand(Jar jar) throws IOException {
-        if (getClasspath().size() == 0
-                && (getProperty(EXPORT_PACKAGE) != null || getProperty(PRIVATE_PACKAGE) != null))
-            warning("Classpath is empty. Private-Package and Export-Package can only expand from the classpath when there is one");
-
-        Map<Instruction, Map<String, String>> privateMap = replaceWitInstruction(
-                getHeader(PRIVATE_PACKAGE), PRIVATE_PACKAGE);
-        Map<Instruction, Map<String, String>> exportMap = replaceWitInstruction(
-                getHeader(EXPORT_PACKAGE), EXPORT_PACKAGE);
-
-        if (isTrue(getProperty(Constants.UNDERTEST))) {
-            privateMap.putAll(replaceWitInstruction(parseHeader(getProperty(
-                    Constants.TESTPACKAGES, "test;presence:=optional")),
-                    TESTPACKAGES));
-        }
-        if (!privateMap.isEmpty())
-            doExpand(jar, "Private-Package, or -testpackages", privateMap, true);
-
-        if (!exportMap.isEmpty()) {
-            Jar exports = new Jar("exports");
-            doExpand(exports, "Export-Package", exportMap, true);
-            jar.addAll(exports);
-            exports.close();
-        }
-
-        if (privateMap.isEmpty() && exportMap.isEmpty() && !isResourceOnly()) {
-            warning("Neither Export-Package, Private-Package, -testpackages is set, therefore no packages will be included");
-        }
-    }
-
-    /**
-     * 
-     * @param jar
-     * @param name
-     * @param instructions
-     */
-    private void doExpand(Jar jar, String name,
-            Map<Instruction, Map<String, String>> instructions,
-            boolean mandatory) {
-        Set<Instruction> superfluous = removeMarkedDuplicates(instructions
-                .keySet());
-
-        for (Iterator<Jar> c = getClasspath().iterator(); c.hasNext();) {
-            Jar now = c.next();
-            doExpand(jar, instructions, now, superfluous);
-        }
-
-        if (mandatory && superfluous.size() > 0) {
-            StringBuffer sb = new StringBuffer();
-            String del = "Instructions in " + name + " that are never used: ";
-            for (Iterator<Instruction> i = superfluous.iterator(); i.hasNext();) {
-                Instruction p = i.next();
-                sb.append(del);
-                sb.append(p.getPattern());
-                del = ", ";
-            }
-            warning(sb.toString());
-        }
-    }
-
-    /**
-     * Iterate over each directory in the class path entry and check if that
-     * directory is a desired package.
-     * 
-     * @param included
-     * @param classpathEntry
-     */
-    private void doExpand(Jar jar,
-            Map<Instruction, Map<String, String>> included, Jar classpathEntry,
-            Set<Instruction> superfluous) {
-
-        loop: for (Map.Entry<String, Map<String, Resource>> directory : classpathEntry
-                .getDirectories().entrySet()) {
-            String path = directory.getKey();
-
-            if (doNotCopy.matcher(getName(path)).matches())
-                continue;
-
-            if (directory.getValue() == null)
-                continue;
-
-            String pack = path.replace('/', '.');
-            Instruction instr = matches(included, pack, superfluous);
-            if (instr != null) {
-                // System.out.println("Pattern match: " + pack + " " +
-                // instr.getPattern() + " " + instr.isNegated());
-                if (!instr.isNegated()) {
-                    Map<String, Resource> contents = directory.getValue();
-
-                    // What to do with split packages? Well if this
-                    // directory already exists, we will check the strategy
-                    // and react accordingly.
-                    boolean overwriteResource = true;
-                    if (jar.hasDirectory(path)) {
-                        Map<String, String> directives = included.get(instr);
-
-                        switch (getSplitStrategy((String) directives
-                                .get(SPLIT_PACKAGE_DIRECTIVE))) {
-                        case SPLIT_MERGE_LAST:
-                            overwriteResource = true;
-                            break;
-
-                        case SPLIT_MERGE_FIRST:
-                            overwriteResource = false;
-                            break;
-
-                        case SPLIT_ERROR:
-                            error(diagnostic(pack, getClasspath(),
-                                    classpathEntry.source));
-                            continue loop;
-
-                        case SPLIT_FIRST:
-                            continue loop;
-
-                        default:
-                            warning(diagnostic(pack, getClasspath(),
-                                    classpathEntry.source));
-                            overwriteResource = false;
-                            break;
-                        }
-                    }
-
-                    jar.addDirectory(contents, overwriteResource);
-
-                    String key = path + "/bnd.info";
-                    Resource r = jar.getResource(key);
-                    if (r != null)
-                        jar.putResource(key, new PreprocessResource(this, r));
-
-                    if (hasSources()) {
-                        String srcPath = "OSGI-OPT/src/" + path;
-                        Map<String, Resource> srcContents = classpathEntry
-                                .getDirectories().get(srcPath);
-                        if (srcContents != null) {
-                            jar.addDirectory(srcContents, overwriteResource);
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Analyze the classpath for a split package
-     * 
-     * @param pack
-     * @param classpath
-     * @param source
-     * @return
-     */
-    private String diagnostic(String pack, List<Jar> classpath, File source) {
-        // Default is like merge-first, but with a warning
-        // Find the culprits
-        pack = pack.replace('.', '/');
-        List<Jar> culprits = new ArrayList<Jar>();
-        for (Iterator<Jar> i = classpath.iterator(); i.hasNext();) {
-            Jar culprit = (Jar) i.next();
-            if (culprit.getDirectories().containsKey(pack)) {
-                culprits.add(culprit);
-            }
-        }
-        return "Split package "
-                + pack
-                + "\nUse directive -split-package:=(merge-first|merge-last|error|first) on Export/Private Package instruction to get rid of this warning\n"
-                + "Package found in   " + culprits + "\n"
-                + "Reference from     " + source + "\n" + "Classpath          "
-                + classpath;
-    }
-
-    private int getSplitStrategy(String type) {
-        if (type == null)
-            return SPLIT_DEFAULT;
-
-        if (type.equals("merge-last"))
-            return SPLIT_MERGE_LAST;
-
-        if (type.equals("merge-first"))
-            return SPLIT_MERGE_FIRST;
-
-        if (type.equals("error"))
-            return SPLIT_ERROR;
-
-        if (type.equals("first"))
-            return SPLIT_FIRST;
-
-        error("Invalid strategy for split-package: " + type);
-        return SPLIT_DEFAULT;
-    }
-
-    private Instruction matches(
-            Map<Instruction, Map<String, String>> instructions, String pack,
-            Set<Instruction> superfluousPatterns) {
-        for (Instruction pattern : instructions.keySet()) {
-            if (pattern.matches(pack)) {
-                superfluousPatterns.remove(pattern);
-                return pattern;
-            }
-        }
-        return null;
-    }
-
-    private Map<String, Map<String, String>> getHeader(String string) {
-        if (string == null)
-            return Collections.emptyMap();
-        return parseHeader(getProperty(string));
-    }
-
-    /**
-     * Parse the Bundle-Includes header. Files in the bundles Include header are
-     * included in the jar. The source can be a directory or a file.
-     * 
-     * @throws IOException
-     * @throws FileNotFoundException
-     */
-    private void doIncludeResources(Jar jar) throws Exception {
-        String includes = getProperty("Bundle-Includes");
-        if (includes == null) {
-            includes = getProperty(INCLUDERESOURCE);
-            if (includes == null)
-                includes = getProperty("Include-Resource");
-        } else
-            warning("Please use -includeresource instead of Bundle-Includes");
-
-        if (includes == null)
-            return;
-
-        Map<String, Map<String, String>> clauses = parseHeader(includes);
-
-        for (Iterator<Map.Entry<String, Map<String, String>>> i = clauses
-                .entrySet().iterator(); i.hasNext();) {
-            Map.Entry<String, Map<String, String>> entry = i.next();
-            doIncludeResource(jar, entry.getKey(), entry.getValue());
-        }
-    }
-
-    private void doIncludeResource(Jar jar, String name,
-            Map<String, String> extra) throws ZipException, IOException,
-            Exception {
-        boolean preprocess = false;
-        if (name.startsWith("{") && name.endsWith("}")) {
-            preprocess = true;
-            name = name.substring(1, name.length() - 1).trim();
-        }
-
-        if (name.startsWith("@")) {
-            extractFromJar(jar, name.substring(1));
-        } else if (extra.containsKey("literal")) {
-            String literal = (String) extra.get("literal");
-            Resource r = new EmbeddedResource(literal.getBytes("UTF-8"), 0);
-            String x = (String) extra.get("extra");
-            if (x != null)
-                r.setExtra(x);
-            jar.putResource(name, r);
-        } else {
-            String source;
-            File sourceFile;
-            String destinationPath;
-
-            String parts[] = name.split("\\s*=\\s*");
-            if (parts.length == 1) {
-                // Just a copy, destination path defined by
-                // source path.
-                source = parts[0];
-                sourceFile = getFile(source);
-                // Directories should be copied to the root
-                // but files to their file name ...
-                if (sourceFile.isDirectory())
-                    destinationPath = "";
-                else
-                    destinationPath = sourceFile.getName();
-            } else {
-                source = parts[1];
-                sourceFile = getFile(source);
-                destinationPath = parts[0];
-
-                // Handle directories
-                if (sourceFile.isDirectory()) {
-                    destinationPath = doResourceDirectory(jar, extra,
-                            preprocess, sourceFile, destinationPath);
-                    return;
-                }
-            }
-
-            //destinationPath = checkDestinationPath(destinationPath);
-
-            if (!sourceFile.exists()) {
-                noSuchFile(jar, name, extra, source, destinationPath);
-            } else
-                copy(jar, destinationPath, sourceFile, preprocess, extra);
-        }
-    }
-
-    private String doResourceDirectory(Jar jar, Map<String, String> extra,
-            boolean preprocess, File sourceFile, String destinationPath)
-            throws Exception {
-        String filter = extra.get("filter:");
-        boolean flatten = isTrue(extra.get("flatten:"));
-        boolean recursive = true;
-        String directive = extra.get("recursive:");
-        if (directive != null) {
-            recursive = isTrue(directive);
-        }
-
-        InstructionFilter iFilter = null;
-        if (filter != null) {
-            iFilter = new InstructionFilter(Instruction
-                    .getPattern(filter), recursive);
-        } else {
-            iFilter = new InstructionFilter(null, recursive);
-        }
-
-        destinationPath = checkDestinationPath(destinationPath);
-
-        File[] files = resolveFiles(sourceFile, iFilter, recursive);
-        for (File file : files) {
-            String dp;
-            if (flatten) {
-                if (destinationPath.length() == 0) {
-                    dp = file.getName();
-                } else {
-                    dp = destinationPath + "/"
-                            + file.getName();
-                }
-            } else {
-                dp = destinationPath
-                        + file.getParentFile().getAbsolutePath()
-                                .substring(
-                                        sourceFile
-                                                .getAbsolutePath()
-                                                .length()).replace('\\','/');
-                if (dp.length() > 0) {
-                    dp += "/" + file.getName();
-                } else {
-                    dp = file.getName();
-                }
-            }
-            copy(jar, dp, file, preprocess, extra);
-        }
-        return destinationPath;
-    }
-
-    private String checkDestinationPath(String destinationPath) {
-
-        // Some people insist on ending a directory with
-        // a slash ... it now also works if you do /=dir
-        if (destinationPath.endsWith("/"))
-            destinationPath = destinationPath.substring(0, destinationPath
-                    .length() - 1);
-        return destinationPath;
-    }
-
-    private File[] resolveFiles(File dir, FileFilter filter, boolean recursive) {
-        return resolveFiles(dir, filter, null, recursive);
-    }
-
-    private File[] resolveFiles(File dir, FileFilter filter, File[] files,
-            boolean recursive) {
-        if (files == null) {
-            files = EMPTY_FILE;
-        }
-
-        if (Analyzer.doNotCopy.matcher(dir.getName()).matches()) {
-            return files;
-        }
-
-        File[] fs = dir.listFiles(filter);
-        for (File file : fs) {
-            if (file.isDirectory()) {
-                if (recursive) {
-                    files = resolveFiles(file, filter, files, recursive);
-                }
-            } else {
-                if (files.length == 0) {
-                    files = new File[] { file };
-                } else {
-                    File[] newFiles = new File[files.length + 1];
-                    System.arraycopy(files, 0, newFiles, 0, files.length);
-                    newFiles[newFiles.length - 1] = file;
-                    files = newFiles;
-                }
-            }
-        }
-        return files;
-    }
-
-    private void noSuchFile(Jar jar, String clause, Map<String, String> extra,
-            String source, String destinationPath) throws Exception {
-        Jar src = getJarFromName(source, "Include-Resource " + source);
-        if (src != null) {
-            JarResource jarResource = new JarResource(src);
-            jar.putResource(destinationPath, jarResource);
-        } else {
-            Resource lastChance = make.process(source);
-            if (lastChance != null) {
-                String x = extra.get("extra");
-                if (x != null)
-                    lastChance.setExtra(x);
-                jar.putResource(destinationPath, lastChance);
-            } else
-                error("Input file does not exist: " + source);
-        }
-    }
-
-    /**
-     * Extra resources from a Jar and add them to the given jar. The clause is
-     * the
-     * 
-     * @param jar
-     * @param clauses
-     * @param i
-     * @throws ZipException
-     * @throws IOException
-     */
-    private void extractFromJar(Jar jar, String name) throws ZipException,
-            IOException {
-        // Inline all resources and classes from another jar
-        // optionally appended with a modified regular expression
-        // like @zip.jar!/META-INF/MANIFEST.MF
-        int n = name.lastIndexOf("!/");
-        Pattern filter = null;
-        if (n > 0) {
-            String fstring = name.substring(n + 2);
-            name = name.substring(0, n);
-            filter = wildcard(fstring);
-        }
-        Jar sub = getJarFromName(name, "extract from jar");
-        if (sub == null)
-            error("Can not find JAR file " + name);
-        else
-            jar.addAll(sub, filter);
-    }
-
-    private Pattern wildcard(String spec) {
-        StringBuffer sb = new StringBuffer();
-        for (int j = 0; j < spec.length(); j++) {
-            char c = spec.charAt(j);
-            switch (c) {
-            case '.':
-                sb.append("\\.");
-                break;
-
-            case '*':
-                // test for ** (all directories)
-                if (j < spec.length() - 1 && spec.charAt(j + 1) == '*') {
-                    sb.append(".*");
-                    j++;
-                } else
-                    sb.append("[^/]*");
-                break;
-            default:
-                sb.append(c);
-                break;
-            }
-        }
-        String s = sb.toString();
-        try {
-            return Pattern.compile(s);
-        } catch (Exception e) {
-            error("Invalid regular expression on wildcarding: " + spec
-                    + " used *");
-        }
-        return null;
-    }
-
-    private void copy(Jar jar, String path, File from, boolean preprocess,
-            Map<String, String> extra) throws Exception {
-        if (doNotCopy.matcher(from.getName()).matches())
-            return;
-
-        if (from.isDirectory()) {
-            String next = path;
-            if (next.length() != 0 && ! next.endsWith("/"))
-                next += '/';
-
-            File files[] = from.listFiles();
-            for (int i = 0; i < files.length; i++) {
-                copy(jar, next + files[i].getName(), files[i], preprocess,
-                        extra);
-            }
-        } else {
-            if (from.exists()) {
-                Resource resource = new FileResource(from);
-                if (preprocess) {
-                    resource = new PreprocessResource(this, resource);
-                }
-                String x = extra.get("extra");
-                if (x != null)
-                    resource.setExtra(x);
-                if ( path.endsWith("/"))
-                    path = path + from.getName();
-                jar.putResource(path, resource);
-            } else {
-                error("Input file does not exist: " + from);
-            }
-        }
-    }
-
-    private String getName(String where) {
-        int n = where.lastIndexOf('/');
-        if (n < 0)
-            return where;
-
-        return where.substring(n + 1);
-    }
-
-    public void setSourcepath(File[] files) {
-        for (int i = 0; i < files.length; i++)
-            addSourcepath(files[i]);
-    }
-
-    public void addSourcepath(File cp) {
-        if (!cp.exists())
-            warning("File on sourcepath that does not exist: " + cp);
-
-        sourcePath.add(cp);
-    }
-
-    /**
-     * Create a POM reseource for Maven containing as much information as
-     * possible from the manifest.
-     * 
-     * @param output
-     * @param builder
-     * @throws FileNotFoundException
-     * @throws IOException
-     */
-    public void doPom(Jar dot) throws FileNotFoundException, IOException {
-        {
-            Manifest manifest = dot.getManifest();
-            String name = manifest.getMainAttributes().getValue(
-                    Analyzer.BUNDLE_NAME);
-            String description = manifest.getMainAttributes().getValue(
-                    Analyzer.BUNDLE_DESCRIPTION);
-            String docUrl = manifest.getMainAttributes().getValue(
-                    Analyzer.BUNDLE_DOCURL);
-            String version = manifest.getMainAttributes().getValue(
-                    Analyzer.BUNDLE_VERSION);
-            String bundleVendor = manifest.getMainAttributes().getValue(
-                    Analyzer.BUNDLE_VENDOR);
-            ByteArrayOutputStream s = new ByteArrayOutputStream();
-            PrintStream ps = new PrintStream(s);
-            String bsn = manifest.getMainAttributes().getValue(
-                    Analyzer.BUNDLE_SYMBOLICNAME);
-            String licenses = manifest.getMainAttributes().getValue(
-                    BUNDLE_LICENSE);
-
-            if (bsn == null) {
-                error("Can not create POM unless Bundle-SymbolicName is set");
-                return;
-            }
-
-            bsn = bsn.trim();
-            int n = bsn.lastIndexOf('.');
-            if (n <= 0) {
-                error("Can not create POM unless Bundle-SymbolicName contains a .");
-                ps.close();
-                s.close();
-                return;
-            }
-            String groupId = bsn.substring(0, n);
-            String artifactId = bsn.substring(n + 1);
-            ps
-                    .println("<project xmlns='http://maven.apache.org/POM/4.0.0' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation='http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd'>");
-            ps.println("  <modelVersion>4.0.0</modelVersion>");
-            ps.println("  <groupId>" + groupId + "</groupId>");
-
-            n = artifactId.indexOf(';');
-            if (n > 0)
-                artifactId = artifactId.substring(0, n).trim();
-
-            ps.println("  <artifactId>" + artifactId + "</artifactId>");
-            ps.println("  <version>" + version + "</version>");
-            if (description != null) {
-                ps.println("  <description>");
-                ps.print("    ");
-                ps.println(description);
-                ps.println("  </description>");
-            }
-            if (name != null) {
-                ps.print("  <name>");
-                ps.print(name);
-                ps.println("</name>");
-            }
-            if (docUrl != null) {
-                ps.print("  <url>");
-                ps.print(docUrl);
-                ps.println("</url>");
-            }
-
-            if (bundleVendor != null) {
-                Matcher m = NAME_URL.matcher(bundleVendor);
-                String namePart = bundleVendor;
-                String urlPart = null;
-                if (m.matches()) {
-                    namePart = m.group(1);
-                    urlPart = m.group(2);
-                }
-                ps.println("  <organization>");
-                ps.print("    <name>");
-                ps.print(namePart.trim());
-                ps.println("</name>");
-                if (urlPart != null) {
-                    ps.print("    <url>");
-                    ps.print(urlPart.trim());
-                    ps.println("</url>");
-                }
-                ps.println("  </organization>");
-            }
-            if (licenses != null) {
-                ps.println("  <licenses>");
-                Map<String, Map<String, String>> map = parseHeader(licenses);
-                for (Iterator<Map.Entry<String, Map<String, String>>> e = map
-                        .entrySet().iterator(); e.hasNext();) {
-                    Map.Entry<String, Map<String, String>> entry = e.next();
-                    ps.println("    <license>");
-                    Map<String, String> values = entry.getValue();
-                    print(ps, values, "name", "name", (String) values
-                            .get("url"));
-                    print(ps, values, "url", "url", null);
-                    print(ps, values, "distribution", "distribution", "repo");
-                    ps.println("    </license>");
-                }
-                ps.println("  </licenses>");
-            }
-            ps.println("</project>");
-            ps.close();
-            s.close();
-            dot
-                    .putResource("pom.xml", new EmbeddedResource(s
-                            .toByteArray(), 0));
-        }
-    }
-
-    /**
-     * Utility function to print a tag from a map
-     * 
-     * @param ps
-     * @param values
-     * @param string
-     * @param tag
-     * @param object
-     */
-    private void print(PrintStream ps, Map<String, String> values,
-            String string, String tag, String object) {
-        String value = (String) values.get(string);
-        if (value == null)
-            value = object;
-        if (value == null)
-            return;
-        ps.println("    <" + tag + ">" + value.trim() + "</" + tag + ">");
-    }
-
-    public void close() {
-        super.close();
-    }
-
-    /**
-     * Build Multiple jars. If the -sub command is set, we filter the file with
-     * the given patterns.
-     * 
-     * @return
-     * @throws Exception
-     */
-    public Jar[] builds() throws Exception {
-        begin();
-
-        // Are we acting as a conduit for another JAR?
-        String conduit = getProperty(CONDUIT);
-        if (conduit != null) {
-            Map<String, Map<String, String>> map = parseHeader(conduit);
-            Jar[] result = new Jar[map.size()];
-            int n = 0;
-            for (String file : map.keySet()) {
-                Jar c = new Jar(getFile(file));
-                addClose(c);
-                String name = map.get(file).get("name");
-                if (name != null)
-                    c.setName(name);
-
-                result[n++] = c;
-            }
-            return result;
-        }
-
-        // If no -sub property, then reuse this builder object
-        // other wise, build all the sub parts.
-        String sub = getProperty(SUB);
-        if (sub == null) {
-            Jar jar = build();
-            if (jar == null)
-                return new Jar[0];
-
-            return new Jar[] { jar };
-        }
-
-        List<Jar> result = new ArrayList<Jar>();
-
-        // Get the Instruction objects that match the sub header
-        Set<Instruction> subs = replaceWitInstruction(parseHeader(sub), SUB)
-                .keySet();
-
-        // Get the member files of this directory
-        List<File> members = new ArrayList<File>(Arrays.asList(getBase()
-                .listFiles()));
-
-        getProperties().remove(SUB);
-        // For each member file
-        nextFile: while (members.size() > 0) {
-
-            File file = members.remove(0);
-            if (file.equals(getPropertiesFile()))
-                continue nextFile;
-
-            for (Iterator<Instruction> i = subs.iterator(); i.hasNext();) {
-
-                Instruction instruction = i.next();
-                if (instruction.matches(file.getName())) {
-
-                    if (!instruction.isNegated()) {
-
-                        Builder builder = null;
-                        try {
-                            builder = getSubBuilder();
-                            addClose(builder);
-                            builder.setProperties(file);
-                            builder.setProperty(SUB, "");
-                            // Recursively build
-                            // TODO
-                            Jar jar = builder.build();
-                            jar.setName(builder.getBsn());
-                            result.add(jar);
-                        } catch (Exception e) {
-                            e.printStackTrace();
-                            error("Sub Building " + file, e);
-                        }
-                        if (builder != null)
-                            getInfo(builder, file.getName() + ": ");
-                    }
-
-                    // Because we matched (even though we could be negated)
-                    // we skip any remaining searches
-                    continue nextFile;
-                }
-            }
-        }
-        setProperty(SUB, sub);
-        return result.toArray(new Jar[result.size()]);
-    }
-
-    public Builder getSubBuilder() throws Exception {
-        Builder builder = new Builder(this);
-        builder.setBase(getBase());
-
-        for (Jar file : getClasspath()) {
-            builder.addClasspath(file);
-        }
-
-        return builder;
-    }
-
-    /**
-     * A macro to convert a maven version to an OSGi version
-     */
-
-    public String _maven_version(String args[]) {
-        if (args.length > 2)
-            error("${maven_version} macro receives too many arguments "
-                    + Arrays.toString(args));
-        else if (args.length < 2)
-            error("${maven_version} macro has no arguments, use ${maven_version;1.2.3-SNAPSHOT}");
-        else {
-            return cleanupVersion(args[1]);
-        }
-        return null;
-    }
-
-    public String _permissions(String args[]) throws IOException {
-        StringBuilder sb = new StringBuilder();
-
-        for (String arg : args) {
-            if ("packages".equals(arg) || "all".equals(arg)) {
-                for (String imp : getImports().keySet()) {
-                    if (!imp.startsWith("java.")) {
-                        sb.append("(org.osgi.framework.PackagePermission \"");
-                        sb.append(imp);
-                        sb.append("\" \"import\")\r\n");
-                    }
-                }
-                for (String exp : getExports().keySet()) {
-                    sb.append("(org.osgi.framework.PackagePermission \"");
-                    sb.append(exp);
-                    sb.append("\" \"export\")\r\n");
-                }
-            } else if ("admin".equals(arg) || "all".equals(arg)) {
-                sb.append("(org.osgi.framework.AdminPermission)");
-            } else if ("permissions".equals(arg))
-                ;
-            else
-                error("Invalid option in ${permissions}: %s", arg);
-        }
-        return sb.toString();
-    }
-
-    public void removeBundleSpecificHeaders() {
-        Set<String> set = new HashSet<String>(Arrays
-                .asList(BUNDLE_SPECIFIC_HEADERS));
-        setForceLocal(set);
-    }
+					return filePath.replace(File.separatorChar, '/');
+				}
+			}
+		}
+		return null;
+	}
 
 }
diff --git a/bundleplugin/src/main/java/org/apache/felix/bundleplugin/BundlePlugin.java b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/BundlePlugin.java
index 94b34a3..756064a 100644
--- a/bundleplugin/src/main/java/org/apache/felix/bundleplugin/BundlePlugin.java
+++ b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/BundlePlugin.java
@@ -63,6 +63,7 @@
 
 import aQute.lib.osgi.Analyzer;
 import aQute.lib.osgi.Builder;
+import aQute.lib.osgi.Constants;
 import aQute.lib.osgi.EmbeddedResource;
 import aQute.lib.osgi.FileResource;
 import aQute.lib.osgi.Jar;
@@ -395,7 +396,7 @@
 
         dumpManifest( "BND Manifest:", jar.getManifest(), getLog() );
 
-        String[] removeHeaders = builder.getProperty( Analyzer.REMOVE_HEADERS, "" ).split( "," );
+        String[] removeHeaders = builder.getProperty( Constants.REMOVEHEADERS, "" ).split( "," );
 
         mergeMavenManifest( currentProject, jar, removeHeaders, getLog() );
         builder.setJar( jar );
@@ -852,7 +853,7 @@
         properties.put( Analyzer.BUNDLE_VERSION, getMaven2OsgiConverter().getVersion(currentProject.getVersion()) );
 
         // remove the extraneous Include-Resource and Private-Package entries from generated manifest
-        properties.put( Analyzer.REMOVE_HEADERS, Analyzer.INCLUDE_RESOURCE + ',' + Analyzer.PRIVATE_PACKAGE );
+        properties.put( Constants.REMOVEHEADERS, Analyzer.INCLUDE_RESOURCE + ',' + Analyzer.PRIVATE_PACKAGE );
 
         header( properties, Analyzer.BUNDLE_DESCRIPTION, currentProject.getDescription() );
         StringBuffer licenseText = printLicenses( currentProject.getLicenses() );
diff --git a/bundleplugin/src/main/java/org/apache/felix/bundleplugin/ManifestPlugin.java b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/ManifestPlugin.java
index 4503840..5a136cd 100644
--- a/bundleplugin/src/main/java/org/apache/felix/bundleplugin/ManifestPlugin.java
+++ b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/ManifestPlugin.java
@@ -100,14 +100,14 @@
 
 
     public Manifest getManifest( MavenProject project, Jar[] classpath ) throws IOException, MojoFailureException,
-        MojoExecutionException
+        MojoExecutionException, Exception
     {
         return getManifest( project, new Properties(), new Properties(), classpath );
     }
 
 
     public Manifest getManifest( MavenProject project, Map instructions, Properties properties, Jar[] classpath )
-        throws IOException, MojoFailureException, MojoExecutionException
+        throws IOException, MojoFailureException, MojoExecutionException, Exception
     {
         Analyzer analyzer = getAnalyzer( project, instructions, properties, classpath );
 
@@ -143,14 +143,15 @@
     }
 
 
-    protected Analyzer getAnalyzer( MavenProject project, Jar[] classpath ) throws IOException, MojoExecutionException
+    protected Analyzer getAnalyzer( MavenProject project, Jar[] classpath )
+        throws IOException, MojoExecutionException, Exception
     {
         return getAnalyzer( project, new LinkedHashMap(), new Properties(), classpath );
     }
 
 
     protected Analyzer getAnalyzer( MavenProject project, Map instructions, Properties properties, Jar[] classpath )
-        throws IOException, MojoExecutionException
+        throws IOException, MojoExecutionException, Exception
     {
         File file = project.getArtifact().getFile();
         if ( file == null )