You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by mc...@apache.org on 2011/10/17 12:31:51 UTC

svn commit: r1185095 [11/15] - in /felix/trunk/bundleplugin: ./ src/main/java/aQute/ src/main/java/aQute/bnd/ src/main/java/aQute/bnd/annotation/ src/main/java/aQute/bnd/annotation/component/ src/main/java/aQute/bnd/annotation/metatype/ src/main/java/a...

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Constants.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Constants.java?rev=1185095&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Constants.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Constants.java Mon Oct 17 10:31:43 2011
@@ -0,0 +1,240 @@
+package aQute.lib.osgi;
+
+import java.nio.charset.*;
+import java.util.regex.*;
+
+public interface Constants {
+	/*
+	 * Defined in OSGi
+	 */
+	/**
+	 * @syntax Bundle-ActivationPolicy ::= policy ( ’;’ directive )* policy ::=
+	 *         ’lazy’
+	 */
+	String					BND_ADDXMLTOTEST							= "Bnd-AddXMLToTest";
+	String					BUNDLE_ACTIVATIONPOLICY						= "Bundle-ActivationPolicy";
+	String					BUNDLE_ACTIVATOR							= "Bundle-Activator";
+	String					BUNDLE_BLUEPRINT							= "Bundle-Copyright";
+	String					BUNDLE_CATEGORY								= "Bundle-Category";
+	String					BUNDLE_CLASSPATH							= "Bundle-ClassPath";
+	String					BUNDLE_CONTACTADDRESS						= "Bundle-ContactAddress";
+	String					BUNDLE_COPYRIGHT							= "Bundle-Copyright";
+	String					BUNDLE_DESCRIPTION							= "Bundle-Description";
+	String					BUNDLE_DOCURL								= "Bundle-DocURL";
+	String					BUNDLE_ICON									= "Bundle-Icon";
+	String					BUNDLE_LICENSE								= "Bundle-License";
+	String					BUNDLE_LOCALIZATION							= "Bundle-Localization";
+	String					BUNDLE_MANIFESTVERSION						= "Bundle-ManifestVersion";
+	String					BUNDLE_NAME									= "Bundle-Name";
+	String					BUNDLE_NATIVECODE							= "Bundle-NativeCode";
+	String					BUNDLE_REQUIREDEXECUTIONENVIRONMENT			= "Bundle-RequiredExecutionEnvironment";
+	String					BUNDLE_SYMBOLICNAME							= "Bundle-SymbolicName";
+	String					BUNDLE_UPDATELOCATION						= "Bundle-UpdateLocation";
+	String					BUNDLE_VENDOR								= "Bundle-Vendor";
+	String					BUNDLE_VERSION								= "Bundle-Version";
+	String					DYNAMICIMPORT_PACKAGE						= "DynamicImport-Package";
+	String					EXPORT_PACKAGE								= "Export-Package";
+	String					EXPORT_SERVICE								= "Export-Service";
+	String					FRAGMENT_HOST								= "Fragment-Host";
+	String					IMPORT_PACKAGE								= "Import-Package";
+	String					IMPORT_SERVICE								= "Import-Service";
+	String					REQUIRE_BUNDLE								= "Require-Bundle";
+	String					SERVICE_COMPONENT							= "Service-Component";
+
+	String					PRIVATE_PACKAGE								= "Private-Package";
+	String					IGNORE_PACKAGE								= "Ignore-Package";
+	String					INCLUDE_RESOURCE							= "Include-Resource";
+	String					CONDITIONAL_PACKAGE							= "Conditional-Package";
+	String					BND_LASTMODIFIED							= "Bnd-LastModified";
+	String					CREATED_BY									= "Created-By";
+	String					TOOL										= "Tool";
+	String					TESTCASES									= "Test-Cases";
+	String					SIGNATURE_TEST								= "-signaturetest";
+
+	String					headers[]									= { BUNDLE_ACTIVATOR,
+			BUNDLE_CONTACTADDRESS, BUNDLE_COPYRIGHT, BUNDLE_DESCRIPTION, BUNDLE_DOCURL,
+			BUNDLE_LOCALIZATION, BUNDLE_NATIVECODE, BUNDLE_VENDOR, BUNDLE_VERSION, BUNDLE_LICENSE,
+			BUNDLE_CLASSPATH, SERVICE_COMPONENT, EXPORT_PACKAGE, IMPORT_PACKAGE,
+			BUNDLE_LOCALIZATION, BUNDLE_MANIFESTVERSION, BUNDLE_NAME, BUNDLE_NATIVECODE,
+			BUNDLE_REQUIREDEXECUTIONENVIRONMENT, BUNDLE_SYMBOLICNAME, BUNDLE_VERSION,
+			FRAGMENT_HOST, PRIVATE_PACKAGE, IGNORE_PACKAGE, INCLUDE_RESOURCE, REQUIRE_BUNDLE,
+			IMPORT_SERVICE, EXPORT_SERVICE, CONDITIONAL_PACKAGE, BND_LASTMODIFIED, TESTCASES,
+			SIGNATURE_TEST												};
+
+	String					BUILDPATH									= "-buildpath";
+	String					BUILDPACKAGES								= "-buildpackages";
+	String					BUMPPOLICY									= "-bumppolicy";
+	String					CONDUIT										= "-conduit";
+	String					COMPILER_SOURCE								= "-source";
+	String					COMPILER_TARGET								= "-target";
+	String					DEPENDSON									= "-dependson";
+	String					DEPLOY										= "-deploy";
+	String					DEPLOYREPO									= "-deployrepo";
+	String					DONOTCOPY									= "-donotcopy";
+	String					DEBUG										= "-debug";
+	String					EXPORT_CONTENTS								= "-exportcontents";
+	String					FAIL_OK										= "-failok";
+	String					INCLUDE										= "-include";
+	String					INCLUDERESOURCE								= "-includeresource";
+	String					MAKE										= "-make";
+	String					METATYPE									= "-metatype";
+	String					MANIFEST									= "-manifest";
+	String					SAVEMANIFEST								= "-savemanifest";
+	String					NODEFAULTVERSION							= "-nodefaultversion";
+	String					NOEXTRAHEADERS								= "-noextraheaders";
+	String					NOMANIFEST									= "-nomanifest";
+	String					NOUSES										= "-nouses";
+	@Deprecated String		NOPE										= "-nope";
+	String					NOBUNDLES									= "-nobundles";
+	String					PEDANTIC									= "-pedantic";
+	String					PLUGIN										= "-plugin";
+	String					POM											= "-pom";
+	String					RELEASEREPO									= "-releaserepo";
+	String					REMOVEHEADERS								= "-removeheaders";
+	String					RESOURCEONLY								= "-resourceonly";
+	String					SOURCES										= "-sources";
+	String					SOURCEPATH									= "-sourcepath";
+	String					SUB											= "-sub";
+	String					RUNPROPERTIES								= "-runproperties";
+	String					RUNSYSTEMPACKAGES							= "-runsystempackages";
+	String					RUNBUNDLES									= "-runbundles";
+	String					RUNPATH										= "-runpath";
+	String					RUNSTORAGE									= "-runstorage";
+	String					RUNBUILDS									= "-runbuilds";
+	String					RUNPATH_MAIN_DIRECTIVE						= "main:";
+	String					RUNPATH_LAUNCHER_DIRECTIVE					= "launcher:";
+	String					RUNVM										= "-runvm";
+	String					RUNTRACE									= "-runtrace";
+	String					RUNFRAMEWORK								= "-runframework";
+	String					RUNTIMEOUT									= "-runtimeout";
+	String					SNAPSHOT									= "-snapshot";
+	String					RUNFRAMEWORK_SERVICES						= "services";
+	String					RUNFRAMEWORK_NONE							= "none";
+	String					REPORTNEWER									= "-reportnewer";
+	String					SIGN										= "-sign";
+	String					TESTPACKAGES								= "-testpackages";
+	String					TESTREPORT									= "-testreport";
+	String					TESTPATH									= "-testpath";
+	String					TESTCONTINUOUS								= "-testcontinuous";
+	String					UNDERTEST									= "-undertest";
+	String					VERBOSE										= "-verbose";
+	@Deprecated String		VERSIONPOLICY_IMPL							= "-versionpolicy-impl";
+	@Deprecated String		VERSIONPOLICY_USES							= "-versionpolicy-uses";
+	String					PROVIDER_POLICY								= "-provider-policy";
+	String					CONSUMER_POLICY								= "-consumer-policy";
+	@Deprecated String		VERSIONPOLICY								= "-versionpolicy";
+	String					WAB											= "-wab";
+	String					WABLIB										= "-wablib";
+	String					REQUIRE_BND									= "-require-bnd";
+
+	// Deprecated
+	String					CLASSPATH									= "-classpath";
+
+	String					options[]									= { BUILDPATH, BUMPPOLICY,
+			CONDUIT, CLASSPATH, CONSUMER_POLICY, DEPENDSON, DONOTCOPY, EXPORT_CONTENTS, FAIL_OK,
+			INCLUDE, INCLUDERESOURCE, MAKE, MANIFEST, NOEXTRAHEADERS, NOUSES, NOBUNDLES, PEDANTIC,
+			PLUGIN, POM, PROVIDER_POLICY, REMOVEHEADERS, RESOURCEONLY, SOURCES, SOURCEPATH,
+			SOURCES, SOURCEPATH, SUB, RUNBUNDLES, RUNPATH, RUNSYSTEMPACKAGES, RUNPROPERTIES,
+			REPORTNEWER, UNDERTEST, TESTPATH, TESTPACKAGES, TESTREPORT, VERBOSE, NOMANIFEST,
+			DEPLOYREPO, RELEASEREPO, SAVEMANIFEST, RUNVM, WAB, WABLIB, RUNFRAMEWORK, RUNTRACE,
+			TESTCONTINUOUS, SNAPSHOT												};
+
+	// Ignore bundle specific headers. These bundles do not make
+	// a lot of sense to inherit
+	String[]				BUNDLE_SPECIFIC_HEADERS						= new String[] {
+			INCLUDE_RESOURCE, BUNDLE_ACTIVATOR, BUNDLE_CLASSPATH, BUNDLE_NAME, BUNDLE_NATIVECODE,
+			BUNDLE_SYMBOLICNAME, IMPORT_PACKAGE, EXPORT_PACKAGE, DYNAMICIMPORT_PACKAGE,
+			FRAGMENT_HOST, REQUIRE_BUNDLE, PRIVATE_PACKAGE, EXPORT_CONTENTS, TESTCASES, NOMANIFEST,
+			SIGNATURE_TEST, WAB, WABLIB								};
+
+	char					DUPLICATE_MARKER							= '~';
+	String					SPECIFICATION_VERSION						= "specification-version";
+	String					SPLIT_PACKAGE_DIRECTIVE						= "-split-package:";
+	String					IMPORT_DIRECTIVE							= "-import:";
+	String					NO_IMPORT_DIRECTIVE							= "-noimport:";
+	String					REMOVE_ATTRIBUTE_DIRECTIVE					= "-remove-attribute:";
+	String					LIB_DIRECTIVE								= "lib:";
+	String					NOANNOTATIONS								= "-noannotations";
+	String					COMMAND_DIRECTIVE							= "command:";
+	String					USES_DIRECTIVE								= "uses:";
+	String					MANDATORY_DIRECTIVE							= "mandatory:";
+	String					INCLUDE_DIRECTIVE							= "include:";
+	String					PROVIDE_DIRECTIVE							= "provide:";
+	String					EXCLUDE_DIRECTIVE							= "exclude:";
+	String					PRESENCE_DIRECTIVE							= "presence:";
+	String					PRIVATE_DIRECTIVE							= "private:";
+	String					SINGLETON_DIRECTIVE							= "singleton:";
+	String					EXTENSION_DIRECTIVE							= "extension:";
+	String					VISIBILITY_DIRECTIVE						= "visibility:";
+	String					FRAGMENT_ATTACHMENT_DIRECTIVE				= "fragment-attachment:";
+	String					RESOLUTION_DIRECTIVE						= "resolution:";
+	String					PATH_DIRECTIVE								= "path:";
+	String					SIZE_ATTRIBUTE								= "size";
+	String					LINK_ATTRIBUTE								= "link";
+	String					NAME_ATTRIBUTE								= "name";
+	String					DESCRIPTION_ATTRIBUTE						= "description";
+	String					OSNAME_ATTRIBUTE							= "osname";
+	String					OSVERSION_ATTRIBUTE							= "osversion";
+	String					PROCESSOR_ATTRIBUTE							= "processor";
+	String					LANGUAGE_ATTRIBUTE							= "language";
+	String					SELECTION_FILTER_ATTRIBUTE					= "selection-filter";
+	String					BLUEPRINT_WAIT_FOR_DEPENDENCIES_ATTRIBUTE	= "blueprint.wait-for-dependencies";
+	String					BLUEPRINT_TIMEOUT_ATTRIBUTE					= "blueprint.timeout";
+	String					VERSION_ATTRIBUTE							= "version";
+	String					BUNDLE_SYMBOLIC_NAME_ATTRIBUTE				= "bundle-symbolic-name";
+	String					BUNDLE_VERSION_ATTRIBUTE					= "bundle-version";
+	String					FROM_DIRECTIVE								= "from:";
+
+	String					KEYSTORE_LOCATION_DIRECTIVE					= "keystore:";
+	String					KEYSTORE_PROVIDER_DIRECTIVE					= "provider:";
+	String					KEYSTORE_PASSWORD_DIRECTIVE					= "password:";
+	String					SIGN_PASSWORD_DIRECTIVE						= "sign-password:";
+
+	String					NONE										= "none";
+
+	String					directives[]								= {
+			SPLIT_PACKAGE_DIRECTIVE, NO_IMPORT_DIRECTIVE, IMPORT_DIRECTIVE, RESOLUTION_DIRECTIVE,
+			INCLUDE_DIRECTIVE, USES_DIRECTIVE, EXCLUDE_DIRECTIVE, KEYSTORE_LOCATION_DIRECTIVE,
+			KEYSTORE_PROVIDER_DIRECTIVE, KEYSTORE_PASSWORD_DIRECTIVE, SIGN_PASSWORD_DIRECTIVE,
+			COMMAND_DIRECTIVE, NOANNOTATIONS, LIB_DIRECTIVE, RUNPATH_LAUNCHER_DIRECTIVE,
+			FROM_DIRECTIVE, PRIVATE_DIRECTIVE
+
+																		// TODO
+																		};
+
+	String					USES_USES									= "<<USES>>";
+	String					CURRENT_USES								= "@uses";
+	String					IMPORT_REFERENCE							= "reference";
+	String					IMPORT_PRIVATE								= "private";
+	String[]				importDirectives							= { IMPORT_REFERENCE,
+			IMPORT_PRIVATE												};
+
+	static final Pattern	VALID_PROPERTY_TYPES						= Pattern
+																				.compile("(String|Long|Double|Float|Integer|Byte|Character|Boolean|Short)");
+
+	String					DEFAULT_BND_EXTENSION						= ".bnd";
+	String					DEFAULT_JAR_EXTENSION						= ".jar";
+	String					DEFAULT_BAR_EXTENSION						= ".bar";
+	String					DEFAULT_BNDRUN_EXTENSION					= ".bndrun";
+	String[]				METAPACKAGES								= { "META-INF", "OSGI-INF",
+			"OSGI-OPT"													};
+
+	String					CURRENT_VERSION								= "@";
+	String					CURRENT_PACKAGE								= "@package";
+
+	String					BUILDFILES									= "buildfiles";
+
+	String					EMPTY_HEADER								= "<<EMPTY>>";
+
+	String					EMBEDDED_REPO								= "/embedded-repo.jar";
+	String					LAUNCHER_PLUGIN								= "Launcher-Plugin";
+	String					TESTER_PLUGIN								= "Tester-Plugin";
+
+	String					DEFAULT_LAUNCHER_BSN						= "biz.aQute.launcher";
+	String					DEFAULT_TESTER_BSN							= "biz.aQute.junit";
+
+	String					DEFAULT_DO_NOT_COPY							= "CVS|\\.svn|\\.git|\\.DS_Store";
+
+	Charset					DEFAULT_CHARSET								= Charset.forName("UTF8");
+	String					VERSION_FILTER	= "version";
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Constants.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Constants.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/EmbeddedResource.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/EmbeddedResource.java?rev=1185095&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/EmbeddedResource.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/EmbeddedResource.java Mon Oct 17 10:31:43 2011
@@ -0,0 +1,86 @@
+package aQute.lib.osgi;
+
+import java.io.*;
+import java.util.zip.*;
+
+public class EmbeddedResource implements Resource {
+	byte	data[];
+	long 	lastModified;
+	String	extra;
+
+	public EmbeddedResource(byte data[], long lastModified) {
+		this.data = data;
+		this.lastModified = lastModified;
+	}
+
+	public InputStream openInputStream() throws FileNotFoundException {
+		return new ByteArrayInputStream(data);
+	}
+
+	public void write(OutputStream out) throws IOException {
+		out.write(data);
+	}
+
+	public String toString() {
+		return ":" + data.length + ":";
+	}
+
+	public static void build(Jar jar, InputStream in, long lastModified) throws IOException {
+		ZipInputStream jin = new ZipInputStream(in);
+		ZipEntry entry = jin.getNextEntry();
+		while (entry != null) {
+			if (!entry.isDirectory()) {
+				byte data[] = collect(jin);
+				jar.putResource(entry.getName(), new EmbeddedResource(data, lastModified), true);
+			}
+			entry = jin.getNextEntry();
+		}
+		jin.close();
+	}
+
+	/**
+	 * Convenience method to turn an inputstream into a byte array. The method
+	 * uses a recursive algorithm to minimize memory usage.
+	 * 
+	 * @param in stream with data
+	 * @param offset where we are in the stream
+	 * @returns byte array filled with data
+	 */
+    static byte[] collect(InputStream in) throws IOException {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        copy(in,out);
+        return out.toByteArray();
+    }
+
+    static void copy(InputStream in, OutputStream out) throws IOException {
+        int available = in.available();
+        if ( available <= 10000)
+            available = 64000;
+        byte [] buffer = new byte[available];
+        int size;
+        while ( (size=in.read(buffer))>0)
+            out.write(buffer,0,size);
+    }
+
+	public long lastModified() {
+		return lastModified;
+	}
+
+	public static void build(Jar sub, Resource resource) throws Exception {
+			InputStream in = resource.openInputStream();
+			build(sub,in, resource.lastModified());
+			in.close();
+	}
+
+	public String getExtra() {
+		return extra;
+	}
+
+	public void setExtra(String extra) {
+		this.extra = extra;
+	}
+
+	public long size() {
+	    return data.length;
+	}
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/EmbeddedResource.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/EmbeddedResource.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/FileResource.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/FileResource.java?rev=1185095&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/FileResource.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/FileResource.java Mon Oct 17 10:31:43 2011
@@ -0,0 +1,84 @@
+package aQute.lib.osgi;
+
+import java.io.*;
+import java.util.regex.Pattern;
+
+public class FileResource implements Resource {
+	File	file;
+	String	extra;
+	
+	public FileResource(File file) {
+		this.file = file;
+	}
+
+	public InputStream openInputStream() throws FileNotFoundException {
+		return new FileInputStream(file);
+	}
+
+	public static void build(Jar jar, File directory, Pattern doNotCopy) {
+		traverse(
+				jar,
+				directory.getAbsolutePath().length(),
+				directory,
+				doNotCopy);
+	}
+
+	public String toString() {
+		return ":" + file.getName() + ":";
+	}
+
+	public void write(OutputStream out) throws Exception {
+		copy(this, out);
+	}
+
+	static synchronized void copy(Resource resource, OutputStream out)
+			throws Exception {
+		InputStream in = resource.openInputStream();
+		try {
+			byte buffer[] = new byte[20000];
+			int size = in.read(buffer);
+			while (size > 0) {
+				out.write(buffer, 0, size);
+				size = in.read(buffer);
+			}
+		}
+		finally {
+			in.close();
+		}
+	}
+
+	static void traverse(Jar jar, int rootlength, File directory,
+			Pattern doNotCopy) {
+		if (doNotCopy != null && doNotCopy.matcher(directory.getName()).matches())
+			return;
+
+		File files[] = directory.listFiles();
+		for (int i = 0; i < files.length; i++) {
+			if (files[i].isDirectory())
+				traverse(jar, rootlength, files[i], doNotCopy);
+			else {
+				String path = files[i].getAbsolutePath().substring(
+						rootlength + 1);
+				if (File.separatorChar != '/')
+					path = path.replace(File.separatorChar, '/');
+				jar.putResource(path, new FileResource(files[i]), true);
+			}
+		}
+	}
+
+	public long lastModified() {
+		return file.lastModified();
+	}
+
+	public String getExtra() {
+		return extra;
+	}
+
+	public void setExtra(String extra) {
+		this.extra = extra;
+	}
+	
+	public long size() {
+	    return (int) file.length();
+	}
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/FileResource.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/FileResource.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Instruction.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Instruction.java?rev=1185095&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Instruction.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Instruction.java Mon Oct 17 10:31:43 2011
@@ -0,0 +1,130 @@
+package aQute.lib.osgi;
+
+import java.util.*;
+import java.util.regex.*;
+
+import aQute.libg.generics.*;
+
+public class Instruction {
+    Pattern pattern;
+    String  instruction;
+    boolean negated;
+    boolean optional;
+
+    public Instruction(String instruction, boolean negated) {
+        this.instruction = instruction;
+        this.negated = negated;
+    }
+
+    public boolean matches(String value) {
+        return getMatcher(value).matches();
+    }
+
+    public boolean isNegated() {
+        return negated;
+    }
+
+    public String getPattern() {
+        return instruction;
+    }
+
+    /**
+     * Convert a string based pattern to a regular expression based pattern.
+     * This is called an instruction, this object makes it easier to handle the
+     * different cases
+     * 
+     * @param string
+     * @return
+     */
+    public static Instruction getPattern(String string) {
+        boolean negated = false;
+        if (string.startsWith("!")) {
+            negated = true;
+            string = string.substring(1);
+        }
+        StringBuffer sb = new StringBuffer();
+        for (int c = 0; c < string.length(); c++) {
+            switch (string.charAt(c)) {
+            case '.':
+                sb.append("\\.");
+                break;
+            case '*':
+                sb.append(".*");
+                break;
+            case '?':
+                sb.append(".?");
+                break;
+            default:
+                sb.append(string.charAt(c));
+                break;
+            }
+        }
+        string = sb.toString();
+        if (string.endsWith("\\..*")) {
+            sb.append("|");
+            sb.append(string.substring(0, string.length() - 4));
+        }
+        return new Instruction(sb.toString(), negated);
+    }
+
+    public String toString() {
+        return getPattern();
+    }
+
+    public Matcher getMatcher(String value) {
+        if (pattern == null) {
+            pattern = Pattern.compile(instruction);
+        }
+        return pattern.matcher(value);
+    }
+
+    public int hashCode() {
+        return instruction.hashCode();
+    }
+
+    public boolean equals(Object other) {
+        return other != null && (other instanceof Instruction)
+                && instruction.equals(((Instruction) other).instruction);
+    }
+
+    public void setOptional() {
+        optional = true;
+    }
+
+    public boolean isOptional() {
+        return optional;
+    }
+
+    public static Map<Instruction, Map<String, String>> replaceWithInstruction(
+            Map<String, Map<String, String>> header) {
+        Map<Instruction, Map<String, String>> map = Processor.newMap();
+        for (Iterator<Map.Entry<String, Map<String, String>>> e = header
+                .entrySet().iterator(); e.hasNext();) {
+            Map.Entry<String, Map<String, String>> entry = e.next();
+            String pattern = entry.getKey();
+            Instruction instr = getPattern(pattern);
+            String presence = entry.getValue()
+                    .get(Constants.PRESENCE_DIRECTIVE);
+            if ("optional".equals(presence))
+                instr.setOptional();
+            map.put(instr, entry.getValue());
+        }
+        return map;
+    }
+
+    public static <T> Collection<T> select(Collection<Instruction> matchers,
+            Collection<T> targets) {
+        Collection<T> result = Create.list();
+        outer: for (T t : targets) {
+            String s = t.toString();
+            for (Instruction i : matchers) {
+                if (i.matches(s)) {
+                    if (!i.isNegated())
+                        result.add(t);
+                    continue outer;
+                }
+            }
+        }
+        return result;
+    }
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Instruction.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Instruction.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/InstructionFilter.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/InstructionFilter.java?rev=1185095&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/InstructionFilter.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/InstructionFilter.java Mon Oct 17 10:31:43 2011
@@ -0,0 +1,37 @@
+package aQute.lib.osgi;
+
+import java.io.*;
+import java.util.regex.*;
+
+public class InstructionFilter implements FileFilter {
+
+	private Instruction instruction;
+	private boolean recursive;
+	private Pattern doNotCopy;
+	
+	public InstructionFilter (Instruction instruction, boolean recursive, Pattern doNotCopy) {
+		this.instruction = instruction;
+		this.recursive = recursive;
+		this.doNotCopy = doNotCopy;
+	}
+	public InstructionFilter (Instruction instruction, boolean recursive) {
+		this(instruction, recursive, Pattern.compile(Constants.DEFAULT_DO_NOT_COPY));
+	}
+	public boolean isRecursive() {
+		return recursive;
+	}
+	public boolean accept(File pathname) {
+		if (doNotCopy != null && doNotCopy.matcher(pathname.getName()).matches()) {
+			return false;
+		}
+
+		if (pathname.isDirectory() && isRecursive()) {
+			return true;
+		}
+		
+		if (instruction == null) {
+			return true;
+		}
+		return !instruction.isNegated() == instruction.matches(pathname.getName());
+	}
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/InstructionFilter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/InstructionFilter.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Jar.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Jar.java?rev=1185095&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Jar.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Jar.java Mon Oct 17 10:31:43 2011
@@ -0,0 +1,690 @@
+package aQute.lib.osgi;
+
+import static aQute.lib.io.IO.*;
+
+import java.io.*;
+import java.security.*;
+import java.util.*;
+import java.util.jar.*;
+import java.util.regex.*;
+import java.util.zip.*;
+
+import aQute.lib.base64.*;
+import aQute.libg.reporter.*;
+
+public class Jar implements Closeable {
+	public static final Object[]		EMPTY_ARRAY	= new Jar[0];
+	Map<String, Resource>				resources	= new TreeMap<String, Resource>();
+	Map<String, Map<String, Resource>>	directories	= new TreeMap<String, Map<String, Resource>>();
+	Manifest							manifest;
+	boolean								manifestFirst;
+	String								name;
+	File								source;
+	ZipFile								zipFile;
+	long								lastModified;
+	String								lastModifiedReason;
+	Reporter							reporter;
+	boolean								doNotTouchManifest;
+	boolean								nomanifest;
+
+	public Jar(String name) {
+		this.name = name;
+	}
+
+	public Jar(String name, File dirOrFile, Pattern doNotCopy) throws ZipException, IOException {
+		this(name);
+		source = dirOrFile;
+		if (dirOrFile.isDirectory())
+			FileResource.build(this, dirOrFile, doNotCopy);
+		else if (dirOrFile.isFile()) {
+			zipFile = ZipResource.build(this, dirOrFile);
+		} else {
+			throw new IllegalArgumentException("A Jar can only accept a valid file or directory: "
+					+ dirOrFile);
+		}
+	}
+
+	public Jar(String name, InputStream in, long lastModified) throws IOException {
+		this(name);
+		EmbeddedResource.build(this, in, lastModified);
+	}
+
+	public Jar(String name, String path) throws IOException {
+		this(name);
+		File f = new File(path);
+		InputStream in = new FileInputStream(f);
+		EmbeddedResource.build(this, in, f.lastModified());
+		in.close();
+	}
+
+	public Jar(File f) throws IOException {
+		this(getName(f), f, null);
+	}
+
+	/**
+	 * Make the JAR file name the project name if we get a src or bin directory.
+	 * 
+	 * @param f
+	 * @return
+	 */
+	private static String getName(File f) {
+		f = f.getAbsoluteFile();
+		String name = f.getName();
+		if (name.equals("bin") || name.equals("src"))
+			return f.getParentFile().getName();
+		else {
+			if (name.endsWith(".jar"))
+				name = name.substring(0, name.length() - 4);
+			return name;
+		}
+	}
+
+	public Jar(String string, InputStream resourceAsStream) throws IOException {
+		this(string, resourceAsStream, 0);
+	}
+
+	public Jar(String string, File file) throws ZipException, IOException {
+		this(string, file, Pattern.compile(Constants.DEFAULT_DO_NOT_COPY));
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String toString() {
+		return "Jar:" + name;
+	}
+
+	public boolean putResource(String path, Resource resource) {
+		return putResource(path, resource, true);
+	}
+
+	public boolean putResource(String path, Resource resource, boolean overwrite) {
+		updateModified(resource.lastModified(), path);
+		while (path.startsWith("/"))
+			path = path.substring(1);
+
+		if (path.equals("META-INF/MANIFEST.MF")) {
+			manifest = null;
+			if (resources.isEmpty())
+				manifestFirst = true;
+		}
+		String dir = getDirectory(path);
+		Map<String, Resource> s = directories.get(dir);
+		if (s == null) {
+			s = new TreeMap<String, Resource>();
+			directories.put(dir, s);
+			int n = dir.lastIndexOf('/');
+			while (n > 0) {
+				String dd = dir.substring(0, n);
+				if (directories.containsKey(dd))
+					break;
+				directories.put(dd, null);
+				n = dd.lastIndexOf('/');
+			}
+		}
+		boolean duplicate = s.containsKey(path);
+		if (!duplicate || overwrite) {
+			resources.put(path, resource);
+			s.put(path, resource);
+		}
+		return duplicate;
+	}
+
+	public Resource getResource(String path) {
+		return resources.get(path);
+	}
+
+	private String getDirectory(String path) {
+		int n = path.lastIndexOf('/');
+		if (n < 0)
+			return "";
+
+		return path.substring(0, n);
+	}
+
+	public Map<String, Map<String, Resource>> getDirectories() {
+		return directories;
+	}
+
+	public Map<String, Resource> getResources() {
+		return resources;
+	}
+
+	public boolean addDirectory(Map<String, Resource> directory, boolean overwrite) {
+		boolean duplicates = false;
+		if (directory == null)
+			return false;
+
+		for (Map.Entry<String, Resource> entry : directory.entrySet()) {
+			String key = entry.getKey();
+			if (!key.endsWith(".java")) {
+				duplicates |= putResource(key, (Resource) entry.getValue(), overwrite);
+			}
+		}
+		return duplicates;
+	}
+
+	public Manifest getManifest() throws Exception {
+		if (manifest == null) {
+			Resource manifestResource = getResource("META-INF/MANIFEST.MF");
+			if (manifestResource != null) {
+				InputStream in = manifestResource.openInputStream();
+				manifest = new Manifest(in);
+				in.close();
+			}
+		}
+		return manifest;
+	}
+
+	public boolean exists(String path) {
+		return resources.containsKey(path);
+	}
+
+	public void setManifest(Manifest manifest) {
+		manifestFirst = true;
+		this.manifest = manifest;
+	}
+
+	public void write(File file) throws Exception {
+		try {
+			OutputStream out = new FileOutputStream(file);
+			write(out);
+			out.close();
+			return;
+
+		} catch (Exception t) {
+			file.delete();
+			throw t;
+		}
+	}
+
+	public void write(String file) throws Exception {
+		write(new File(file));
+	}
+
+	public void write(OutputStream out) throws Exception {
+		ZipOutputStream jout = nomanifest || doNotTouchManifest ? new ZipOutputStream(out)
+				: new JarOutputStream(out);
+		Set<String> done = new HashSet<String>();
+
+		Set<String> directories = new HashSet<String>();
+		if (doNotTouchManifest) {
+			Resource r = getResource("META-INF/MANIFEST.MF");
+			if (r != null) {
+				writeResource(jout, directories, "META-INF/MANIFEST.MF", r);
+				done.add("META-INF/MANIFEST.MF");
+			}
+		} else
+			doManifest(done, jout);
+
+		for (Map.Entry<String, Resource> entry : getResources().entrySet()) {
+			// Skip metainf contents
+			if (!done.contains(entry.getKey()))
+				writeResource(jout, directories, (String) entry.getKey(),
+						(Resource) entry.getValue());
+		}
+		jout.finish();
+	}
+
+	private void doManifest(Set<String> done, ZipOutputStream jout) throws Exception {
+		if (nomanifest)
+			return;
+
+		JarEntry ze = new JarEntry("META-INF/MANIFEST.MF");
+		jout.putNextEntry(ze);
+		writeManifest(jout);
+		jout.closeEntry();
+		done.add(ze.getName());
+	}
+
+	/**
+	 * Cleanup the manifest for writing. Cleaning up consists of adding a space
+	 * after any \n to prevent the manifest to see this newline as a delimiter.
+	 * 
+	 * @param out
+	 *            Output
+	 * @throws IOException
+	 */
+
+	public void writeManifest(OutputStream out) throws Exception {
+		writeManifest(getManifest(), out);
+	}
+
+	public static void writeManifest(Manifest manifest, OutputStream out) throws IOException {
+		if (manifest == null)
+			return;
+
+		manifest = clean(manifest);
+		outputManifest(manifest, out);
+	}
+
+	/**
+	 * Unfortunately we have to write our own manifest :-( because of a stupid
+	 * bug in the manifest code. It tries to handle UTF-8 but the way it does it
+	 * it makes the bytes platform dependent.
+	 * 
+	 * So the following code outputs the manifest.
+	 * 
+	 * A Manifest consists of
+	 * 
+	 * <pre>
+	 *   'Manifest-Version: 1.0\r\n'
+	 *   main-attributes *
+	 *   \r\n
+	 *   name-section
+	 *   
+	 *   main-attributes ::= attributes
+	 *   attributes      ::= key ': ' value '\r\n'
+	 *   name-section    ::= 'Name: ' name '\r\n' attributes
+	 * </pre>
+	 * 
+	 * Lines in the manifest should not exceed 72 bytes (! this is where the
+	 * manifest screwed up as well when 16 bit unicodes were used).
+	 * 
+	 * <p>
+	 * As a bonus, we can now sort the manifest!
+	 */
+	static byte[]	CONTINUE	=  new byte[] {'\r','\n', ' '};
+
+	/**
+	 * Main function to output a manifest properly in UTF-8.
+	 * 
+	 * @param manifest
+	 *            The manifest to output
+	 * @param out
+	 *            The output stream
+	 * @throws IOException
+	 *             when something fails
+	 */
+	public static void outputManifest(Manifest manifest, OutputStream out) throws IOException {
+		writeEntry(out, "Manifest-Version", "1.0");
+		attributes(manifest.getMainAttributes(), out);
+
+		TreeSet<String> keys = new TreeSet<String>();
+		for (Object o : manifest.getEntries().keySet())
+			keys.add(o.toString());
+
+		for (String key : keys) {
+			write(out, 0, "\r\n");
+			writeEntry(out, "Name", key);
+			attributes(manifest.getAttributes(key), out);
+		}
+	}
+
+	/**
+	 * Write out an entry, handling proper unicode and line length constraints
+	 * 
+	 */
+	private static void writeEntry(OutputStream out, String name, String value) throws IOException {
+		int n = write(out, 0, name + ": ");
+		n = write(out, n, value);
+		write(out, 0, "\r\n");
+	}
+
+	/**
+	 * Convert a string to bytes with UTF8 and then output in max 72 bytes
+	 * 
+	 * @param out
+	 *            the output string
+	 * @param i
+	 *            the current width
+	 * @param s
+	 *            the string to output
+	 * @return the new width
+	 * @throws IOException
+	 *             when something fails
+	 */
+	private static int write(OutputStream out, int i, String s) throws IOException {
+		byte[] bytes = s.getBytes("UTF8");
+		return write(out, i, bytes);
+	}
+
+	/**
+	 * Write the bytes but ensure that the line length does not exceed 72
+	 * characters. If it is more than 70 characters, we just put a cr/lf +
+	 * space.
+	 * 
+	 * @param out
+	 *            The output stream
+	 * @param width
+	 *            The nr of characters output in a line before this method
+	 *            started
+	 * @param bytes
+	 *            the bytes to output
+	 * @return the nr of characters in the last line
+	 * @throws IOException
+	 *             if something fails
+	 */
+	private static int write(OutputStream out, int width, byte[] bytes) throws IOException {
+		for (int i = 0; i < bytes.length; i++) {
+			if (width >= 72) { // we need to add the \n\r!
+				out.write(CONTINUE);
+				width = 1;
+			}
+			out.write(bytes[i]);
+			width++;
+		}
+		return width;
+	}
+
+	/**
+	 * Output an Attributes map. We will sort this map before outputing.
+	 * 
+	 * @param value
+	 *            the attrbutes
+	 * @param out
+	 *            the output stream
+	 * @throws IOException
+	 *             when something fails
+	 */
+	private static void attributes(Attributes value, OutputStream out) throws IOException {
+		TreeMap<String, String> map = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
+		for (Map.Entry<Object, Object> entry : value.entrySet()) {
+			map.put(entry.getKey().toString(), entry.getValue().toString());
+		}
+
+		map.remove("Manifest-Version"); // get rid of
+		// manifest
+		// version
+		for (Map.Entry<String, String> entry : map.entrySet()) {
+			writeEntry(out, entry.getKey(), entry.getValue());
+		}
+	}
+
+	private static Manifest clean(Manifest org) {
+
+		Manifest result = new Manifest();
+		for (Map.Entry<?, ?> entry : org.getMainAttributes().entrySet()) {
+			String nice = clean((String) entry.getValue());
+			result.getMainAttributes().put(entry.getKey(), nice);
+		}
+		for (String name : org.getEntries().keySet()) {
+			Attributes attrs = result.getAttributes(name);
+			if (attrs == null) {
+				attrs = new Attributes();
+				result.getEntries().put(name, attrs);
+			}
+
+			for (Map.Entry<?, ?> entry : org.getAttributes(name).entrySet()) {
+				String nice = clean((String) entry.getValue());
+				attrs.put((Attributes.Name) entry.getKey(), nice);
+			}
+		}
+		return result;
+	}
+
+	private static String clean(String s) {
+		if (s.indexOf('\n') < 0)
+			return s;
+
+		StringBuffer sb = new StringBuffer(s);
+		for (int i = 0; i < sb.length(); i++) {
+			if (sb.charAt(i) == '\n')
+				sb.insert(++i, ' ');
+		}
+		return sb.toString();
+	}
+
+	private void writeResource(ZipOutputStream jout, Set<String> directories, String path,
+			Resource resource) throws Exception {
+		if (resource == null)
+			return;
+
+		createDirectories(directories, jout, path);
+		ZipEntry ze = new ZipEntry(path);
+		ze.setMethod(ZipEntry.DEFLATED);
+		long lastModified = resource.lastModified();
+		if (lastModified == 0L) {
+			lastModified = System.currentTimeMillis();
+		}
+		ze.setTime(lastModified);
+		if (resource.getExtra() != null)
+			ze.setExtra(resource.getExtra().getBytes());
+		jout.putNextEntry(ze);
+		resource.write(jout);
+		jout.closeEntry();
+	}
+
+	void createDirectories(Set<String> directories, ZipOutputStream zip, String name)
+			throws IOException {
+		int index = name.lastIndexOf('/');
+		if (index > 0) {
+			String path = name.substring(0, index);
+			if (directories.contains(path))
+				return;
+			createDirectories(directories, zip, path);
+			ZipEntry ze = new ZipEntry(path + '/');
+			zip.putNextEntry(ze);
+			zip.closeEntry();
+			directories.add(path);
+		}
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	/**
+	 * Add all the resources in the given jar that match the given filter.
+	 * 
+	 * @param sub
+	 *            the jar
+	 * @param filter
+	 *            a pattern that should match the resoures in sub to be added
+	 */
+	public boolean addAll(Jar sub, Instruction filter) {
+		return addAll(sub, filter, "");
+	}
+
+	/**
+	 * Add all the resources in the given jar that match the given filter.
+	 * 
+	 * @param sub
+	 *            the jar
+	 * @param filter
+	 *            a pattern that should match the resoures in sub to be added
+	 */
+	public boolean addAll(Jar sub, Instruction filter, String destination) {
+		boolean dupl = false;
+		for (String name : sub.getResources().keySet()) {
+			if ("META-INF/MANIFEST.MF".equals(name))
+				continue;
+
+			if (filter == null || filter.matches(name) != filter.isNegated())
+				dupl |= putResource(Processor.appendPath(destination, name), sub.getResource(name),
+						true);
+		}
+		return dupl;
+	}
+
+	public void close() {
+		if (zipFile != null)
+			try {
+				zipFile.close();
+			} catch (IOException e) {
+				// Ignore
+			}
+		resources = null;
+		directories = null;
+		manifest = null;
+		source = null;
+	}
+
+	public long lastModified() {
+		return lastModified;
+	}
+
+	public void updateModified(long time, String reason) {
+		if (time > lastModified) {
+			lastModified = time;
+			lastModifiedReason = reason;
+		}
+	}
+
+	public void setReporter(Reporter reporter) {
+		this.reporter = reporter;
+	}
+
+	public boolean hasDirectory(String path) {
+		return directories.get(path) != null;
+	}
+
+	public List<String> getPackages() {
+		List<String> list = new ArrayList<String>(directories.size());
+
+		for (Map.Entry<String, Map<String, Resource>> i : directories.entrySet()) {
+			if (i.getValue() != null) {
+				String path = i.getKey();
+				String pack = path.replace('/', '.');
+				list.add(pack);
+			}
+		}
+		return list;
+	}
+
+	public File getSource() {
+		return source;
+	}
+
+	public boolean addAll(Jar src) {
+		return addAll(src, null);
+	}
+
+	public boolean rename(String oldPath, String newPath) {
+		Resource resource = remove(oldPath);
+		if (resource == null)
+			return false;
+
+		return putResource(newPath, resource);
+	}
+
+	public Resource remove(String path) {
+		Resource resource = resources.remove(path);
+		String dir = getDirectory(path);
+		Map<String, Resource> mdir = directories.get(dir);
+		// must be != null
+		mdir.remove(path);
+		return resource;
+	}
+
+	/**
+	 * Make sure nobody touches the manifest! If the bundle is signed, we do not
+	 * want anybody to touch the manifest after the digests have been
+	 * calculated.
+	 */
+	public void setDoNotTouchManifest() {
+		doNotTouchManifest = true;
+	}
+
+	/**
+	 * Calculate the checksums and set them in the manifest.
+	 */
+
+	public void calcChecksums(String algorithms[]) throws Exception {
+		if (algorithms == null)
+			algorithms = new String[] { "SHA", "MD5" };
+
+		Manifest m = getManifest();
+		if ( m == null) {
+			m= new Manifest();
+			setManifest(m);
+		}
+		
+		MessageDigest digests[] = new MessageDigest[algorithms.length];
+		int n = 0;
+		for (String algorithm : algorithms)
+			digests[n++] = MessageDigest.getInstance(algorithm);
+
+		byte buffer[] = new byte[30000];
+
+		for (Map.Entry<String, Resource> entry : resources.entrySet()) {
+			
+			// Skip the manifest
+			if (entry.getKey().equals("META-INF/MANIFEST.MF"))
+				continue;
+
+			Resource r = entry.getValue();
+			Attributes attributes = m.getAttributes(entry.getKey());
+			if (attributes == null) {
+				attributes = new Attributes();
+				getManifest().getEntries().put(entry.getKey(), attributes);
+			}
+			InputStream in = r.openInputStream();
+			try {
+				for (MessageDigest d : digests)
+					d.reset();
+				int size = in.read(buffer);
+				while (size > 0) {
+					for (MessageDigest d : digests)
+						d.update(buffer, 0, size);
+					size = in.read(buffer);
+				}
+			} finally {
+				in.close();
+			}
+			for (MessageDigest d : digests)
+				attributes.putValue(d.getAlgorithm() + "-Digest", Base64.encodeBase64(d.digest()));
+		}
+	}
+
+	Pattern	BSN	= Pattern.compile("\\s*([-\\w\\d\\._]+)\\s*;?.*");
+
+	public String getBsn() throws Exception {
+		Manifest m = getManifest();
+		if (m == null)
+			return null;
+
+		String s = m.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
+		Matcher matcher = BSN.matcher(s);
+		if (matcher.matches()) {
+			return matcher.group(1);
+		}
+		return null;
+	}
+
+	public String getVersion() throws Exception {
+		Manifest m = getManifest();
+		if (m == null)
+			return null;
+
+		String s = m.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
+		if (s == null)
+			return null;
+
+		return s.trim();
+	}
+
+	/**
+	 * Expand the JAR file to a directory.
+	 * 
+	 * @param dir
+	 *            the dst directory, is not required to exist
+	 * @throws Exception
+	 *             if anything does not work as expected.
+	 */
+	public void expand(File dir) throws Exception {
+		dir = dir.getAbsoluteFile();
+		dir.mkdirs();
+		if (!dir.isDirectory()) {
+			throw new IllegalArgumentException("Not a dir: " + dir.getAbsolutePath());
+		}
+
+		for (Map.Entry<String, Resource> entry : getResources().entrySet()) {
+			File f = getFile(dir, entry.getKey());
+			f.getParentFile().mkdirs();
+			copy(entry.getValue().openInputStream(), f);
+		}
+	}
+
+	/**
+	 * Make sure we have a manifest
+	 * @throws Exception
+	 */
+	public void ensureManifest() throws Exception {
+		if ( getManifest() != null)
+			return;
+		manifest = new Manifest();
+	}
+
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Jar.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Jar.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/JarResource.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/JarResource.java?rev=1185095&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/JarResource.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/JarResource.java Mon Oct 17 10:31:43 2011
@@ -0,0 +1,46 @@
+package aQute.lib.osgi;
+
+import java.io.*;
+
+public class JarResource implements Resource {
+	Jar		jar;
+	String extra;
+	
+	public JarResource(Jar jar ) {
+		this.jar = jar;
+	}
+	
+	public String getExtra() {
+		return extra;
+	}
+
+	public long lastModified() {
+		return jar.lastModified();
+	}
+
+
+	public void write(OutputStream out) throws Exception {
+		jar.write(out);
+	}
+	
+	public InputStream openInputStream() throws Exception {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		write(out);
+		out.close();
+		ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+		return in;
+	}
+
+	public void setExtra(String extra) {
+		this.extra = extra;
+	}
+	
+	public Jar getJar() { 
+	    return jar;
+	}
+	
+	public String toString() {
+	    return ":" + jar.getName() + ":";
+	}
+
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/JarResource.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/JarResource.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Macro.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Macro.java?rev=1185095&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Macro.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Macro.java Mon Oct 17 10:31:43 2011
@@ -0,0 +1,952 @@
+package aQute.lib.osgi;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.net.*;
+import java.text.*;
+import java.util.*;
+import java.util.regex.*;
+
+import aQute.lib.io.*;
+import aQute.libg.sed.*;
+import aQute.libg.version.*;
+
+/**
+ * Provide a macro processor. This processor can replace variables in strings
+ * based on a properties and a domain. The domain can implement functions that
+ * start with a "_" and take args[], the names of these functions are available
+ * as functions in the macro processor (without the _). Macros can nest to any
+ * depth but may not contain loops.
+ * 
+ * Add POSIX macros:
+ * ${#parameter}
+    String length.
+
+${parameter%word}
+    Remove smallest suffix pattern.
+
+${parameter%%word}
+    Remove largest suffix pattern.
+
+${parameter#word}
+    Remove smallest prefix pattern.
+
+${parameter##word}
+    Remove largest prefix pattern. 
+ */
+public class Macro implements Replacer {
+	Processor	domain;
+	Object		targets[];
+	boolean		flattening;
+
+	public Macro(Processor domain, Object... targets) {
+		this.domain = domain;
+		this.targets = targets;
+		if (targets != null) {
+			for (Object o : targets) {
+				assert o != null;
+			}
+		}
+	}
+
+	public String process(String line, Processor source) {
+		return process(line, new Link(source,null,line));
+	}
+
+	String process(String line, Link link) {
+		StringBuffer sb = new StringBuffer();
+		process(line, 0, '\u0000', '\u0000', sb, link);
+		return sb.toString();
+	}
+
+	int process(CharSequence org, int index, char begin, char end, StringBuffer result, Link link) {
+		StringBuilder line = new StringBuilder(org);
+		int nesting = 1;
+
+		StringBuffer variable = new StringBuffer();
+		outer: while (index < line.length()) {
+			char c1 = line.charAt(index++);
+			if (c1 == end) {
+				if (--nesting == 0) {
+					result.append(replace(variable.toString(), link));
+					return index;
+				}
+			} else if (c1 == begin)
+				nesting++;
+			else if (c1 == '\\' && index < line.length() - 1 && line.charAt(index) == '$') {
+				// remove the escape backslash and interpret the dollar as a
+				// literal
+				index++;
+				variable.append('$');
+				continue outer;
+			} else if (c1 == '$' && index < line.length() - 2) {
+				char c2 = line.charAt(index);
+				char terminator = getTerminator(c2);
+				if (terminator != 0) {
+					index = process(line, index + 1, c2, terminator, variable, link);
+					continue outer;
+				}
+			}
+			variable.append(c1);
+		}
+		result.append(variable);
+		return index;
+	}
+
+	public static char getTerminator(char c) {
+		switch (c) {
+		case '(':
+			return ')';
+		case '[':
+			return ']';
+		case '{':
+			return '}';
+		case '<':
+			return '>';
+		case '\u00ab': // Guillemet double << >>
+			return '\u00bb';
+		case '\u2039': // Guillemet single
+			return '\u203a';
+		}
+		return 0;
+	}
+
+	protected String replace(String key, Link link) {
+		if (link != null && link.contains(key))
+			return "${infinite:" + link.toString() + "}";
+
+		if (key != null) {
+			key = key.trim();
+			if (key.length() > 0) {
+				Processor source = domain;
+				String value = null;
+				while( source != null) {
+					value = source.getProperties().getProperty(key);
+					if ( value != null )
+						break;
+					
+					source = source.getParent();
+				}
+				
+				if (value != null)
+					return process(value, new Link(source,link, key));
+
+				value = doCommands(key, link);
+				if (value != null)
+					return process(value, new Link(source, link, key));
+
+				if (key != null && key.trim().length() > 0) {
+					value = System.getProperty(key);
+					if (value != null)
+						return value;
+				}
+				if (!flattening)
+					domain.warning("No translation found for macro: " + key);
+			} else {
+				domain.warning("Found empty macro key");
+			}
+		} else {
+			domain.warning("Found null macro key");
+		}
+		return "${" + key + "}";
+	}
+
+	/**
+	 * Parse the key as a command. A command consist of parameters separated by
+	 * ':'.
+	 * 
+	 * @param key
+	 * @return
+	 */
+	static Pattern	commands	= Pattern.compile("(?<!\\\\);");
+
+	private String doCommands(String key, Link source) {
+		String[] args = commands.split(key);
+		if (args == null || args.length == 0)
+			return null;
+
+		for (int i = 0; i < args.length; i++)
+			if (args[i].indexOf('\\') >= 0)
+				args[i] = args[i].replaceAll("\\\\;", ";");
+
+		
+		if ( args[0].startsWith("^")) {
+			String varname = args[0].substring(1).trim();
+			
+			Processor parent = source.start.getParent();
+			if ( parent != null)
+				return parent.getProperty(varname);
+			else
+				return null;
+		}
+		
+		
+		Processor rover = domain;
+		while (rover != null) {
+			String result = doCommand(rover, args[0], args);
+			if (result != null)
+				return result;
+
+			rover = rover.getParent();
+		}
+
+		for (int i = 0; targets != null && i < targets.length; i++) {
+			String result = doCommand(targets[i], args[0], args);
+			if (result != null)
+				return result;
+		}
+
+		return doCommand(this, args[0], args);
+	}
+
+	private String doCommand(Object target, String method, String[] args) {
+		if (target == null)
+			; // System.out.println("Huh? Target should never be null " +
+		// domain);
+		else {
+			String cname = "_" + method.replaceAll("-", "_");
+			try {
+				Method m = target.getClass().getMethod(cname, new Class[] { String[].class });
+				return (String) m.invoke(target, new Object[] { args });
+			} catch (NoSuchMethodException e) {
+				// Ignore
+			} catch (InvocationTargetException e) {
+				if ( e.getCause() instanceof IllegalArgumentException ) {
+					domain.error("%s, for cmd: %s, arguments; %s", e.getMessage(), method, Arrays.toString(args));
+				} else {
+					domain.warning("Exception in replace: " + e.getCause());
+					e.getCause().printStackTrace();
+				}
+			} catch (Exception e) {
+				domain.warning("Exception in replace: " + e + " method=" + method);
+				e.printStackTrace();
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * Return a unique list where the duplicates are removed.
+	 * 
+	 * @param args
+	 * @return
+	 */
+	static String	_uniqHelp	= "${uniq;<list> ...}";
+
+	public String _uniq(String args[]) {
+		verifyCommand(args, _uniqHelp, null, 1, Integer.MAX_VALUE);
+		Set<String> set = new LinkedHashSet<String>();
+		for (int i = 1; i < args.length; i++) {
+			Processor.split(args[i], set);
+		}
+		return Processor.join(set, ",");
+	}
+
+	public String _pathseparator(String args[]) {
+		return File.pathSeparator;
+	}
+
+	public String _separator(String args[]) {
+		return File.separator;
+	}
+
+	public String _filter(String args[]) {
+		return filter(args, false);
+	}
+
+	public String _filterout(String args[]) {
+		return filter(args, true);
+
+	}
+
+	static String	_filterHelp	= "${%s;<list>;<regex>}";
+
+	String filter(String[] args, boolean include) {
+		verifyCommand(args, String.format(_filterHelp, args[0]), null, 3, 3);
+
+		Collection<String> list = new ArrayList<String>(Processor.split(args[1]));
+		Pattern pattern = Pattern.compile(args[2]);
+
+		for (Iterator<String> i = list.iterator(); i.hasNext();) {
+			if (pattern.matcher(i.next()).matches() == include)
+				i.remove();
+		}
+		return Processor.join(list);
+	}
+
+	static String	_sortHelp	= "${sort;<list>...}";
+
+	public String _sort(String args[]) {
+		verifyCommand(args, _sortHelp, null, 2, Integer.MAX_VALUE);
+
+		List<String> result = new ArrayList<String>();
+		for (int i = 1; i < args.length; i++) {
+			Processor.split(args[i], result);
+		}
+		Collections.sort(result);
+		return Processor.join(result);
+	}
+
+	static String	_joinHelp	= "${join;<list>...}";
+
+	public String _join(String args[]) {
+
+		verifyCommand(args, _joinHelp, null, 1, Integer.MAX_VALUE);
+
+		List<String> result = new ArrayList<String>();
+		for (int i = 1; i < args.length; i++) {
+			Processor.split(args[i], result);
+		}
+		return Processor.join(result);
+	}
+
+	static String	_ifHelp	= "${if;<condition>;<iftrue> [;<iffalse>] }";
+
+	public String _if(String args[]) {
+		verifyCommand(args, _ifHelp, null, 3, 4);
+		String condition = args[1].trim();
+		if (condition.length() != 0)
+			return args[2];
+		if (args.length > 3)
+			return args[3];
+		else
+			return "";
+	}
+
+	public String _now(String args[]) {
+		return new Date().toString();
+	}
+
+	public static String	_fmodifiedHelp	= "${fmodified;<list of filenames>...}, return latest modification date";
+
+	public String _fmodified(String args[]) throws Exception {
+		verifyCommand(args, _fmodifiedHelp, null, 2, Integer.MAX_VALUE);
+
+		long time = 0;
+		Collection<String> names = new ArrayList<String>();
+		for (int i = 1; i < args.length; i++) {
+			Processor.split(args[i], names);
+		}
+		for (String name : names) {
+			File f = new File(name);
+			if (f.exists() && f.lastModified() > time)
+				time = f.lastModified();
+		}
+		return "" + time;
+	}
+
+	public String _long2date(String args[]) {
+		try {
+			return new Date(Long.parseLong(args[1])).toString();
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		return "not a valid long";
+	}
+
+	public String _literal(String args[]) {
+		if (args.length != 2)
+			throw new RuntimeException("Need a value for the ${literal;<value>} macro");
+		return "${" + args[1] + "}";
+	}
+
+	public String _def(String args[]) {
+		if (args.length != 2)
+			throw new RuntimeException("Need a value for the ${def;<value>} macro");
+
+		return domain.getProperty(args[1], "");
+	}
+
+	/**
+	 * 
+	 * replace ; <list> ; regex ; replace
+	 * 
+	 * @param args
+	 * @return
+	 */
+	public String _replace(String args[]) {
+		if (args.length != 4) {
+			domain.warning("Invalid nr of arguments to replace " + Arrays.asList(args));
+			return null;
+		}
+
+		String list[] = args[1].split("\\s*,\\s*");
+		StringBuffer sb = new StringBuffer();
+		String del = "";
+		for (int i = 0; i < list.length; i++) {
+			String element = list[i].trim();
+			if (!element.equals("")) {
+				sb.append(del);
+				sb.append(element.replaceAll(args[2], args[3]));
+				del = ", ";
+			}
+		}
+
+		return sb.toString();
+	}
+
+	public String _warning(String args[]) {
+		for (int i = 1; i < args.length; i++) {
+			domain.warning(process(args[i]));
+		}
+		return "";
+	}
+
+	public String _error(String args[]) {
+		for (int i = 1; i < args.length; i++) {
+			domain.error(process(args[i]));
+		}
+		return "";
+	}
+
+	/**
+	 * toclassname ; <path>.class ( , <path>.class ) *
+	 * 
+	 * @param args
+	 * @return
+	 */
+	static String	_toclassnameHelp	= "${classname;<list of class names>}, convert class paths to FQN class names ";
+
+	public String _toclassname(String args[]) {
+		verifyCommand(args, _toclassnameHelp, null, 2, 2);
+		Collection<String> paths = Processor.split(args[1]);
+
+		List<String> names = new ArrayList<String>(paths.size());
+		for (String path : paths) {
+			if (path.endsWith(".class")) {
+				String name = path.substring(0, path.length() - 6).replace('/', '.');
+				names.add(name);
+			} else if (path.endsWith(".java")) {
+				String name = path.substring(0, path.length() - 5).replace('/', '.');
+				names.add(name);
+			} else {
+				domain.warning("in toclassname, " + args[1]
+						+ " is not a class path because it does not end in .class");
+			}
+		}
+		return Processor.join(names, ",");
+	}
+
+	/**
+	 * toclassname ; <path>.class ( , <path>.class ) *
+	 * 
+	 * @param args
+	 * @return
+	 */
+
+	static String	_toclasspathHelp	= "${toclasspath;<list>[;boolean]}, convert a list of class names to paths";
+
+	public String _toclasspath(String args[]) {
+		verifyCommand(args, _toclasspathHelp, null, 2, 3);
+		boolean cl = true;
+		if (args.length > 2)
+			cl = new Boolean(args[2]);
+
+		Collection<String> names = Processor.split(args[1]);
+		Collection<String> paths = new ArrayList<String>(names.size());
+		for (String name : names) {
+			String path = name.replace('.', '/') + (cl ? ".class" : "");
+			paths.add(path);
+		}
+		return Processor.join(paths, ",");
+	}
+
+	public String _dir(String args[]) {
+		if (args.length < 2) {
+			domain.warning("Need at least one file name for ${dir;...}");
+			return null;
+		} else {
+			String del = "";
+			StringBuffer sb = new StringBuffer();
+			for (int i = 1; i < args.length; i++) {
+				File f = domain.getFile(args[i]);
+				if (f.exists() && f.getParentFile().exists()) {
+					sb.append(del);
+					sb.append(f.getParentFile().getAbsolutePath());
+					del = ",";
+				}
+			}
+			return sb.toString();
+		}
+
+	}
+
+	public String _basename(String args[]) {
+		if (args.length < 2) {
+			domain.warning("Need at least one file name for ${basename;...}");
+			return null;
+		} else {
+			String del = "";
+			StringBuffer sb = new StringBuffer();
+			for (int i = 1; i < args.length; i++) {
+				File f = domain.getFile(args[i]);
+				if (f.exists() && f.getParentFile().exists()) {
+					sb.append(del);
+					sb.append(f.getName());
+					del = ",";
+				}
+			}
+			return sb.toString();
+		}
+
+	}
+
+	public String _isfile(String args[]) {
+		if (args.length < 2) {
+			domain.warning("Need at least one file name for ${isfile;...}");
+			return null;
+		} else {
+			boolean isfile = true;
+			for (int i = 1; i < args.length; i++) {
+				File f = new File(args[i]).getAbsoluteFile();
+				isfile &= f.isFile();
+			}
+			return isfile ? "true" : "false";
+		}
+
+	}
+
+	public String _isdir(String args[]) {
+		if (args.length < 2) {
+			domain.warning("Need at least one file name for ${isdir;...}");
+			return null;
+		} else {
+			boolean isdir = true;
+			for (int i = 1; i < args.length; i++) {
+				File f = new File(args[i]).getAbsoluteFile();
+				isdir &= f.isDirectory();
+			}
+			return isdir ? "true" : "false";
+		}
+
+	}
+
+	public String _tstamp(String args[]) {
+		String format = "yyyyMMddHHmm";
+		long now = System.currentTimeMillis();
+
+		if (args.length > 1) {
+			format = args[1];
+			if (args.length > 2) {
+				now = Long.parseLong(args[2]);
+				if (args.length > 3) {
+					domain.warning("Too many arguments for tstamp: " + Arrays.toString(args));
+				}
+			}
+		}
+		SimpleDateFormat sdf = new SimpleDateFormat(format);
+		return sdf.format(new Date(now));
+	}
+
+	/**
+	 * Wildcard a directory. The lists can contain Instruction that are matched
+	 * against the given directory
+	 * 
+	 * ${lsr;<dir>;<list>(;<list>)*} ${lsa;<dir>;<list>(;<list>)*}
+	 * 
+	 * @author aqute
+	 * 
+	 */
+
+	public String _lsr(String args[]) {
+		return ls(args, true);
+	}
+
+	public String _lsa(String args[]) {
+		return ls(args, false);
+	}
+
+	String ls(String args[], boolean relative) {
+		if (args.length < 2)
+			throw new IllegalArgumentException(
+					"the ${ls} macro must at least have a directory as parameter");
+
+		File dir = domain.getFile(args[1]);
+		if (!dir.isAbsolute())
+			throw new IllegalArgumentException(
+					"the ${ls} macro directory parameter is not absolute: " + dir);
+
+		if (!dir.exists())
+			throw new IllegalArgumentException(
+					"the ${ls} macro directory parameter does not exist: " + dir);
+
+		if (!dir.isDirectory())
+			throw new IllegalArgumentException(
+					"the ${ls} macro directory parameter points to a file instead of a directory: "
+							+ dir);
+
+		String[] files = dir.list();
+		List<String> result;
+
+		if (args.length < 3) {
+			result = Arrays.asList(files);
+		} else
+			result = new ArrayList<String>();
+
+		for (int i = 2; i < args.length; i++) {
+			String parts[] = args[i].split("\\s*,\\s*");
+			for (String pattern : parts) {
+				// So make it in to an instruction
+				Instruction instr = Instruction.getPattern(pattern);
+
+				// For each project, match it against the instruction
+				for (int f = 0; f < files.length; f++) {
+					if (files[f] != null) {
+						if (instr.matches(files[f])) {
+							if (!instr.isNegated()) {
+								if (relative)
+									result.add(files[f]);
+								else
+									result.add(new File(dir, files[f]).getAbsolutePath());
+							}
+							files[f] = null;
+						}
+					}
+				}
+			}
+		}
+		return Processor.join(result, ",");
+	}
+
+	public String _currenttime(String args[]) {
+		return Long.toString(System.currentTimeMillis());
+	}
+
+	/**
+	 * Modify a version to set a version policy. Thed policy is a mask that is
+	 * mapped to a version.
+	 * 
+	 * <pre>
+	 * +           increment
+	 * -           decrement
+	 * =           maintain
+	 * &tilde;           discard
+	 * 
+	 * ==+      = maintain major, minor, increment micro, discard qualifier
+	 * &tilde;&tilde;&tilde;=     = just get the qualifier
+	 * version=&quot;[${version;==;${@}},${version;=+;${@}})&quot;
+	 * </pre>
+	 * 
+	 * 
+	 * 
+	 * 
+	 * @param args
+	 * @return
+	 */
+	final static String		MASK_STRING			= "[\\-+=~0123456789]{0,3}[=~]?";
+	final static Pattern	MASK				= Pattern.compile(MASK_STRING);
+	final static String		_versionHelp		= "${version;<mask>;<version>}, modify a version\n"
+														+ "<mask> ::= [ M [ M [ M [ MQ ]]]\n"
+														+ "M ::= '+' | '-' | MQ\n"
+														+ "MQ ::= '~' | '='";
+	final static Pattern	_versionPattern[]	= new Pattern[] { null, null, MASK,
+			Verifier.VERSION					};
+
+	public String _version(String args[]) {
+		verifyCommand(args, _versionHelp, null, 2, 3);
+
+		String mask = args[1];
+
+		Version version = null;
+		if (args.length >= 3)
+			version = new Version(args[2]);
+
+		return version(version, mask);
+	}
+
+	String version(Version version, String mask) {
+		if (version == null) {
+			String v = domain.getProperty("@");
+			if (v == null) {
+				domain
+						.error(
+								"No version specified for ${version} or ${range} and no implicit version ${@} either, mask=%s",
+								mask);
+				v = "0";
+			}
+			version = new Version(v);
+		}
+
+		StringBuilder sb = new StringBuilder();
+		String del = "";
+
+		for (int i = 0; i < mask.length(); i++) {
+			char c = mask.charAt(i);
+			String result = null;
+			if (c != '~') {
+				if (i == 3) {
+					result = version.getQualifier();
+				} else if (Character.isDigit(c)) {
+					// Handle masks like +00, =+0
+					result = String.valueOf(c);
+				} else {
+					int x = version.get(i);
+					switch (c) {
+					case '+':
+						x++;
+						break;
+					case '-':
+						x--;
+						break;
+					case '=':
+						break;
+					}
+					result = Integer.toString(x);
+				}
+				if (result != null) {
+					sb.append(del);
+					del = ".";
+					sb.append(result);
+				}
+			}
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * Schortcut for version policy
+	 * 
+	 * <pre>
+	 * -provide-policy : ${policy;[==,=+)}
+	 * -consume-policy : ${policy;[==,+)}
+	 * </pre>
+	 * 
+	 * @param args
+	 * @return
+	 */
+
+	static Pattern	RANGE_MASK		= Pattern.compile("(\\[|\\()(" + MASK_STRING + "),(" + MASK_STRING +")(\\]|\\))");
+	static String	_rangeHelp		= "${range;<mask>[;<version>]}, range for version, if version not specified lookyp ${@}\n"
+											+ "<mask> ::= [ M [ M [ M [ MQ ]]]\n"
+											+ "M ::= '+' | '-' | MQ\n" + "MQ ::= '~' | '='";
+	static Pattern	_rangePattern[]	= new Pattern[] { null, RANGE_MASK };
+
+	public String _range(String args[]) {
+		verifyCommand(args, _rangeHelp, _rangePattern, 2, 3);
+		Version version = null;
+		if (args.length >= 3)
+			version = new Version(args[2]);
+
+		String spec = args[1];
+
+		Matcher m = RANGE_MASK.matcher(spec);
+		m.matches();
+		String floor = m.group(1);
+		String floorMask = m.group(2);
+		String ceilingMask = m.group(3);
+		String ceiling = m.group(4);
+
+		StringBuilder sb = new StringBuilder();
+		sb.append(floor);
+		sb.append(version(version, floorMask));
+		sb.append(",");
+		sb.append(version(version, ceilingMask));
+		sb.append(ceiling);
+
+		String s = sb.toString();
+		VersionRange vr = new VersionRange(s);
+		if (!(vr.includes(vr.getHigh()) || vr.includes(vr.getLow()))) {
+			domain.error("${range} macro created an invalid range %s from %s and mask %s", s,
+					version, spec);
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * System command. Execute a command and insert the result.
+	 * 
+	 * @param args
+	 * @param help
+	 * @param patterns
+	 * @param low
+	 * @param high
+	 */
+	public String _system(String args[]) throws Exception {
+		verifyCommand(args, "${system;<command>[;<in>]}, execute a system command", null, 2, 3);
+		String command = args[1];
+		String input = null;
+
+		if (args.length > 2) {
+			input = args[2];
+		}
+
+		Process process = Runtime.getRuntime().exec(command, null, domain.getBase());
+		if (input != null) {
+			process.getOutputStream().write(input.getBytes("UTF-8"));
+		}
+		process.getOutputStream().close();
+
+		String s = IO.collect(process.getInputStream(), "UTF-8");
+		int exitValue = process.waitFor();
+		if (exitValue != 0) {
+			domain.error("System command " + command + " failed with " + exitValue);
+		}
+		return s.trim();
+	}
+
+	public String _env(String args[]) {
+		verifyCommand(args, "${env;<name>}, get the environmet variable", null, 2, 2);
+
+		try {
+			return System.getenv(args[1]);
+		} catch (Throwable t) {
+			return null;
+		}
+	}
+
+	/**
+	 * Get the contents of a file.
+	 * 
+	 * @param in
+	 * @return
+	 * @throws IOException
+	 */
+
+	public String _cat(String args[]) throws IOException {
+		verifyCommand(args, "${cat;<in>}, get the content of a file", null, 2, 2);
+		File f = domain.getFile(args[1]);
+		if (f.isFile()) {
+			return IO.collect(f);
+		} else if (f.isDirectory()) {
+			return Arrays.toString(f.list());
+		} else {
+			try {
+				URL url = new URL(args[1]);
+				return IO.collect(url, "UTF-8");
+			} catch (MalformedURLException mfue) {
+				// Ignore here
+			}
+			return null;
+		}
+	}
+
+	public static void verifyCommand(String args[], String help, Pattern[] patterns, int low,
+			int high) {
+		String message = "";
+		if (args.length > high) {
+			message = "too many arguments";
+		} else if (args.length < low) {
+			message = "too few arguments";
+		} else {
+			for (int i = 0; patterns != null && i < patterns.length && i < args.length; i++) {
+				if (patterns[i] != null) {
+					Matcher m = patterns[i].matcher(args[i]);
+					if (!m.matches())
+						message += String.format("Argument %s (%s) does not match %s\n", i,
+								args[i], patterns[i].pattern());
+				}
+			}
+		}
+		if (message.length() != 0) {
+			StringBuilder sb = new StringBuilder();
+			String del = "${";
+			for (String arg : args) {
+				sb.append(del);
+				sb.append(arg);
+				del = ";";
+			}
+			sb.append("}, is not understood. ");
+			sb.append(message);
+			throw new IllegalArgumentException(sb.toString());
+		}
+	}
+
+	// Helper class to track expansion of variables
+	// on the stack.
+	static class Link {
+		Link	previous;
+		String	key;
+		Processor start;
+
+		public Link(Processor start, Link previous, String key) {
+			this.previous = previous;
+			this.key = key;
+			this.start = start;
+		}
+
+		public boolean contains(String key) {
+			if (this.key.equals(key))
+				return true;
+
+			if (previous == null)
+				return false;
+
+			return previous.contains(key);
+		}
+
+		public String toString() {
+			StringBuffer sb = new StringBuffer();
+			String del = "[";
+			for (Link r = this; r != null; r = r.previous) {
+				sb.append(del);
+				sb.append(r.key);
+				del = ",";
+			}
+			sb.append("]");
+			return sb.toString();
+		}
+	}
+
+	/**
+	 * Take all the properties and translate them to actual values. This method
+	 * takes the set properties and traverse them over all entries, including
+	 * the default properties for that properties. The values no longer contain
+	 * macros.
+	 * 
+	 * @return A new Properties with the flattened values
+	 */
+	public Properties getFlattenedProperties() {
+		// Some macros only work in a lower processor, so we
+		// do not report unknown macros while flattening
+		flattening = true;
+		try {
+			Properties flattened = new Properties();
+			Properties source = domain.getProperties();
+			for (Enumeration<?> e = source.propertyNames(); e.hasMoreElements();) {
+				String key = (String) e.nextElement();
+				if (!key.startsWith("_"))
+					if (key.startsWith("-"))
+						flattened.put(key, source.getProperty(key));
+					else
+						flattened.put(key, process(source.getProperty(key)));
+			}
+			return flattened;
+		} finally {
+			flattening = false;
+		}
+	};
+
+	public static String	_fileHelp	= "${file;<base>;<paths>...}, create correct OS dependent path";
+
+	public String _osfile(String args[]) {
+		verifyCommand(args, _fileHelp, null, 3, 3);
+		File base = new File(args[1]);
+		File f = Processor.getFile(base, args[2]);
+		return f.getAbsolutePath();
+	}
+
+	public String _path(String args[]) {
+		List<String> list = new ArrayList<String>();
+		for (int i = 1; i < args.length; i++) {
+			list.addAll(Processor.split(args[i]));
+		}
+		return Processor.join(list, File.pathSeparator);
+	}
+
+	public static Properties getParent(Properties p) {
+		try {
+			Field f = Properties.class.getDeclaredField("defaults");
+			f.setAccessible(true);
+			return (Properties) f.get(p);
+		} catch (Exception e) {
+			Field[] fields = Properties.class.getFields();
+			System.out.println(Arrays.toString(fields));
+			return null;
+		}
+	}
+
+	public String process(String line) {
+		return process(line,domain);
+	}
+
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Macro.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Macro.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision