package test;

import java.io.*;
import java.util.*;
import java.util.jar.*;
import java.util.regex.*;

import aQute.bnd.header.*;
import aQute.bnd.osgi.*;
import aQute.bnd.test.*;
import aQute.bnd.version.*;
import aQute.lib.collections.*;
import aQute.lib.hex.*;
import aQute.lib.io.*;
import aQute.service.reporter.Report.Location;

@SuppressWarnings("resource")
public class BuilderTest extends BndTestCase {

	/**
	 * Test if the Manifest gets the last modified date
	 */

	public void testLastModifiedForManifest() throws Exception {
		File file = new File("tmp.jar");
		try {
			long time = System.currentTimeMillis();

			Builder b = new Builder();
			b.addClasspath(IO.getFile("jar/osgi.jar"));
			b.setExportPackage("org.osgi.framework");
			Jar build = b.build();
			try {
				assertTrue(b.check());

				build.write("tmp.jar");
				Jar ajr = new Jar(file);
				try {
					Resource r = ajr.getResource("META-INF/MANIFEST.MF");
					assertNotNull(r);
					long t = r.lastModified();
					Date date = new Date(t);
					System.out.println(date + " " + t);
					// TODO we need to adapt the timestamp handling
					assertTrue(date + " " + t, t ==1142555622000L);
				}
				finally {
					ajr.close();
				}
			}
			finally {
				build.close();
			}
		}
		finally {
			file.delete();
		}

	}

	/**
	 * A Require-Bundle should not fail on missing imports, just warn
	 * 
	 * @throws Exception
	 */

	public void testMissingImportWithRequireBundle() throws Exception {
		Builder b = new Builder();
		try {
			b.addClasspath(new File("bin"));
			b.setPedantic(true);
			b.setExportPackage("test.classreference;version=1");
			b.setImportPackage("!*");
			b.setProperty("Require-Bundle", "com.abc");
			b.build();
			assertTrue(b.check());

			Verifier v = new Verifier(b.getJar());
			v.verify();
			assertTrue(v.check());
		}
		finally {
			b.close();
		}
	}

	/**
	 * #479 I have now tested this locally. Apparently the fix doesn't change
	 * the reported behavior. In my test bundle, I have removed all imports. Bnd
	 * builds this with no errors reported. I add: DynamicImport-Package: dummy
	 */

	public void testMissingImportsWithDynamicImport() throws Exception {
		Builder b = new Builder();
		try {
			b.addClasspath(new File("bin"));
			b.setPedantic(true);
			b.setExportPackage("test.classreference;version=1");
			b.setImportPackage("!*");
			b.setProperty(Constants.DYNAMICIMPORT_PACKAGE, "dummy");
			b.build();
			assertTrue(b.check());

			Verifier v = new Verifier(b.getJar());
			try {
				v.verify();
				assertTrue(v.check("Unresolved references to \\[javax.swing\\] by class\\(es\\)"));
			}
			finally {
				v.close();
			}
		}
		finally {
			b.close();
		}
	}

	/**
	 * #479 I have now tested this locally. Apparently the fix doesn't change
	 * the reported behavior. In my test bundle, I have removed all imports. Bnd
	 * builds this with no errors reported. I add: DynamicImport-Package: dummy
	 */

	public void testMissingImportsWithoutDynamicImport() throws Exception {
		Builder b = new Builder();
		try {
			b.addClasspath(new File("bin"));
			b.setPedantic(true);
			b.setExportPackage("test.classreference;version=1");
			b.setImportPackage("!*");
			b.build();
			assertTrue(b.check());

			Verifier v = new Verifier(b.getJar());
			try {
				v.verify();
				assertTrue(v.check("Unresolved references to \\[javax.swing\\] by class\\(es\\)"));
			}
			finally {
				v.close();
			}
		}
		finally {
			b.close();
		}

	}

	/**
	 * <pre>
	 * [2013-12-11 15:55:14] BJ Hargrave: init:
	 *      [echo] Enter project org.osgi.test.cases.prefs (${top})
	 * [bndprepare] 2 WARNINGS
	 * [bndprepare]  No translation found for macro: classes;extending;junit.framework.TestCase;concrete
	 * [bndprepare]  No translation found for macro: classes,concrete
	 * [2013-12-11 15:55:31] BJ Hargrave: I am getting this on the latest bnd.master in the OSGi test projects
	 * </pre>
	 * 
	 * @throws Exception
	 */

	public static void testClassQuery() throws Exception {
		Builder a = new Builder();
		try {
			a.addClasspath(new File("bin"));
			a.setExportPackage("test.component");
			a.setProperty("testcases", "${sort;${classes;extending;junit.framework.TestCase;concrete}}");
			a.setProperty("Test-Cases", "${testcases}");
			Jar jar = a.build();
			assertTrue(a.check());
			Manifest m = jar.getManifest();
			Parameters p = new Parameters(m.getMainAttributes().getValue("Test-Cases"));
			assertTrue(p.size() >= 4);
		}
		finally {
			a.close();
		}
	}

	/**
	 * Bundle ActivationPolicy
	 * 
	 * @throws Exception
	 */
	public void testBundleActivationPolicy() throws Exception {
		Builder b = new Builder();
		try {
			b.addClasspath(IO.getFile("bin"));
			;
			b.setProperty("Bundle-ActivationPolicy", "lazy");
			b.setProperty("Export-Package", "test.activator");
			b.build();
			assertTrue(b.check());
		}
		finally {
			b.close();
		}
	}

	/**
	 * #388 Manifest header to get GIT head
	 */
	public void testGitHead() {
		Builder b = new Builder();
		try {
			String s = b.getReplacer().process("${githead}");
			assertTrue(Hex.isHex(s));
		}
		finally {
			b.close();
		}
	}

	/**
	 * If a package-info.java + packageinfo are present then normally
	 * package-info takes precedence if it sets a Version. This test sees that
	 * if no version is sets, packageinfo is used.
	 */
	public void testPackageInfo_no_version() throws Exception {
		Builder b = new Builder();
		try {
			b.addClasspath(new File("bin"));
			b.setExportPackage("test.packageinfo.both_no_version");
			Jar build = b.build();
			assertTrue(b.check());

			Attrs imports = b.getExports().getByFQN("test.packageinfo.both_no_version");
			assertEquals("1.2.3", imports.getVersion());
		}
		finally {
			b.close();
		}

	}

	/**
	 * An old osgi 3.0.0 jar had an old packageinfo in it. This included some
	 * never well developed syntax which now clashes with the proprty syntax.
	 * 
	 * @throws Exception
	 */
	public void testVeryOldPackageInfo() throws Exception {
		Builder b = new Builder();
		try {
			b.addClasspath(IO.getFile("jar/osgi-3.0.0.jar"));
			b.setExportPackage("org.osgi.util.measurement;version=100, org.osgi.util.tracker;version=100, *");
			Jar build = b.build();
			assertTrue(b.check("Version for package org.osgi.util.measurement is set to different values in the source ", "Version for package org.osgi.util.tracker is set to different values in the source"));
		}
		finally {
			b.close();
		}

	}

	/**
	 * Using a package info without the version keyword gives strange results in
	 * the manifest, should generate an error.
	 */

	public void testBadPackageInfo() throws Exception {
		Builder b = new Builder();
		try {
			b.addClasspath(new File("bin"));
			b.setExportPackage("test.package_info_versioniskey");
			b.build();

			String message = b.getErrors().get(0);
			assertTrue("The lacking version error first",
					message.contains("package info for test.package_info_versioniskey attribute [1.0.0=''],"));
			Location location = b.getLocation(message);
			assertNotNull("Supposed to have a location", location);
			assertNotNull("And that must have a file", location.file);
			assertEquals("Which should be the packaginfo file", "packageinfo", new File(location.file).getName());
			assertEquals(4, location.line);
			assertEquals(5, location.length);

			assertTrue(b.check("package info for test.package_info_versioniskey attribute \\[1.0.0=''\\],"));
		}
		finally {
			b.close();
		}
	}

	/**
	 * https://github.com/bndtools/bnd/issues/359 Starts with a bundle that has
	 * one package 'a' containing classes A and B. First removes B from 'a', and
	 * checks that the last modified date of the resulting bundle changed. Then
	 * removes A from 'a', and checks again that the last modified data changed.
	 */
	public static void testRemoveClassFromPackage() throws Exception {
		try {
			Builder b = new Builder();
			try {
				IO.getFile("bin/a1/a").mkdirs();
				IO.copy(IO.getFile("bin/a/A.class"), IO.getFile("bin/a1/a/A.class"));
				IO.copy(IO.getFile("bin/a/B.class"), IO.getFile("bin/a1/a/B.class"));
				Jar classpath = new Jar(IO.getFile("bin/a1"));
				b.addClasspath(classpath);
				b.setPrivatePackage("a");
				Jar result = b.build();
				Resource ra = result.getResource("a/A.class");
				Resource rb = result.getResource("a/B.class");
				long lm1 = result.lastModified();
				assertTrue("Last modified date of bundle > 0", lm1 > 0);

				if (isWindows())
					Thread.sleep(1000); // windows has a very low resolution
										// sometimes

				IO.getFile("bin/a1/a/B.class").delete();
				classpath.remove("a/B.class");
				classpath.updateModified(System.currentTimeMillis(), "Removed file B");
				result = b.build();
				long lm2 = result.lastModified();
				assertTrue("Last modified date of bundle has increased after deleting class from package", lm2 > lm1);

				if (isWindows())
					Thread.sleep(1000); // windows has a very low resolution
										// sometimes

				IO.getFile("bin/a1/a/A.class").delete();
				classpath.remove("a/A.class");
				classpath.updateModified(System.currentTimeMillis(), "Removed file A");

				if (isWindows())
					Thread.sleep(1000); // windows has a very low resolution
										// sometimes

				result = b.build();
				long lm3 = result.lastModified();
				assertTrue("Last modified date of bundle has increased after deleting last class from package",
						lm3 > lm2);
			}
			finally {
				b.close();
			}
		}
		finally {
			try {
				IO.getFile("bin/a1/a/A.class").delete();
			}
			catch (Exception e) {}
			try {
				IO.getFile("bin/a1/a/B.class").delete();
			}
			catch (Exception e) {}
			try {
				IO.getFile("bin/a1/a").delete();
			}
			catch (Exception e) {}
			try {
				IO.getFile("bin/a1").delete();
			}
			catch (Exception e) {}
		}
	}

	private static boolean isWindows() {
		return File.separatorChar == '\\';
	}

	/**
	 * https://github.com/bndtools/bnd/issues/315 Turns out bnd doesn't seem to
	 * support a class in a capitalized package name. I accidentally called a
	 * package with a capital letter and I get the strange error message and a
	 * refusal to build it. (See title for error message) My package could be
	 * named "Coffee" and the package named "CoffeeClient", The bnd.bnd file
	 * could have: Private-Package: Coffee I'm running 2.0.0REL with Eclipse
	 * Juno.
	 */
	public static void testUpperCasePackage() throws Exception {
		Builder b = new Builder();
		try {
			b.addClasspath(IO.getFile("bin"));
			b.setExportPackage("UPPERCASEPACKAGE");
			b.build();
			assertTrue(b.check());
			b.getExports().containsFQN("UPPERCASEPACKAGE");
		}
		finally {
			b.close();
		}
	}

	/**
	 * Dave Smith <dave.smith@candata.com> I have pulled the latest from git and
	 * am testing out 2.0 with our current application. I am getting the
	 * following error message on the bnd.bnd file null, for cmd : classes,
	 * arguments [classes;CONCRETE;ANNOTATION;javax.persistence.Entity] My bnd
	 * file does have the following line ... Hibernate-Db =
	 * ${classes;CONCRETE;ANNOTATION;javax.persistence.Entity}
	 * 
	 * @throws Exception
	 */

	public static void testClasses() throws Exception {
		Builder b = new Builder();
		try {
			b.setProperty("x", "${classes;CONCRETE;ANNOTATION;aQute.bnd.annotation.component.Component}");
			b.setProperty("y", "${classes;CONCRETE;ANNOTATED;aQute.bnd.annotation.component.Component}");
			b.setProperty("z", "${classes;CONCRETE;ANNOTATEDX;x.y.Z}");
			b.setPrivatePackage("test");
			b.addClasspath(IO.getFile("bin"));
			b.build();
			String s = b.getProperty("x");
			assertEquals(s, b.getProperty("y"));
			assertTrue(s.contains("test.Target"));
			assertEquals("${classes;CONCRETE;ANNOTATEDX;x.y.Z}", b.getProperty("z"));
			assertTrue(b.check("ANNOTATEDX"));
		}
		finally {
			b.close();
		}
	}

	/**
	 * Check if we can create digests
	 * 
	 * @throws Exception
	 */

	public static void testDigests() throws Exception {
		Builder b = new Builder();
		try {
			b.addClasspath(IO.getFile(new File(""), "jar/osgi.jar"));
			b.setExportPackage("org.osgi.framework");
			b.setProperty(Constants.DIGESTS, "MD5, SHA1");
			Jar jar = b.build();
			assertTrue(b.check());
			File f = File.createTempFile("test", ".jar");
			jar.write(f);

			Jar other = new Jar(f);
			Manifest manifest = other.getManifest();
			assertNotNull(manifest);
			Attributes attrs = manifest.getAttributes("org/osgi/framework/BundleActivator.class");
			assertNotNull(attrs);
			assertEquals("RTRhr3kadnulINegRhpmog==", attrs.getValue("MD5-Digest"));
			assertEquals("BfVfpnE3Srx/0UWwtzNecrAGf8A=", attrs.getValue("SHA-Digest"));
			other.close();
		}
		finally {
			b.close();
		}
	}

	/**
	 * FELIX-3407 Utterly confusing example that states that generic references
	 * are not picked up. The classes under test are in {@link A},{@link B}, and
	 * {@link C}.
	 */
	public static void testGenericPickup() throws Exception {
		Builder b = new Builder();
		try {
			b.setPrivatePackage("test.genericinterf.a");
			b.addClasspath(new File("bin"));
			b.build();
			assertTrue(b.check());
			System.out.println(b.getImports());
			assertTrue(b.getImports().containsFQN("test.genericinterf.b"));
			assertTrue(b.getImports().containsFQN("test.genericinterf.c"));
		}
		finally {
			b.close();
		}
	}

	/**
	 * Github #130 Consider the following descriptor file: Bundle-Activator:
	 * org.example.Activator Private-Package: org.example Now suppose that at
	 * build time, bnd cannot find the package org.example, or it is empty. Bnd
	 * sees the Bundle-Activator instruction as creating a dependency, so it
	 * generates a manifest containing an import for that package:
	 * Import-Package: org.example This is unexpected. If a Private-Package
	 * instruction is given with a specific package name (i.e. not a wildcard),
	 * and that package does not exist or is empty, then bnd should fail or
	 * print an error.
	 * 
	 * @throws Exception
	 */
	public static void testPrivatePackageNonExistent() throws Exception {
		Builder b = new Builder();
		try {
			b.addClasspath(IO.getFile("jar/osgi.jar"));
			b.setBundleActivator("com.example.Activator");
			b.setPrivatePackage("com.example");
			b.setIncludeResource("p;literal='x'");

			b.build();
			assertTrue(b.check("on the class path: \\[com.example\\]",
					"Bundle-Activator com.example.Activator is being imported"));
		}
		finally {
			b.close();
		}
	}

	/**
	 * #41 Test the EE macro
	 */

	public static void testEEMacro() throws Exception {
		Builder b = new Builder();
		try {
			b.addClasspath(IO.getFile("jar/ifc112.jar"));
			b.setPrivatePackage("netscape.util.*");
			b.setBundleRequiredExecutionEnvironment("${ee}");
			Jar jar = b.build();
			assertTrue(b.check());

			Domain domain = Domain.domain(jar.getManifest());
			Parameters ee = domain.getBundleRequiredExecutionEnvironment();
			System.err.println(ee);
			assertTrue(ee.containsKey("JRE-1.1"));
		}
		finally {
			b.close();
		}
	}

	public static void testEEMacro2() throws Exception {
		String[] packages = {
				"eclipse_1_1", "eclipse_1_2", "eclipse_1_3", "eclipse_1_4", "eclipse_1_5", "eclipse_1_6",
				"eclipse_1_7", "eclipse_jsr14", "sun_1_8"
		};

		String[] ees = {
				"JRE-1.1", "J2SE-1.2", "J2SE-1.3", "J2SE-1.4", "J2SE-1.5", "JavaSE-1.6", "JavaSE-1.7", "J2SE-1.4",
				"JavaSE-1.8"
		};
		String[] versions = {
				"1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7", "1.4", "1.8"
		};
		Pattern p = Pattern.compile("\\(&\\(osgi.ee=JavaSE\\)\\(version=(" + Version.VERSION_STRING + ")\\)\\)");
		for (int i = 0; i < packages.length; i++) {
			Builder b = new Builder();
			try {
				b.addClasspath(IO.getFile("compilerversions/compilerversions.jar"));
				b.setPrivatePackage(packages[i]);
				b.setBundleRequiredExecutionEnvironment("${ee}");
				Jar jar = b.build();
				assertTrue(b.check());
				Domain domain = Domain.domain(jar.getManifest());
				Parameters ee = domain.getBundleRequiredExecutionEnvironment();
				System.err.println(ee);
				assertEquals(ees[i], ee.toString());

				//
				// Check the requirements
				//
				Parameters een = domain.getRequireCapability();
				assertFalse(een.isEmpty());
				Attrs attrs = een.get("osgi.ee");
				String filter = attrs.get("filter:");
				Matcher m = p.matcher(filter);
				assertTrue(m.matches());
				assertEquals(versions[i], m.group(1));
			}
			finally {
				b.close();
			}
		}
	}

	/**
	 * bnd issues Consider the following descriptor file:
	 * 
	 * <pre>
	 * Bundle-Activator: org.example.Activator 
	 * Private-Package: org.example
	 * </pre>
	 * 
	 * Now suppose that at build time, bnd cannot find the package org.example,
	 * or it is empty. Bnd sees the Bundle-Activator instruction as creating a
	 * dependency, so it generates a manifest containing an import for that
	 * package:
	 * 
	 * <pre>
	 * Import-Package: org.example
	 * </pre>
	 * 
	 * This is unexpected. If a Private-Package instruction is given with a
	 * specific package name (i.e. not a wildcard), and that package does not
	 * exist or is empty, then bnd should fail or print an error.
	 */

	public static void testReportEmptyPrivatePackage() throws Exception {
		Builder b = new Builder();
		try {
			b.addClasspath(new File("bin"));
			b.setPrivatePackage("does.not.exist");
			b.build();
			assertTrue(b.check("The JAR is empty", "Unused Private-Package instruction"));
		}
		finally {
			b.close();
		}
	}

	/**
	 * Test the name section
	 */

	public static void testNamesection() throws Exception {
		Builder b = new Builder();
		try {
			b.addClasspath(IO.getFile("jar/osgi.jar"));
			b.setProperty(Constants.NAMESECTION,
					"org/osgi/service/event/*;MD5='${md5;${@}}';SHA1='${sha1;${@}}';MD5H='${md5;${@};hex}'");
			b.setProperty(Constants.PRIVATEPACKAGE, "org.osgi.service.event");
			Jar build = b.build();
			assertOk(b);
			build.calcChecksums(new String[] {
					"MD5", "SHA1"
			});
			assertTrue(b.check());
			Manifest m = build.getManifest();
			m.write(System.err);

			assertNotNull(m.getAttributes("org/osgi/service/event/EventAdmin.class").getValue("MD5"));
			assertNotNull(m.getAttributes("org/osgi/service/event/EventAdmin.class").getValue("SHA1"));
			assertEquals(m.getAttributes("org/osgi/service/event/EventAdmin.class").getValue("MD5-Digest"), m
					.getAttributes("org/osgi/service/event/EventAdmin.class").getValue("MD5"));
		}
		finally {
			b.close();
		}

	}

	/**
	 * Test the digests
	 */

	// public void testDigests() throws Exception {
	// Builder b = new Builder();
	// b.addClasspath(IO.getFile("jar/osgi.jar"));
	// b.setProperty(Constants.DIGESTS, "MD5, SHA1");
	// b.setProperty(Constants.PRIVATE_PACKAGE, "*");
	// Jar build = b.build();
	// assertOk(b);
	//
	// Manifest m = build.getManifest();
	// assertEquals(261, build.getResources().size());
	//
	// for (Entry<String,Resource> e : build.getResources().entrySet()) {
	// System.out.println("Path " + e.getKey());
	//
	// Attributes attrs = m.getAttributes(e.getKey());
	// assertNotNull(e.getKey(), attrs);
	// boolean md5 = false, sha1 = false;
	//
	// for (Entry<Object,Object> ee : attrs.entrySet()) {
	// String name = ee.getKey().toString().toLowerCase();
	// if (name.endsWith("-digest")) {
	// String value = ee.getValue().toString().trim();
	// assertNotNull("original digest", value);
	//
	// byte[] original = Base64.decodeBase64(value);
	// assertNotNull("original digest", original);
	//
	// String alg = name.substring(0, name.length() - 7);
	// if (alg.equals("md5"))
	// md5 = true;
	// if (alg.equals("sha1"))
	// sha1 = true;
	//
	// MessageDigest md = MessageDigest.getInstance(alg);
	// InputStream in = e.getValue().openInputStream();
	// byte[] buffer = new byte[8000];
	// int size = in.read(buffer);
	// while (size > 0) {
	// md.update(buffer, 0, size);
	// size = in.read(buffer);
	// }
	// byte calculated[] = md.digest();
	// assertTrue("comparing digests " + e.getKey() + " " + value + " " +
	// Base64.encodeBase64(calculated),
	// Arrays.equals(original, calculated));
	// }
	// }
	// assertTrue("expected md5", md5);
	// assertTrue("expected sha1", sha1);
	// }
	// }

	/**
	 * Check of the use of x- directives are not skipped. bnd allows x-
	 * directives in the import/export clauses but strips other ones.
	 * 
	 * @throws Exception
	 */
	public static void testXDirectives() throws Exception {
		Builder b = new Builder();
		try {
			b.addClasspath(IO.getFile("jar/osgi.jar"));
			b.setProperty("Export-Package", "org.osgi.framework;x-foo:=true;bar:=false");
			Jar jar = b.build();
			assertTrue(b.check("bar:"));
			Manifest m = jar.getManifest();
			String s = m.getMainAttributes().getValue("Export-Package");
			assertTrue(s.contains("x-foo:"));
		}
		finally {
			b.close();
		}
	}

	/**
	 * Check of SNAPSHOT is replaced with the -snapshot instr
	 * 
	 * @throws Exception
	 */
	public static void testSnapshot() throws Exception {
		Builder b = new Builder();
		try {
			b.setProperty("-resourceonly", "true");
			b.setProperty("-snapshot", "TIMESTAMP");
			b.setProperty("Bundle-Version", "1.0-SNAPSHOT");
			Jar jar = b.build();
			assertTrue(b.check("The JAR is empty"));
			Manifest m = jar.getManifest();
			assertEquals("1.0.0.TIMESTAMP", m.getMainAttributes().getValue("Bundle-Version"));
		}
		finally {
			b.close();
		}
	}

	/**
	 * Check if do not copy works on files
	 */

	public static void testDoNotCopy() throws Exception {
		Builder b = new Builder();
		try {
			b.setProperty("-resourceonly", "true");
			b.setProperty("-donotcopy", ".*\\.jar|\\..*");
			b.setProperty("Include-Resource", "jar");
			b.build();
			assertTrue(b.check());

			Set<String> names = b.getJar().getResources().keySet();
			assertEquals(11, names.size());
			assertTrue(names.contains("AnnotationWithJSR14.jclass"));
			assertTrue(names.contains("mandatorynoversion.bnd"));
			assertTrue(names.contains("mina.bar"));
			assertTrue(names.contains("minax.bnd"));
			assertTrue(names.contains("rox.bnd"));
			assertTrue(names.contains("WithAnnotations.jclass"));
		}
		finally {
			b.close();
		}
	}

	/**
	 * Check if do not copy works on files
	 */

	public static void testDoNotCopyDS() throws Exception {
		Builder b = new Builder();
		try {
			b.setProperty("-resourceonly", "true");
			b.setProperty("Include-Resource", "jar/");
			b.build();
			assertTrue(b.check());

			Set<String> names = b.getJar().getResources().keySet();
			assertFalse(names.contains(".DS_Store"));
		}
		finally {
			b.close();
		}
	}

	/**
	 * No error is generated when a file is not found.
	 */

	public static void testFileNotFound() throws Exception {
		Builder b = new Builder();
		try {
			b.setPedantic(true);
			b.setProperty("-classpath", "xyz.jar");
			b.setProperty("Include-Resource", "lib=lib, jar/osgi.jar");
			b.setProperty("-resourceonly", "true");
			b.build();
			assertTrue(b.check("Input file does not exist: lib", "Cannot find entry on -classpath: xyz.jar"));
		}
		finally {
			b.close();
		}
	}

	/**
	 * bnd seems to pick the wrong version if a packageinfo is available
	 * multiple times.
	 * 
	 * @throws Exception
	 */

	public static void testMultiplePackageInfo() throws Exception {
		Builder b = new Builder();
		try {
			b.addClasspath(IO.getFile("jar/osgi.jar"));
			b.addClasspath(IO.getFile("jar/osgi.core.jar"));
			b.setProperty(Constants.PRIVATEPACKAGE, "org.osgi.service.packageadmin;-split-package:=first");
			b.build();
			assertTrue(b.check());
			String version = b.getImports().getByFQN("org.osgi.framework").get(Constants.VERSION_ATTRIBUTE);
			assertEquals("[1.3,2)", version);
		}
		finally {
			b.close();
		}
	}

	/**
	 * Test the from: directive on expanding packages.
	 */
	public static void testFromOSGiDirective() throws Exception {
		Builder b = new Builder();
		try {
			b.addClasspath(IO.getFile("jar/osgi.jar"));
			b.addClasspath(IO.getFile("jar/org.eclipse.osgi-3.5.0.jar"));
			b.setProperty("Export-Package", "org.osgi.framework;from:=osgi");
			b.build();
			assertTrue(b.check());

			assertEquals("1.3", b.getExports().getByFQN("org.osgi.framework").get("version"));
		}
		finally {
			b.close();
		}

	}

	public static void testFromEclipseDirective() throws Exception {
		Builder b = new Builder();
		try {
			b.addClasspath(IO.getFile("jar/osgi.jar"));
			b.addClasspath(IO.getFile("jar/org.eclipse.osgi-3.5.0.jar"));
			b.setProperty("Export-Package", "org.osgi.framework;from:=org.eclipse.osgi-3.5.0");
			b.build();
			assertTrue(b.check());

			assertEquals("1.3", b.getExports().getByFQN("org.osgi.framework").get("version"));
		}
		finally {
			b.close();
		}
	}

	/**
	 * Test the provide package
	 */
	public static void testProvidedVersion() throws Exception {
		Builder b = new Builder();
		try {
			b.addClasspath(IO.getFile("jar/osgi.jar"));
			b.addClasspath(new File("bin"));
			b.setProperty(Constants.EXPORT_PACKAGE, "org.osgi.service.event;provide:=true");
			b.setProperty("Private-Package", "test.refer");
			Jar jar = b.build();
			assertTrue(b.check());
			String ip = jar.getManifest().getMainAttributes().getValue(Constants.IMPORT_PACKAGE);
			Parameters map = Processor.parseHeader(ip, null);
			assertEquals("[1.0,1.1)", map.get("org.osgi.service.event").get("version"));
		}
		finally {
			b.close();
		}
	}

	public static void testUnProvidedVersion() throws Exception {
		Builder b = new Builder();
		try {
			b.addClasspath(IO.getFile("jar/osgi.jar"));
			b.addClasspath(new File("bin"));
			b.setProperty(Constants.EXPORT_PACKAGE, "org.osgi.service.event;provide:=false");
			b.setProperty("Private-Package", "test.refer");
			Jar jar = b.build();
			assertTrue(b.check());
			String ip = jar.getManifest().getMainAttributes().getValue(Constants.IMPORT_PACKAGE);
			Parameters map = Processor.parseHeader(ip, null);
			assertEquals("[1.0,2)", map.get("org.osgi.service.event").get("version"));
		}
		finally {
			b.close();
		}
	}

	/**
	 * Complaint that exported versions were not picked up from external bundle.
	 */

	public static void testExportedVersionsNotPickedUp() throws Exception {
		Builder b = new Builder();
		try {
			b.addClasspath(IO.getFile("jar/jsr311-api-1.1.1.jar"));
			b.setProperty("Export-Package", "javax.ws.rs.core");
			Jar jar = b.build();
			assertTrue(b.check());
			String ip = jar.getManifest().getMainAttributes().getValue(Constants.EXPORT_PACKAGE);
			Parameters map = Processor.parseHeader(ip, null);
			assertEquals("1.1.1", map.get("javax.ws.rs.core").get("version"));
		}
		finally {
			b.close();
		}
	}

	/**
	 * Test where the version comes from: Manifest or packageinfo
	 * 
	 * @throws Exception
	 */
	public static void testExportVersionSource() throws Exception {
		Manifest manifest = new Manifest();
		manifest.getMainAttributes().putValue("Export-Package", "org.osgi.service.event;version=100");

		// Remove packageinfo
		Jar manifestOnly = new Jar(IO.getFile("jar/osgi.jar"));
		try {
			manifestOnly.remove("org/osgi/service/event/packageinfo");
			manifestOnly.setManifest(manifest);

			// Remove manifest
			Jar packageInfoOnly = new Jar(IO.getFile("jar/osgi.jar"));
			packageInfoOnly.setManifest(new Manifest());

			Jar both = new Jar(IO.getFile("jar/osgi.jar"));
			both.setManifest(manifest);

			// Only version in manifest
			Builder bms = new Builder();
			try {
				bms.addClasspath(manifestOnly);
				bms.setProperty("Export-Package", "org.osgi.service.event");
				bms.build();
				assertTrue(bms.check());

				String s = bms.getExports().getByFQN("org.osgi.service.event").get("version");
				assertEquals("100", s);

				// Only version in packageinfo
				Builder bpinfos = new Builder();
				bpinfos.addClasspath(packageInfoOnly);
				bpinfos.setProperty("Export-Package", "org.osgi.service.event");
				bpinfos.build();
				assertTrue(bpinfos.check());

				s = bpinfos.getExports().getByFQN("org.osgi.service.event").get("version");
				assertEquals("1.0.1", s);
			}
			finally {
				bms.close();
			}
		}
		finally {
			manifestOnly.close();
		}

	}

	/**
	 * Test where the version comes from: Manifest or packageinfo
	 * 
	 * @throws Exception
	 */
	public static void testImportVersionSource() throws Exception {
		Jar fromManifest = new Jar("manifestsource");
		Jar fromPackageInfo = new Jar("packageinfosource");
		Jar fromBoth = new Jar("both");
		try {
			Manifest mms = new Manifest();
			mms.getMainAttributes().putValue("Export-Package", "org.osgi.service.event; version=100");
			fromManifest.setManifest(mms);

			fromPackageInfo.putResource("org/osgi/service/event/packageinfo",
					new EmbeddedResource("version 99".getBytes(), 0));

			Manifest mboth = new Manifest();
			mboth.getMainAttributes().putValue("Export-Package", "org.osgi.service.event; version=101");
			fromBoth.putResource("org/osgi/service/event/packageinfo",
					new EmbeddedResource("version 199".getBytes(), 0));
			fromBoth.setManifest(mboth);

			// Only version in manifest
			Builder bms = new Builder();
			try {
				bms.addClasspath(fromManifest);
				bms.setProperty("Import-Package", "org.osgi.service.event");
				bms.build();
				assertTrue(bms.check("The JAR is empty"));
				String s = bms.getImports().getByFQN("org.osgi.service.event").get("version");
				assertEquals("[100.0,101)", s);
				// Only version in packageinfo
				Builder bpinfos = new Builder();
				try {
					bpinfos.addClasspath(fromPackageInfo);
					bpinfos.setProperty("Import-Package", "org.osgi.service.event");
					bpinfos.build();
					assertTrue(bms.check());
					s = bpinfos.getImports().getByFQN("org.osgi.service.event").get("version");
					assertEquals("[99.0,100)", s);

					// Version in manifest + packageinfo
					Builder bboth = new Builder();
					try {
						bboth.addClasspath(fromBoth);
						bboth.setProperty("Import-Package", "org.osgi.service.event");
						bboth.build();
						assertTrue(bms.check());
						s = bboth.getImports().getByFQN("org.osgi.service.event").get("version");
						assertEquals("[101.0,102)", s);
					}
					finally {
						bboth.close();
					}
				}
				finally {
					bpinfos.close();
				}
			}
			finally {
				bms.close();
			}

		}
		finally {
			fromManifest.close();
			fromPackageInfo.close();
			fromBoth.close();
		}
	}

	public static void testNoImportDirective() throws Exception {
		Builder b = new Builder();
		try {
			b.setProperty("Export-Package", "org.osgi.util.measurement, org.osgi.service.http;-noimport:=true");
			b.setProperty("Private-Package", "org.osgi.framework, test.refer");
			b.addClasspath(IO.getFile("jar/osgi.jar"));
			b.addClasspath(new File("bin"));
			Jar jar = b.build();
			assertTrue(b.check());

			Manifest m = jar.getManifest();
			String imports = m.getMainAttributes().getValue("Import-Package");
			assertTrue(imports.contains("org.osgi.util.measurement")); // referred
																		// to
																		// but
																		// no
																		// private
																		// references
																		// (does
																		// not
																		// use
																		// fw).
			assertFalse(imports.contains("org.osgi.service.http")); // referred
																	// to
																	// but no
																	// private
																	// references
																	// (does not
																	// use
																	// fw).
		}
		finally {
			b.close();
		}

	}

	public static void testNoImportDirective2() throws Exception {
		Builder b = new Builder();
		try {
			b.setProperty("Export-Package", "org.osgi.util.measurement;-noimport:=true, org.osgi.service.http");
			b.setProperty("Private-Package", "org.osgi.framework, test.refer");
			b.addClasspath(IO.getFile("jar/osgi.jar"));
			b.addClasspath(new File("bin"));
			Jar jar = b.build();
			assertTrue(b.check());

			Manifest m = jar.getManifest();
			String imports = m.getMainAttributes().getValue("Import-Package");
			assertFalse(imports.contains("org.osgi.util.measurement")); // referred
																		// to
																		// but
																		// no
																		// private
																		// references
																		// (does
																		// not
																		// use
																		// fw).
			assertTrue(imports.contains("org.osgi.service.http")); // referred
																	// to
																	// but no
																	// private
																	// references
																	// (does not
																	// use
																	// fw).

		}
		finally {
			b.close();
		}
	}

	public static void testAutoNoImport() throws Exception {
		Builder b = new Builder();
		try {
			b.setProperty(
					"Export-Package",
					"org.osgi.service.event, org.osgi.service.packageadmin, org.osgi.util.measurement, org.osgi.service.http;-noimport:=true");
			b.setProperty("Private-Package", "org.osgi.framework, test.refer");
			b.addClasspath(IO.getFile("jar/osgi.jar"));
			b.addClasspath(new File("bin"));
			Jar jar = b.build();
			assertTrue(b.check("has 1,  private references"));

			Manifest m = jar.getManifest();
			String imports = m.getMainAttributes().getValue("Import-Package");
			assertFalse(imports.contains("org.osgi.service.packageadmin")); // no
																			// internal
																			// references
			assertFalse(imports.contains("org.osgi.util.event")); // refers to
																	// private
																	// framework
			assertTrue(imports.contains("org.osgi.util.measurement")); // referred
																		// to
																		// but
																		// no
																		// private
																		// references
																		// (does
																		// not
																		// use
																		// fw).
			assertFalse(imports.contains("org.osgi.service.http")); // referred
																	// to
																	// but no
																	// private
																	// references
																	// (does not
																	// use
																	// fw).
		}
		finally {
			b.close();
		}
	}

	public static void testSimpleWab() throws Exception {
		Builder b = new Builder();
		try {
			b.setProperty("-wab", "");
			b.setProperty("Private-Package", "org.osgi.service.event");
			b.addClasspath(IO.getFile("jar/osgi.jar"));
			Jar jar = b.build();
			assertTrue(b.check());

			Manifest m = jar.getManifest();
			m.write(System.err);
			assertNotNull(b.getImports().getByFQN("org.osgi.framework"));
		}
		finally {
			b.close();
		}

	}

	public static void testWab() throws Exception {
		Builder b = new Builder();
		try {
			b.setProperty("-wablib", "jar/asm.jar, jar/easymock.jar");
			b.setProperty("-wab", "jar/osgi.jar");
			b.setProperty("-includeresource", "OSGI-INF/xml/x.xml;literal=\"text\"");
			b.setProperty("Private-Package", "org.osgi.framework");
			b.addClasspath(IO.getFile("jar/osgi.jar"));
			Jar jar = b.build();
			assertTrue(b.check());

			Manifest m = jar.getManifest();
			assertNotNull(m);
			assertEquals("WEB-INF/classes,WEB-INF/lib/asm.jar,WEB-INF/lib/easymock.jar", m.getMainAttributes()
					.getValue("Bundle-ClassPath"));
			assertNotNull(jar.getResource("WEB-INF/lib/asm.jar"));
			assertNotNull(jar.getResource("WEB-INF/classes/org/osgi/framework/BundleContext.class"));
			assertNotNull(jar.getResource("osgi.jar"));
			assertNotNull(jar.getResource("OSGI-INF/xml/x.xml"));
		}
		finally {
			b.close();
		}
	}

	public static void testRemoveHeaders() throws Exception {
		Builder b = new Builder();
		try {
			b.setProperty("Private-Package", "org.osgi.framework");
			b.setProperty("T1", "1");
			b.setProperty("T2", "1");
			b.setProperty("T1_2", "1");
			b.setProperty("-removeheaders", "!T1_2,T1*");
			b.addClasspath(IO.getFile("jar/osgi.jar"));
			Jar jar = b.build();
			assertTrue(b.check());

			Manifest m = jar.getManifest();
			assertNotNull(m);
			assertEquals("1", m.getMainAttributes().getValue("T2"));
			assertEquals("1", m.getMainAttributes().getValue("T1_2"));
			assertEquals(null, m.getMainAttributes().getValue("T1"));
		}
		finally {
			b.close();
		}
	}

	public static void testNoManifest() throws Exception {
		Builder b = new Builder();
		try {
			b.setProperty("-nomanifest", "true");
			b.setProperty(Constants.BUNDLE_CLASSPATH, "WEB-INF/classes");
			b.setProperty("Include-Resource", "WEB-INF/classes=@jar/asm.jar");
			Jar jar = b.build();
			assertTrue(b.check());

			File f = new File("tmp.jar");
			f.deleteOnExit();
			jar.write(f);

			JarInputStream jin = new JarInputStream(new FileInputStream(f));
			Manifest m = jin.getManifest();
			assertNull(m);
		}
		finally {
			b.close();
		}
	}

	public static void testClassesonNoBCP() throws Exception {
		Builder b = new Builder();
		try {
			b.setProperty("-resourceonly", "true");
			b.setProperty("Include-Resource", "WEB-INF/classes=@jar/asm.jar");
			b.setProperty("-nomanifest", "true");
			b.build();
			assertTrue(b.check("Classes found in the wrong directory"));
		}
		finally {
			b.close();
		}
	}

	public static void testClassesonBCP() throws Exception {
		Builder b = new Builder();
		try {
			b.setProperty("-resourceonly", "true");
			b.setProperty("Include-Resource", "WEB-INF/classes=@jar/asm.jar");
			b.setProperty("Bundle-ClassPath", "WEB-INF/classes");
			b.build();
			assertTrue(b.check());
		}
		finally {
			b.close();
		}
	}

	/**
	 * #196 StringIndexOutOfBoundsException in Builder.getClasspathEntrySuffix
	 * If a class path entry was changed the isInScope threw an exception
	 * because it assumed all cpes were directories.
	 * 
	 * @throws Exception
	 */
	public static void testInScopeClasspathEntry() throws Exception {
		Builder b = new Builder();
		try {
			b.setProperty("Export-Package", "aQute.bnd.*");
			b.addClasspath(new File("bin"));
			b.addClasspath(IO.getFile("jar/osgi.jar"));

			List<File> project = Arrays.asList(b.getFile("bin/aQute/bnd/build/Project.class"));
			assertTrue(b.isInScope(project));
			List<File> cpe = Arrays.asList(b.getFile("jar/osgi.jar"));
			assertTrue(b.isInScope(cpe));
		}
		finally {
			b.close();
		}
	}

	public static void testInScopeExport() throws Exception {
		Builder b = new Builder();
		try {
			b.setProperty("Export-Package", "aQute.bnd.*");
			b.addClasspath(new File("bin"));
			List<File> project = Arrays.asList(b.getFile("bin/aQute/bnd/build/Project.class"));
			assertTrue(b.isInScope(project));
			List<File> nonexistent = Arrays.asList(b.getFile("bin/aQute/bnd/build/Abc.xyz"));
			assertTrue(b.isInScope(nonexistent));
			List<File> outside = Arrays.asList(b.getFile("bin/test/AnalyzerTest.class"));
			assertFalse(b.isInScope(outside));
		}
		finally {
			b.close();
		}
	}

	public static void testInScopePrivate() throws Exception {
		Builder b = new Builder();
		b.setProperty("Private-Package", "!aQute.bnd.build,aQute.bnd.*");
		b.addClasspath(new File("bin"));
		List<File> project = Arrays.asList(b.getFile("bin/aQute/bnd/build/Project.class"));
		assertFalse(b.isInScope(project));
		List<File> nonexistent = Arrays.asList(b.getFile("bin/aQute/bnd/acb/Def.xyz"));
		assertTrue(b.isInScope(nonexistent));
		List<File> outside = Arrays.asList(b.getFile("bin/test/AnalyzerTest.class"));
		assertFalse(b.isInScope(outside));
	}

	public static void testInScopeResources() throws Exception {
		Builder b = new Builder();
		try {
			b.setProperty("Include-Resource",
					"@a.jar/!xya.txt,{@b.jar/!xya.txt}, -@c.jar/!xya.txt, dir, x=dirb, {-x=dirc}");
			assertFalse(b.isInScope(Arrays.asList(b.getFile("x.jar"))));
			assertTrue(b.isInScope(Arrays.asList(b.getFile("a.jar"))));
			assertTrue(b.isInScope(Arrays.asList(b.getFile("b.jar"))));
			assertTrue(b.isInScope(Arrays.asList(b.getFile("dir/a.jar"))));
			assertTrue(b.isInScope(Arrays.asList(b.getFile("dir/x.jar"))));
			assertTrue(b.isInScope(Arrays.asList(b.getFile("dir/x.jar"))));
			assertTrue(b.isInScope(Arrays.asList(b.getFile("dirb/x.jar"))));
			assertTrue(b.isInScope(Arrays.asList(b.getFile("dirb/x.jar"))));
			assertTrue(b.isInScope(Arrays.asList(b.getFile("dirc/x.jar"))));
		}
		finally {
			b.close();
		}
	}

	public static void testExtra() throws Exception {
		Builder b = new Builder();
		try {
			b.setProperty("Include-Resource",
					"jar/osgi.jar;extra=itworks, www/xyz.jar=jar/osgi.jar;extra='italsoworks'");
			b.setProperty("-resourceonly", "true");
			Jar jar = b.build();
			assertTrue(b.check());

			Resource r = jar.getResource("osgi.jar");
			assertNotNull(r);
			assertEquals("itworks", r.getExtra());
			Resource r2 = jar.getResource("www/xyz.jar");
			assertNotNull(r2);
			assertEquals("italsoworks", r2.getExtra());
		}
		finally {
			b.close();
		}
	}

	/**
	 * Got a split package warning during verify when private overlaps with
	 * export
	 */
	public static void testSplitWhenPrivateOverlapsExport() throws Exception {
		Builder b = new Builder();
		try {
			b.addClasspath(IO.getFile("jar/osgi.jar"));
			b.setProperty("Private-Package", "org.osgi.service.*");
			b.setProperty("Export-Package", "org.osgi.service.event");
			b.build();
			assertTrue(b.check());
		}
		finally {
			b.close();
		}
	}

	/**
	 * This test checks if
	 * 
	 * @throws Exception
	 */

	public static void testMacroBasedExpansion() throws Exception {
		Processor proc = new Processor();

		Builder builder = new Builder(proc);
		try {
			builder.setProperty("Export-Package", "${spec.packages}");
			proc.setProperty("spec.packages", "${core.packages}, ${cmpn.packages}, ${mobile.packages}");
			proc.setProperty("core.specs", "org.osgi.service.packageadmin, org.osgi.service.permissionadmin");
			proc.setProperty("core.packages", "${replace;${core.specs};.+;$0.*}");
			proc.setProperty("cmpn.specs", "org.osgi.service.event, org.osgi.service.cu");
			proc.setProperty("cmpn.packages", "${replace;${cmpn.specs};.+;$0.*}");
			proc.setProperty("mobile.specs", "org.osgi.service.wireadmin, org.osgi.service.log, org.osgi.service.cu");
			proc.setProperty("mobile.packages", "${replace;${mobile.specs};.+;$0.*}");
			builder.addClasspath(IO.getFile("jar/osgi.jar"));

			Jar jar = builder.build();
			// The total set is not uniqued so we're having an unused pattern
			// this could be solved with ${uniq;${spec.packages}} but this is
			// just
			// another test
			assertTrue(builder.check("Unused Export-Package instructions: \\[org.osgi.service.cu.\\*~\\]"));
			Domain domain = Domain.domain(jar.getManifest());

			Parameters h = domain.getExportPackage();
			assertTrue(h.containsKey("org.osgi.service.cu"));
			assertTrue(h.containsKey("org.osgi.service.cu.admin"));
		}
		finally {
			builder.close();
			proc.close();
		}
	}

	/**
	 * Make resolution dependent on the fact that a package is on the classpath
	 * or not
	 */

	public static void testConditionalResolution() throws Exception {
		Builder b = new Builder();
		try {
			b.setProperty("res", "${if;${exporters;${@package}};mandatory;optional}");
			b.setProperty("Import-Package", "*;resolution:=\\${res}");
			b.setProperty("Export-Package", "org.osgi.service.io, org.osgi.service.log");
			b.addClasspath(IO.getFile("jar/osgi.jar"));
			b.build();
			assertTrue(b.check());

			Map<String,String> ioimports = b.getImports().getByFQN("javax.microedition.io");
			Map<String,String> fwimports = b.getImports().getByFQN("org.osgi.framework");

			assertNotNull(ioimports);
			assertNotNull(fwimports);
			assertTrue(ioimports.containsKey("resolution:"));
			assertTrue(fwimports.containsKey("resolution:"));
			assertEquals("optional", ioimports.get("resolution:"));
			assertEquals("mandatory", fwimports.get("resolution:"));
		}
		finally {
			b.close();
		}

	}

	/**
	 * Test private imports. We first build a jar with a import:=private packge.
	 * Then place it
	 * 
	 * @throws Exception
	 */

	public static void testClassnames() throws Exception {
		Builder b = new Builder();
		try {
			b.addClasspath(IO.getFile("jar/osgi.jar"));
			b.addClasspath(IO.getFile("jar/ds.jar"));
			b.addClasspath(IO.getFile("jar/ifc112.jar"));
			b.setProperty("Export-Package", "*");
			b.setProperty("C1", "${classes;implementing;org.osgi.service.component.*}");
			b.setProperty("C2", "${classes;extending;org.xml.sax.helpers.*}");
			b.setProperty("C3", "${classes;importing;org.xml.sax}");
			b.setProperty("C4", "${classes;named;*Parser*}");
			b.setProperty("C5", "${classes;named;*Parser*;version;45.*}");
			Jar jar = b.build();
			assertTrue(b.check());

			Manifest m = jar.getManifest();
			m.write(System.err);
			Attributes main = m.getMainAttributes();
			assertList(
					asl("org.eclipse.equinox.ds.service.ComponentContextImpl,org.eclipse.equinox.ds.service.ComponentFactoryImpl,org.eclipse.equinox.ds.service.ComponentInstanceImpl"),
					asl(main.getValue("C1")));
			assertList(asl("org.eclipse.equinox.ds.parser.ElementHandler, "
					+ "org.eclipse.equinox.ds.parser.IgnoredElement,"
					+ "org.eclipse.equinox.ds.parser.ImplementationElement,"
					+ "org.eclipse.equinox.ds.parser.ParserHandler, "
					+ "org.eclipse.equinox.ds.parser.PropertiesElement,"
					+ "org.eclipse.equinox.ds.parser.PropertyElement, "
					+ "org.eclipse.equinox.ds.parser.ProvideElement, "
					+ "org.eclipse.equinox.ds.parser.ReferenceElement, "
					+ "org.eclipse.equinox.ds.parser.ServiceElement,"
					+ "org.eclipse.equinox.ds.parser.ComponentElement"), asl(main.getValue("C2")));
			assertList(
					asl("org.eclipse.equinox.ds.parser.ComponentElement,org.eclipse.equinox.ds.parser.ElementHandler,org.eclipse.equinox.ds.parser.IgnoredElement,org.eclipse.equinox.ds.parser.ImplementationElement,org.eclipse.equinox.ds.parser.Parser,org.eclipse.equinox.ds.parser.ParserHandler,org.eclipse.equinox.ds.parser.PropertiesElement,org.eclipse.equinox.ds.parser.PropertyElement,org.eclipse.equinox.ds.parser.ProvideElement,org.eclipse.equinox.ds.parser.ReferenceElement,org.eclipse.equinox.ds.parser.ServiceElement"),
					asl(main.getValue("C3")));
			assertList(
					asl("org.eclipse.equinox.ds.parser.XMLParserNotAvailableException,org.eclipse.equinox.ds.parser.Parser,org.eclipse.equinox.ds.parser.ParserHandler,netscape.application.HTMLParser,org.eclipse.equinox.ds.parser.ParserConstants,org.osgi.util.xml.XMLParserActivator"),
					asl(main.getValue("C4")));
			assertEquals("netscape.application.HTMLParser", main.getValue("C5"));
		}
		finally {
			b.close();
		}
	}

	static void assertList(Collection<String> a, Collection<String> b) {
		List<String> onlyInA = new ArrayList<String>();
		onlyInA.addAll(a);
		onlyInA.removeAll(b);

		List<String> onlyInB = new ArrayList<String>();
		onlyInB.addAll(b);
		onlyInB.removeAll(a);

		if (onlyInA.isEmpty() && onlyInB.isEmpty())
			return;

		fail("Lists are not equal, only in A: " + onlyInA + ",\n   and only in B: " + onlyInB);
	}

	static Collection<String> asl(String s) {
		return new TreeSet<String>(Processor.split(s));
	}

	public static void testImportMicroNotTruncated() throws Exception {
		Builder b = new Builder();
		try {
			b.addClasspath(IO.getFile("jar/osgi.jar"));
			b.setProperty("Import-Package", "org.osgi.service.event;version=${@}");
			b.build();
			assertTrue(b.check("The JAR is empty"));
			String s = b.getImports().getByFQN("org.osgi.service.event").get("version");
			assertEquals("1.0.1", s);
		}
		finally {
			b.close();
		}
	}

	public static void testImportMicroTruncated() throws Exception {
		Builder b = new Builder();
		try {
			b.addClasspath(IO.getFile("jar/osgi.jar"));
			b.setProperty("Import-Package", "org.osgi.service.event");
			b.build();
			assertTrue(b.check("The JAR is empty"));

			String s = b.getImports().getByFQN("org.osgi.service.event").get("version");
			assertEquals("[1.0,2)", s);
		}
		finally {
			b.close();
		}

	}

	public static void testMultipleExport2() throws Exception {
		File cp[] = {
			IO.getFile("jar/asm.jar")
		};
		Builder bmaker = new Builder();
		try {
			Properties p = new Properties();
			p.setProperty("Export-Package",
					"org.objectweb.asm;version=1.1, org.objectweb.asm;version=1.2, org.objectweb.asm;version=2.3");
			bmaker.setProperties(p);
			bmaker.setClasspath(cp);
			Jar jar = bmaker.build();
			assertTrue(bmaker.check());
			jar.getManifest().write(System.err);
			Manifest m = jar.getManifest();
			m.write(System.err);
			String ip = m.getMainAttributes().getValue("Export-Package");
			assertTrue(ip.indexOf("org.objectweb.asm;version=\"1.1\"") >= 0);
			assertTrue(ip.indexOf("org.objectweb.asm;version=\"1.2\"") >= 0);
			assertTrue(ip.indexOf("org.objectweb.asm;version=\"2.3\"") >= 0);
		}
		finally {
			bmaker.close();
		}
	}

	public static void testBsnAssignmentNoFile() throws Exception {
		Properties p = new Properties();
		p.setProperty("Private-Package", "org.objectweb.asm");
		Attributes m = setup(p, null).getMainAttributes();

		// We use properties so the default BSN is then the project name
		// because that is the base directory
		assertEquals(m.getValue("Bundle-SymbolicName"), "biz.aQute.bndlib.tests");

		// The file name for the properties is not bnd.bnd, so the
		// name of the properties file is the default bsn
		m = setup(null, IO.getFile("src/test/com.acme/defaultbsn.bnd")).getMainAttributes();
		assertEquals("com.acme.defaultbsn", m.getValue("Bundle-SymbolicName"));

		// If the file is called bnd.bnd, then we take the parent directory
		m = setup(null, IO.getFile("src/test/com.acme/bnd.bnd")).getMainAttributes();
		assertEquals("com.acme", m.getValue("Bundle-SymbolicName"));

		// If the file is called bnd.bnd, then we take the parent directory
		m = setup(null, IO.getFile("src/test/com.acme/setsbsn.bnd")).getMainAttributes();
		assertEquals("is.a.set.bsn", m.getValue("Bundle-SymbolicName"));

		// This sets the bsn, se we should see it back
		p.setProperty("Bundle-SymbolicName", "this.is.my.test");
		m = setup(p, null).getMainAttributes();
		assertEquals(m.getValue("Bundle-SymbolicName"), "this.is.my.test");
	}

	public static Manifest setup(Properties p, File f) throws Exception {
		File cp[] = {
			IO.getFile("jar/asm.jar")
		};
		Builder bmaker = new Builder();
		if (f != null)
			bmaker.setProperties(f);
		else
			bmaker.setProperties(p);
		bmaker.setClasspath(cp);
		Jar jar = bmaker.build();
		assertTrue(bmaker.check());
		Manifest m = jar.getManifest();
		return m;
	}

	public static void testDuplicateExport() throws Exception {
		File cp[] = {
			IO.getFile("jar/asm.jar")
		};
		Builder bmaker = new Builder();
		try {
			Properties p = new Properties();
			p.setProperty("Import-Package", "*");
			p.setProperty("Export-Package", "org.*;version=1.2,org.objectweb.asm;version=1.3");
			bmaker.setProperties(p);
			bmaker.setClasspath(cp);
			Jar jar = bmaker.build();
			assertTrue(bmaker.check());
			Manifest m = jar.getManifest();
			m.write(System.err);
			String ip = m.getMainAttributes().getValue("Export-Package");
			assertTrue(ip.indexOf("org.objectweb.asm;version=\"1.2\"") >= 0);
		}
		finally {
			bmaker.close();
		}
	}

	public static void testNoExport() throws Exception {
		File cp[] = {
			IO.getFile("jar/asm.jar")
		};
		Builder bmaker = new Builder();
		try {
			Properties p = new Properties();
			p.setProperty("Import-Package", "*");
			p.setProperty("Export-Package", "org.*'");
			bmaker.setProperties(p);
			bmaker.setClasspath(cp);
			Jar jar = bmaker.build();
			assertTrue(bmaker.check());

			jar.getManifest().write(System.err);
			Manifest m = jar.getManifest();
			String ip = m.getMainAttributes().getValue("Export-Package");
			assertTrue(ip.indexOf("org.objectweb.asm") >= 0);
		}
		finally {
			bmaker.close();
		}
	}

	public static void testHardcodedImport() throws Exception {
		File cp[] = {
			IO.getFile("jar/asm.jar")
		};
		Builder bmaker = new Builder();
		try {
			Properties p = new Properties();
			p.setProperty("Import-Package", "whatever,*");
			p.setProperty("Export-Package", "org.*");
			bmaker.setProperties(p);
			bmaker.setClasspath(cp);
			Jar jar = bmaker.build();
			assertTrue(bmaker.check());

			Manifest m = jar.getManifest();
			String ip = m.getMainAttributes().getValue("Import-Package");
			assertTrue(ip.indexOf("whatever") >= 0);
		}
		finally {
			bmaker.close();
		}
	}

	public static void testCopyDirectory() throws Exception {
		Builder bmaker = new Builder();
		try {
			Properties p = new Properties();
			p.setProperty("-resourceonly", "true");
			p.setProperty("Include-Resource", "bnd=bnd");
			bmaker.setProperties(p);
			Jar jar = bmaker.build();
			assertTrue(bmaker.check());

			Map<String,Resource> map = jar.getDirectories().get("bnd");
			assertNotNull(map);
			assertEquals(2, map.size());
		}
		finally {
			bmaker.close();
		}
	}

	/**
	 * There is an error that gives a split package when you export a package
	 * that is also private I think.
	 * 
	 * @throws Exception
	 */
	public static void testSplitOnExportAndPrivate() throws Exception {
		File cp[] = {
			IO.getFile("jar/asm.jar")
		};
		Builder bmaker = new Builder();
		try {
			Properties p = new Properties();
			p.setProperty("Export-Package", "org.objectweb.asm.signature");
			p.setProperty("Private-Package", "org.objectweb.asm");
			bmaker.setProperties(p);
			bmaker.setClasspath(cp);
			bmaker.build();
			assertTrue(bmaker.check());
		}
		finally {
			bmaker.close();
		}
	}

	public static void testConduit() throws Exception {
		Properties p = new Properties();
		p.setProperty("-conduit", "jar/asm.jar");
		Builder b = new Builder();
		try {
			b.setProperties(p);
			Jar jars[] = b.builds();
			assertTrue(b.check());
			assertNotNull(jars);
			assertEquals(1, jars.length);
			assertEquals("ASM", jars[0].getManifest().getMainAttributes().getValue("Implementation-Title"));
		}
		finally {
			b.close();
		}
	}

	/**
	 * Export a package that was loaded with resources
	 * 
	 * @throws Exception
	 */
	public static void testExportSyntheticPackage() throws Exception {
		Builder bmaker = new Builder();
		try {
			Properties p = new Properties();
			p.setProperty("-resourceonly", "true");
			p.setProperty("Include-Resource", "resources=jar");
			p.setProperty("-exportcontents", "resources");
			bmaker.setProperties(p);
			Jar jar = bmaker.build();
			assertTrue(bmaker.check());

			Manifest manifest = jar.getManifest();
			String header = manifest.getMainAttributes().getValue("Export-Package");
			System.err.println(header);
			assertTrue(header.indexOf("resources") >= 0);
		}
		finally {
			bmaker.close();
		}
	}

	/**
	 * Exporting packages in META-INF
	 * 
	 * @throws Exception
	 */
	public static void testMETAINF() throws Exception {
		File cp[] = {
				new File("src"), IO.getFile("jar/asm.jar")
		};
		Builder bmaker = new Builder();
		try {
			Properties p = new Properties();
			p.setProperty("Include-Resource", "META-INF/xyz/asm.jar=jar/asm.jar");
			p.setProperty("Export-Package", "META-INF/xyz, org.*");
			bmaker.setProperties(p);
			bmaker.setClasspath(cp);
			Jar jar = bmaker.build();
			assertTrue(bmaker.check("Invalid package name: 'META-INF"));

			jar.getManifest().write(System.err);
			Manifest manifest = jar.getManifest();
			String header = manifest.getMainAttributes().getValue("Export-Package");
			assertTrue(header.indexOf("META-INF.xyz") >= 0);
		}
		finally {
			bmaker.close();
		}
	}

	/**
	 * Bnd cleans up versions if they do not follow the OSGi rule. Check a
	 * number of those versions.
	 * 
	 * @throws Exception
	 */
	public static void testVersionCleanup() throws Exception {
		assertVersion("1.201209072340200", "1.0.0.201209072340200");
		assertVersion("000001.0003.00000-SNAPSHOT", "1.3.0.SNAPSHOT");
		assertVersion("000000.0000.00000-SNAPSHOT", "0.0.0.SNAPSHOT");
		assertVersion("0-SNAPSHOT", "0.0.0.SNAPSHOT");
		assertVersion("1.3.0.0-0-01-0-SNAPSHOT", "1.3.0.0-0-01-0-SNAPSHOT");
		assertVersion("1.3.0.0-0-01-0", "1.3.0.0-0-01-0");
		assertVersion("0.9.0.1.2.3.4.5-incubator-SNAPSHOT", "0.9.0.incubator-SNAPSHOT");
		assertVersion("0.4aug123", "0.0.0.4aug123");
		assertVersion("0.9.4aug123", "0.9.0.4aug123");
		assertVersion("0.9.0.4aug123", "0.9.0.4aug123");

		assertVersion("1.2.3", "1.2.3");
		assertVersion("1.2.3-123", "1.2.3.123");
		assertVersion("1.2.3.123", "1.2.3.123");
		assertVersion("1.2.3.123x", "1.2.3.123x");
		assertVersion("1.123x", "1.0.0.123x");

		assertVersion("0.9.0.4.3.2.1.0.4aug123", "0.9.0.4aug123");
		assertVersion("0.9.0.4aug123", "0.9.0.4aug123");

		assertVersion("0.9.0.4.3.4.5.6.6", "0.9.0.6");

		assertVersion("0.9.0-incubator-SNAPSHOT", "0.9.0.incubator-SNAPSHOT");
		assertVersion("1.2.3.x", "1.2.3.x");
		assertVersion("1.2.3", "1.2.3");
		assertVersion("1.2", "1.2");
		assertVersion("1", "1");
		assertVersion("1.2.x", "1.2.0.x");
		assertVersion("1.x", "1.0.0.x");
		assertVersion("1.2.3-x", "1.2.3.x");
		assertVersion("1.2:x", "1.2.0.x");
		assertVersion("1.2-snapshot", "1.2.0.snapshot");
		assertVersion("1#x", "1.0.0.x");
		assertVersion("1.&^%$#date2007/03/04", "1.0.0.date20070304");
	}

	static void assertVersion(String input, String expected) {
		assertEquals(expected, Builder.cleanupVersion(input));
	}

	/**
	 * -exportcontents provides a header that is only relevant in the analyze
	 * phase, it augments the Export-Package header.
	 */

	public static void testExportContents() throws Exception {
		Builder builder = new Builder();
		try {
			builder.setProperty(Analyzer.INCLUDE_RESOURCE, "test/activator/inherits=src/test/activator/inherits");
			builder.setProperty("-exportcontents", "*;x=true;version=1");
			builder.build();
			assertTrue(builder.check());
			Manifest manifest = builder.calcManifest();
			Attributes main = manifest.getMainAttributes();
			Parameters map = OSGiHeader.parseHeader(main.getValue("Export-Package"));
			Map<String,String> export = map.get("test.activator.inherits");
			assertNotNull(export);
			assertEquals("1", export.get("version"));
			assertEquals("true", export.get("x"));
		}
		finally {
			builder.close();
		}
	}

	/**
	 * Check Conditional package. First import a subpackage then let the
	 * subpackage import a super package. This went wrong in the OSGi build. We
	 * see such a pattern in the Spring jar. The package
	 * org.springframework.beans.factory.access refers to
	 * org.springframework.beans.factory and org.springframework.beans. The
	 */
	public static void testConditionalBaseSuper() throws Exception {
		Builder b = new Builder();
		try {
			b.setProperty(Constants.CONDITIONALPACKAGE, "test.top.*");
			b.setProperty(Constants.PRIVATEPACKAGE, "test.top.middle.bottom");
			b.addClasspath(new File("bin"));
			Jar dot = b.build();
			assertTrue(b.check());

			assertNotNull(dot.getResource("test/top/middle/bottom/Bottom.class"));
			assertNotNull(dot.getResource("test/top/middle/Middle.class"));
			assertNotNull(dot.getResource("test/top/Top.class"));

			assertFalse(b.getImports().getByFQN("test.top") != null);
			assertFalse(b.getImports().getByFQN("test.top.middle") != null);
			assertFalse(b.getImports().getByFQN("test.top.middle.bottom") != null);
		}
		finally {
			b.close();
		}
	}

	/**
	 * It looks like Conditional-Package can add the same package multiple
	 * times. So lets test this.
	 */
	public static void testConditional2() throws Exception {
		Properties base = new Properties();
		base.put(Analyzer.EXPORT_PACKAGE, "org.osgi.service.log");
		base.put(Analyzer.CONDITIONAL_PACKAGE, "org.osgi.*");
		Builder analyzer = new Builder();
		try {
			analyzer.setProperties(base);
			analyzer.setClasspath(new File[] {
				IO.getFile("jar/osgi.jar")
			});
			analyzer.build();
			assertTrue(analyzer.check("private references"));
			Jar jar = analyzer.getJar();
			assertTrue(analyzer.check());
			assertNotNull(analyzer.getExports().getByFQN("org.osgi.service.log"));
			assertNotNull(jar.getDirectories().get("org/osgi/framework"));
		}
		finally {
			analyzer.close();
		}
	}

	/**
	 * Test the strategy: error
	 */
	public static void testStrategyError() throws Exception {
		Properties base = new Properties();
		base.put(Analyzer.EXPORT_PACKAGE, "*;-split-package:=error");
		Builder analyzer = new Builder();
		try {
			analyzer.setClasspath(new File[] {
					IO.getFile("jar/asm.jar"), IO.getFile("jar/asm.jar")
			});
			analyzer.setProperties(base);
			analyzer.build();
			assertTrue(analyzer.check("The JAR is empty", "Split package"));
		}
		finally {
			analyzer.close();
		}
	}

	/**
	 * Test the strategy: default
	 */
	public static void testStrategyDefault() throws Exception {
		Properties base = new Properties();
		base.put(Analyzer.EXPORT_PACKAGE, "*");
		Builder analyzer = new Builder();
		try {
			analyzer.setClasspath(new File[] {
					IO.getFile("jar/asm.jar"), IO.getFile("jar/asm.jar")
			});
			analyzer.setProperties(base);
			analyzer.build();
			assertEquals(2, analyzer.getWarnings().size());
			assertTrue(analyzer.check("Split package"));
		}
		finally {
			analyzer.close();
		}
	}

	/**
	 * Test the strategy: merge-first
	 */
	public static void testStrategyMergeFirst() throws Exception {
		Properties base = new Properties();
		base.put(Analyzer.EXPORT_PACKAGE, "*;-split-package:=merge-first");
		Builder analyzer = new Builder();
		try {
			analyzer.setClasspath(new File[] {
					IO.getFile("jar/asm.jar"), IO.getFile("jar/asm.jar")
			});
			analyzer.setProperties(base);
			analyzer.build();
			assertTrue(analyzer.check());
		}
		finally {
			analyzer.close();
		}
	}

	/**
	 * Test the strategy: merge-last
	 */
	public static void testStrategyMergeLast() throws Exception {
		Properties base = new Properties();
		base.put(Analyzer.EXPORT_PACKAGE, "*;-split-package:=merge-last");
		Builder analyzer = new Builder();
		try {
			analyzer.setClasspath(new File[] {
					IO.getFile("jar/asm.jar"), IO.getFile("jar/asm.jar")
			});
			analyzer.setProperties(base);
			analyzer.build();
			assertTrue(analyzer.check());
		}
		finally {
			analyzer.close();
		}
	}

	/**
	 * Test Resource inclusion that do not exist
	 * 
	 * @throws Exception
	 */
	public static void testResourceNotFound() throws Exception {
		Properties base = new Properties();
		base.put(Analyzer.EXPORT_PACKAGE, "*;x-test:=true");
		base.put(Analyzer.INCLUDE_RESOURCE, "does_not_exist");
		Builder analyzer = new Builder();
		try {
			analyzer.setClasspath(new File[] {
				IO.getFile("jar/asm.jar")
			});
			analyzer.setProperties(base);
			analyzer.build();
			assertTrue(analyzer.check("file does not exist: does_not_exist"));
		}
		finally {
			analyzer.close();
		}
	}

	/**
	 * Check if we can use findpath to build the Bundle-Classpath.
	 */

	public static void testFindPathInBundleClasspath() throws Exception {
		Properties base = new Properties();
		base.put(Analyzer.INCLUDE_RESOURCE, "jar=jar");
		base.put(Analyzer.BUNDLE_CLASSPATH, "${findpath;jar/.{1,4}\\.jar}");
		Builder analyzer = new Builder();
		try {
			analyzer.setProperties(base);
			analyzer.build();
			assertTrue(analyzer.check());

			Manifest manifest = analyzer.getJar().getManifest();
			String bcp = manifest.getMainAttributes().getValue("Bundle-Classpath");

			assertTrue(bcp.indexOf("ds.jar") >= 0);
			assertTrue(bcp.indexOf("asm.jar") >= 0);
			assertTrue(bcp.indexOf("bcel.jar") >= 0);
			assertTrue(bcp.indexOf("mina.jar") >= 0);
			assertTrue(bcp.indexOf("rox.jar") >= 0);
			assertTrue(bcp.indexOf("osgi.jar") >= 0);
		}
		finally {
			analyzer.close();
		}
	}

	/**
	 * Check if we export META-INF when we export the complete classpath.
	 */

	public static void testVersionCleanupAll() throws Exception {
		Properties base = new Properties();
		base.put(Analyzer.EXPORT_PACKAGE, "*");
		base.put(Analyzer.BUNDLE_VERSION, "0.9.0-incubator-SNAPSHOT");
		Builder analyzer = new Builder();
		try {
			analyzer.setClasspath(new File[] {
				IO.getFile("jar/asm.jar")
			});
			analyzer.setProperties(base);
			analyzer.build();

			assertTrue(analyzer.check());
			Manifest manifest = analyzer.getJar().getManifest();
			String version = manifest.getMainAttributes().getValue(Analyzer.BUNDLE_VERSION);
			assertEquals("0.9.0.incubator-SNAPSHOT", version);
		}
		finally {
			analyzer.close();
		}
	}

	/**
	 * We are only adding privately the core equinox ds package. We then add
	 * conditionally all packages that should belong to this as well as any OSGi
	 * interfaces.
	 * 
	 * @throws Exception
	 */
	public static void testConditional() throws Exception {
		File cp[] = {
				IO.getFile("jar/osgi.jar"), IO.getFile("jar/ds.jar")
		};
		Builder bmaker = new Builder();
		try {
			Properties p = new Properties();
			p.put("Import-Package", "*");
			p.put("Private-Package", "org.eclipse.equinox.ds");
			p.put("Conditional-Package", "org.eclipse.equinox.ds.*, org.osgi.service.*");
			bmaker.setProperties(p);
			bmaker.setClasspath(cp);
			bmaker.build();
			assertTrue(bmaker.check());

			assertTrue(bmaker.getContained().getByFQN("org.eclipse.equinox.ds.instance") != null);
			assertTrue(bmaker.getContained().getByFQN("org.eclipse.equinox.ds.model") != null);
			assertTrue(bmaker.getContained().getByFQN("org.eclipse.equinox.ds.parser") != null);
			assertTrue(bmaker.getContained().getByFQN("org.osgi.service.cm") != null);
			assertTrue(bmaker.getContained().getByFQN("org.osgi.service.component") != null);
			assertFalse(bmaker.getContained().getByFQN("org.osgi.service.wireadmin") != null);
		}
		finally {
			bmaker.close();
		}
	}

	/**
	 * Check if we export META-INF when we export the complete classpath.
	 */

	public static void testMetaInfExport() throws Exception {
		Properties base = new Properties();
		base.put(Analyzer.EXPORT_PACKAGE, "*");
		Builder analyzer = new Builder();
		try {
			analyzer.setClasspath(new File[] {
				IO.getFile("jar/asm.jar")
			});
			analyzer.setProperties(base);
			analyzer.build();
			assertTrue(analyzer.check());
			assertFalse(analyzer.getExports().getByFQN("META-INF") != null);
			assertTrue(analyzer.getExports().getByFQN("org.objectweb.asm") != null);
		}
		finally {
			analyzer.close();
		}
	}

	/**
	 * Check that the activator is found.
	 * 
	 * @throws Exception
	 */
	public static void testFindActivator() throws Exception {
		Builder bmaker = new Builder();
		try {
			Properties p = new Properties();
			p.put("Bundle-Activator", "test.activator.Activator");
			p.put("build", "xyz"); // for @Version annotation
			p.put("Private-Package", "test.*");
			bmaker.setProperties(p);
			bmaker.setClasspath(new File[] {
				new File("bin")
			});
			Jar jar = bmaker.build();
			assertTrue(bmaker.check());
			report("testFindActivator", bmaker, jar);
			assertEquals(0, bmaker.getErrors().size());
			assertEquals(0, bmaker.getWarnings().size());
		}
		finally {
			bmaker.close();
		}
	}

	public static void testImportVersionRange() throws Exception {
		assertVersionEquals("[1.1,2.0)", "[1.1,2.0)");
		assertVersionEquals("[${@},2.0)", "[1.3,2.0)");
		assertVersionEquals("[${@},${@}]", "[1.3,1.3]");
	}

	static void assertVersionEquals(String input, String output) throws Exception {
		File cp[] = {
			IO.getFile("jar/osgi.jar")
		};
		Builder bmaker = new Builder();
		try {
			bmaker.setClasspath(cp);
			Properties p = new Properties();
			p.put(Analyzer.EXPORT_PACKAGE, "test.activator");
			p.put(Analyzer.IMPORT_PACKAGE, "org.osgi.framework;version=\"" + input + "\"");
			bmaker.setProperties(p);
			bmaker.build();
			assertTrue(bmaker.check("The JAR is empty"));
			Packages imports = bmaker.getImports();
			Map<String,String> framework = imports.get(bmaker.getPackageRef("org.osgi.framework"));
			assertEquals(output, framework.get("version"));
		}
		finally {
			bmaker.close();
		}

	}

	public static void testImportExportBadVersion() throws Exception {
		Builder b = new Builder();
		try {
			b.addClasspath(IO.getFile("jar/ds.jar"));
			b.set(Analyzer.BUNDLE_VERSION, "0.9.5-@#SNAPSHOT");
			b.set(Analyzer.EXPORT_PACKAGE, "*;version=0.9.5-@#SNAPSHOT");
			b.set(Analyzer.IMPORT_PACKAGE, "*;version=0.9.5-@#SNAPSHOT");

			Jar jar = b.build();
			assertTrue(b.check());
			Manifest m = jar.getManifest();
			m.write(System.err);
			assertEquals(m.getMainAttributes().getValue("Bundle-Version"), "0.9.5.SNAPSHOT");

			assertNotNull(b.getExports().getByFQN("org.eclipse.equinox.ds.parser"));
			assertEquals("0.9.5.SNAPSHOT", b.getExports().getByFQN("org.eclipse.equinox.ds.parser").getVersion());

			assertNotNull(b.getImports().getByFQN("org.osgi.framework"));
			assertEquals("0.9.5.SNAPSHOT", b.getImports().getByFQN("org.osgi.framework").getVersion());
		}
		finally {
			b.close();
		}
	}

	/**
	 * Check if can find an activator in the bundle while using a complex bundle
	 * classpath.
	 * 
	 * @throws Exception
	 */
	public static void testBundleClasspath3() throws Exception {
		Builder bmaker = new Builder();
		try {
			Properties p = new Properties();
			p.put("Export-Package", "test.activator;-split-package:=merge-first");
			p.put("Bundle-Activator", "test.activator.Activator");
			p.put("Import-Package", "*");
			p.put("Include-Resource", "ds.jar=jar/ds.jar");
			p.put("Bundle-ClassPath", ".,ds.jar");
			bmaker.setProperties(p);
			bmaker.setClasspath(new File[] {
					new File("bin"), new File("src")
			});
			Jar jar = bmaker.build();
			assertTrue(bmaker.check());

			report("testBundleClasspath3", bmaker, jar);
			assertEquals(0, bmaker.getErrors().size());
			assertEquals(0, bmaker.getWarnings().size());
		}
		finally {
			bmaker.close();
		}

	}

	/**
	 * Check if can find an activator in a embedded jar while using a complex
	 * bundle classpath.
	 * 
	 * @throws Exception
	 */
	public static void testBundleClasspath2() throws Exception {
		Builder bmaker = new Builder();
		try {
			Properties p = new Properties();
			p.put("Bundle-Activator", "org.eclipse.equinox.ds.Activator");
			p.put("Private-Package", "test.activator;-split-package:=merge-first");
			p.put("Import-Package", "*");
			p.put("Include-Resource", "ds.jar=jar/ds.jar");
			p.put("Bundle-ClassPath", ".,ds.jar");
			bmaker.setProperties(p);
			bmaker.setClasspath(new File[] {
					new File("bin"), new File("src")
			});
			Jar jar = bmaker.build();
			assertTrue(bmaker.check());

			report("testBundleClasspath2", bmaker, jar);
			assertEquals(bmaker.getErrors().size(), 0);
			assertEquals(bmaker.getWarnings().size(), 0);
		}
		finally {
			bmaker.close();
		}
	}

	public static void testBundleClasspath() throws Exception {
		Builder bmaker = new Builder();
		try {
			Properties p = new Properties();
			p.put("Export-Package", "test.activator;-split-package:=merge-first");
			p.put("Bundle-Activator", "test.activator.Activator");
			p.put("Import-Package", "*");
			p.put("Bundle-ClassPath", ".");
			bmaker.setProperties(p);
			bmaker.setClasspath(new File[] {
					new File("bin"), new File("src")
			});
			Jar jar = bmaker.build();
			assertTrue(bmaker.check());

			report("testBundleClasspath", bmaker, jar);
			jar.exists("testresources/activator/Activator.class");
			assertEquals(bmaker.getErrors().size(), 0);
			assertEquals(bmaker.getWarnings().size(), 0);
		}
		finally {
			bmaker.close();
		}

	}

	public static void testUnreferredImport() throws Exception {
		Builder bmaker = new Builder();
		try {
			Properties p = new Properties();

			p.put("-classpath", "jar/mina.jar");
			p.put("Export-Package", "*");
			p.put("Import-Package", "org.apache.commons.collections.map,*");
			bmaker.setProperties(p);
			Jar jar = bmaker.build();
			assertTrue(bmaker.check());
			report("testUnreferredImport", bmaker, jar);
		}
		finally {
			bmaker.close();
		}
	}

	public static void testUnreferredNegatedImport() throws Exception {
		Builder bmaker = new Builder();
		try {
			Properties p = new Properties();

			p.put("-classpath", "jar/mina.jar");
			p.put("Export-Package", "*");
			p.put("Import-Package", "!org.apache.commons.collections.map,*");
			bmaker.setProperties(p);
			Jar jar = bmaker.build();
			assertTrue(bmaker.check());
			report("testUnreferredImport", bmaker, jar);
		}
		finally {
			bmaker.close();
		}

	}

	public static void testIncludeResourceResourcesOnlyJar2() throws Exception {
		Builder bmaker = new Builder();
		try {
			Properties p = new Properties();

			p.put("-classpath", "jar/ro.jar");
			p.put("Export-Package", "*");
			p.put("Import-Package", "");
			bmaker.setProperties(p);
			Jar jar = bmaker.build();
			assertTrue(bmaker.check());

			report("testIncludeResourceResourcesOnlyJar2", bmaker, jar);
			assertTrue(bmaker.getExports().getByFQN("ro") != null);
			assertFalse(bmaker.getExports().getByFQN("META-INF") != null);

			assertEquals(3, jar.getResources().size());
		}
		finally {
			bmaker.close();
		}
	}

	public static void testClasspathFileNotExist() throws Exception {
		Builder bmaker = new Builder();
		try {
			Properties p = new Properties();
			File cp[] = new File[] {
				IO.getFile("jar/idonotexist.jar")
			};

			bmaker.setProperties(p);
			bmaker.setClasspath(cp);
			bmaker.build();
			assertTrue(bmaker.check("The JAR is empty", "Missing file on classpath: .*/jar/idonotexist.jar"));
		}
		finally {
			bmaker.close();
		}

	}

	public static void testExpandWithNegate() throws Exception {
		Builder bmaker = new Builder();
		try {
			Properties p = new Properties();
			File cp[] = new File[] {
				IO.getFile("jar/asm.jar")
			};

			p.put("Export-Package", "!org.objectweb.asm,*");
			bmaker.setProperties(p);
			bmaker.setClasspath(cp);
			Jar jar = bmaker.build();
			assertTrue(bmaker.check());

			assertNull(jar.getDirectories().get("org/objectweb/asm"));
			assertNotNull(jar.getDirectories().get("org/objectweb/asm/signature"));
			assertEquals(0, bmaker.getWarnings().size());
			assertEquals(0, bmaker.getErrors().size());
			assertEquals(3, jar.getResources().size());
		}
		finally {
			bmaker.close();
		}
	}

	public static void testIncludeResourceResourcesOnlyJar() throws Exception {
		Builder bmaker = new Builder();
		try {
			Properties p = new Properties();
			File cp[] = new File[] {
				IO.getFile("jar/ro.jar")
			};

			p.put("Export-Package", "*");
			p.put("Import-Package", "");
			bmaker.setProperties(p);
			bmaker.setClasspath(cp);
			Jar jar = bmaker.build();
			assertEquals(0, bmaker.getWarnings().size());
			assertEquals(0, bmaker.getErrors().size());
			assertEquals(3, jar.getResources().size());
		}
		finally {
			bmaker.close();
		}
	}

	public static void testIncludeResourceResourcesOnly() throws Exception {
		Builder bmaker = new Builder();
		try {
			Properties p = new Properties();
			File cp[] = new File[] {
				new File("src")
			};

			p.put("Import-Package", "");
			p.put("Private-Package", "test.resourcesonly");
			bmaker.setProperties(p);
			bmaker.setClasspath(cp);
			Jar jar = bmaker.build();
			assertTrue(bmaker.check());
			assertEquals(0, bmaker.getWarnings().size());
			assertEquals(0, bmaker.getErrors().size());
			assertEquals(4, jar.getResources().size());
		}
		finally {
			bmaker.close();
		}

	}

	public static void testIncludeResourceFromZipDefault() throws Exception {
		Builder bmaker = new Builder();
		Properties p = new Properties();
		p.put("Include-Resource", "@jar/easymock.jar");
		bmaker.setProperties(p);
		Jar jar = bmaker.build();
		assertTrue(bmaker.check());
		assertEquals(59, jar.getResources().size());

	}

	public static void testIncludeResourceFromZipDeep() throws Exception {
		Builder bmaker = new Builder();
		try {
			Properties p = new Properties();
			p.put("Include-Resource", "@jar/easymock.jar!/**");
			bmaker.setProperties(p);
			Jar jar = bmaker.build();
			assertTrue(bmaker.check());

			assertEquals(59, jar.getResources().size());
		}
		finally {
			bmaker.close();
		}
	}

	public static void testIncludeResourceFromZipOneDirectory() throws Exception {
		Builder bmaker = new Builder();
		try {
			Properties p = new Properties();
			p.put("Import-Package", "");
			p.put("Include-Resource", "@jar/easymock.jar!/org/easymock/**");
			bmaker.setProperties(p);
			Jar jar = bmaker.build();
			assertTrue(bmaker.check());

			assertEquals(59, jar.getResources().size());
			assertNotNull(jar.getResource("org/easymock/AbstractMatcher.class"));
		}
		finally {
			bmaker.close();
		}

	}

	public static void testIncludeResourceFromZipOneDirectoryOther() throws Exception {
		Builder bmaker = new Builder();
		try {
			Properties p = new Properties();
			p.put(Constants.BUNDLE_CLASSPATH, "OPT-INF/test");
			p.put("Import-Package", "!*");
			p.put("-resourceonly", "true");
			p.put("Include-Resource", "OPT-INF/test=@jar/osgi.jar!/org/osgi/service/event/**");
			bmaker.setProperties(p);
			Jar jar = bmaker.build();

			assertTrue(bmaker.check());

			assertEquals(7, jar.getResources().size());
			assertNotNull(jar.getResource("OPT-INF/test/org/osgi/service/event/EventAdmin.class"));
		}
		finally {
			bmaker.close();
		}

	}

	public static void testIncludeResourceFromZipRecurseDirectory() throws Exception {
		Builder bmaker = new Builder();
		try {
			Properties p = new Properties();
			p.put("Import-Package", "!*");
			p.put("Include-Resource", "@jar/easymock.jar!/org/easymock/**");
			bmaker.setProperties(p);
			Jar jar = bmaker.build();
			assertTrue(bmaker.check());
			assertEquals(59, jar.getResources().size());
		}
		finally {
			bmaker.close();
		}

	}

	public static void testIncludeLicenseFromZip() throws Exception {
		Builder bmaker = new Builder();
		try {
			Properties p = new Properties();
			p.put("Import-Package", "");
			p.put("Include-Resource", "@jar/osgi.jar!/LICENSE");
			bmaker.setProperties(p);
			Jar jar = bmaker.build();
			assertTrue(bmaker.check());
			assertEquals(1, jar.getResources().size());
			assertNotNull(jar.getResource("LICENSE"));
		}
		finally {
			bmaker.close();
		}

	}

	public static void testEasymock() throws Exception {
		File cp[] = {
			IO.getFile("jar/easymock.jar")
		};
		Builder bmaker = new Builder();
		try {
			Properties p = new Properties();
			p.put("Import-Package", "*");
			p.put("Export-Package", "*");
			p.put("Bundle-SymbolicName", "easymock");
			p.put("Bundle-Version", "2.2");
			bmaker.setProperties(p);
			bmaker.setClasspath(cp);
			Jar jar = bmaker.build();
			assertTrue(bmaker.check());
			jar.getManifest().write(System.err);
		}
		finally {
			bmaker.close();
		}

	}

	public static void testSources() throws Exception {
		Builder bmaker = new Builder();
		try {
			bmaker.addClasspath(new File("bin"));
			bmaker.setSourcepath(new File[] {
				new File("src")
			});
			bmaker.setProperty("-sources", "true");
			bmaker.setProperty("Export-Package", "test.activator");
			Jar jar = bmaker.build();
			assertTrue(bmaker.check());
			assertEquals(
					"[test/activator/AbstractActivator.class, test/activator/Activator.class, test/activator/Activator11.class, test/activator/Activator2.class, test/activator/Activator3.class, test/activator/ActivatorPackage.class, test/activator/ActivatorPrivate.class, test/activator/DefaultVisibilityActivator.class, test/activator/IActivator.class, test/activator/MissingNoArgsConstructorActivator.class, test/activator/NotAnActivator.class]",
					new SortedList<String>(jar.getDirectories().get("test/activator").keySet()).toString());
		}
		finally {
			bmaker.close();
		}

	}

	public void testVerify() throws Exception {
		System.err.println("Erroneous bundle: tb1.jar");
		Jar jar = new Jar("test", getClass().getResourceAsStream("tb1.jar"));
		Verifier verifier = new Verifier(jar);
		verifier.verify();
		assertTrue(verifier.check());
		jar.close();
		verifier.close();
	}

	public static void report(String title, Analyzer builder, Jar jar) {
		System.err.println("Directories " + jar.getDirectories().keySet());
		System.err.println("Warnings    " + builder.getWarnings());
		System.err.println("Errors      " + builder.getErrors());
		System.err.println("Exports     " + builder.getExports());
		System.err.println("Imports     " + builder.getImports());
	}

}
