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 2009/07/13 12:06:50 UTC

svn commit: r793527 [2/7] - in /felix/trunk/bundleplugin: ./ src/main/java/aQute/ src/main/java/aQute/bnd/ src/main/java/aQute/bnd/build/ src/main/java/aQute/bnd/help/ src/main/java/aQute/bnd/make/ src/main/java/aQute/bnd/service/ src/main/java/aQute/b...

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/Make.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/Make.java?rev=793527&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/Make.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/Make.java Mon Jul 13 10:06:47 2009
@@ -0,0 +1,101 @@
+package aQute.bnd.make;
+
+import java.util.*;
+import java.util.regex.*;
+
+import aQute.bnd.service.*;
+import aQute.lib.osgi.*;
+
+public class Make {
+    Builder                               builder;
+    Map<Instruction, Map<String, String>> make;
+
+    public Make(Builder builder) {
+        this.builder = builder;
+        // builder.getPlugins().add(new MakeBnd());
+        // builder.getPlugins().add(new MakeCopy());
+    }
+
+    public Resource process(String source) {
+        Map<Instruction, Map<String, String>> make = getMakeHeader();
+        builder.trace("make " + source);
+
+        for (Map.Entry<Instruction, Map<String, String>> entry : make
+                .entrySet()) {
+            Instruction instr = (Instruction) entry.getKey();
+            Matcher m = instr.getMatcher(source);
+            if (m.matches() || instr.isNegated()) {
+                Map<String, String> arguments = replace(m, entry.getValue());
+                List<MakePlugin> plugins = builder.getPlugins(MakePlugin.class);
+                for (MakePlugin plugin : plugins) {
+                    try {
+                        Resource resource = plugin.make(builder,
+                                source, arguments);
+                        if (resource != null) {
+                            builder.trace("Made " + source + " from args "
+                                    + arguments + " with " + plugin);
+                            return resource;
+                        }
+                    } catch (Exception e) {
+                        builder.error("Plugin " + plugin
+                                + " generates error when use in making "
+                                + source + " with args " + arguments, e);
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    private Map<String, String> replace(Matcher m, Map<String, String> value) {
+        Map<String, String> newArgs = Processor.newMap();
+        for (Map.Entry<String, String> entry : value.entrySet()) {
+            String s = entry.getValue();
+            s = replace(m, s);
+            newArgs.put(entry.getKey(), s);
+        }
+        return newArgs;
+    }
+
+    String replace(Matcher m, CharSequence s) {
+        StringBuffer sb = new StringBuffer();
+        int max = '0' + m.groupCount() + 1;
+        for (int i = 0; i < s.length(); i++) {
+            char c = s.charAt(i);
+            if (c == '$' && i < s.length() - 1) {
+                c = s.charAt(++i);
+                if (c >= '0' && c <= max) {
+                    int index = c - '0';
+                    String replacement = m.group(index);
+                    if (replacement != null)
+                        sb.append(replacement);
+                } else {
+                    if (c == '$')
+                        i++;
+                    sb.append(c);
+                }
+            } else
+                sb.append(c);
+        }
+        return sb.toString();
+    }
+
+    Map<Instruction, Map<String, String>> getMakeHeader() {
+        if (make != null)
+            return make;
+        make = Processor.newMap();
+
+        String s = builder.getProperty(Builder.MAKE);
+        Map<String, Map<String, String>> make = builder.parseHeader(s);
+        for (Iterator<Map.Entry<String, Map<String, String>>> e = make
+                .entrySet().iterator(); e.hasNext();) {
+            Map.Entry<String, Map<String, String>> entry = e.next();
+            String pattern = Processor.removeDuplicateMarker(entry.getKey());
+            
+            Instruction instr = Instruction.getPattern(pattern);
+            this.make.put(instr, entry.getValue());
+        }
+
+        return this.make;
+    }
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/Make.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/MakeBnd.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/MakeBnd.java?rev=793527&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/MakeBnd.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/MakeBnd.java Mon Jul 13 10:06:47 2009
@@ -0,0 +1,64 @@
+package aQute.bnd.make;
+
+import java.io.*;
+import java.util.*;
+import java.util.regex.*;
+
+import aQute.bnd.build.*;
+import aQute.bnd.service.*;
+import aQute.lib.osgi.*;
+
+public class MakeBnd implements MakePlugin, Constants {
+    final static Pattern JARFILE = Pattern.compile("(.+)\\.(jar|ipa)");
+
+    public Resource make(Builder builder, String destination,
+            Map<String, String> argumentsOnMake) throws Exception {
+        String type = (String) argumentsOnMake.get("type");
+        if (!"bnd".equals(type))
+            return null;
+
+        String recipe = (String) argumentsOnMake.get("recipe");
+        if (recipe == null) {
+            builder.error("No recipe specified on a make instruction for "
+                    + destination);
+            return null;
+        }
+        File bndfile = builder.getFile(recipe);
+        if (bndfile.isFile()) {
+            // We do not use a parent because then we would
+            // build ourselves again. So we can not blindly
+            // inherit the properties.
+            Builder bchild = builder.getSubBuilder();
+            bchild.removeBundleSpecificHeaders();
+            
+            // We must make sure that we do not include ourselves again!
+            bchild.setProperty(Analyzer.INCLUDE_RESOURCE, "");
+            bchild.setProperties(bndfile, builder.getBase());
+            
+            Jar jar = bchild.build();
+            Jar dot = builder.getTarget();
+
+            if (builder.hasSources()) {
+                for (String key : jar.getResources().keySet()) {
+                    if (key.startsWith("OSGI-OPT/src"))
+                        dot.putResource(key, (Resource) jar.getResource(key));
+                }
+            }
+            builder.getInfo(bchild, bndfile.getName() +": ");
+            String debug = bchild.getProperty(DEBUG);
+            if (Processor.isTrue(debug)) {
+                if ( builder instanceof ProjectBuilder ) {
+                    ProjectBuilder pb = (ProjectBuilder) builder;
+                    File target = pb.getProject().getTarget();
+                    target.mkdirs();
+                    String bsn = bchild.getBsn();
+                    File output = new File(target, bsn+".jar");
+                    jar.write(output);
+                }
+            }
+            return new JarResource(jar);
+        } else
+            return null;
+    }
+
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/MakeBnd.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/MakeCopy.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/MakeCopy.java?rev=793527&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/MakeCopy.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/MakeCopy.java Mon Jul 13 10:06:47 2009
@@ -0,0 +1,45 @@
+package aQute.bnd.make;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+import aQute.bnd.service.*;
+import aQute.lib.osgi.*;
+
+public class MakeCopy implements MakePlugin {
+
+    public Resource make(Builder builder, String destination,
+            Map<String, String> argumentsOnMake) throws Exception {
+        String type = argumentsOnMake.get("type");
+        if (!type.equals("copy"))
+            return null;
+
+        String from = argumentsOnMake.get("from");
+        if (from == null) {
+            String content = argumentsOnMake.get("content");
+            if (content == null)
+                throw new IllegalArgumentException(
+                        "No 'from' or 'content' field in copy "
+                                + argumentsOnMake);
+            return new EmbeddedResource(content.getBytes("UTF-8"),0);
+        } else {
+
+            File f = builder.getFile(from);
+            if (f.isFile())
+                return new FileResource(f);
+            else {
+                try {
+                    URL url = new URL(from);
+                    return new URLResource(url);
+                } catch(MalformedURLException mfue) {
+                    // We ignore this
+                }
+                throw new IllegalArgumentException(
+                        "Copy source does not exist " + from
+                                + " for destination " + destination);
+            }
+        }
+    }
+
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/MakeCopy.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/ServiceComponent.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/ServiceComponent.java?rev=793527&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/ServiceComponent.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/ServiceComponent.java Mon Jul 13 10:06:47 2009
@@ -0,0 +1,408 @@
+package aQute.bnd.make;
+
+import java.io.*;
+import java.util.*;
+import java.util.regex.*;
+
+import aQute.bnd.service.*;
+import aQute.lib.filter.*;
+import aQute.lib.osgi.*;
+import aQute.libg.version.*;
+
+/**
+ * This class is an analyzer plugin. It looks at the properties and tries to
+ * find out if the Service-Component header contains the bnd shortut syntax. If
+ * not, the header is copied to the output, if it does, an XML file is created
+ * and added to the JAR and the header is modified appropriately.
+ */
+public class ServiceComponent implements AnalyzerPlugin {
+    public final static String      NAMESPACE_STEM                 = "http://www.osgi.org/xmlns/scr";
+    public final static String      JIDENTIFIER                    = "<<identifier>>";
+    public final static String      COMPONENT_FACTORY              = "factory:";
+    public final static String      COMPONENT_SERVICEFACTORY       = "servicefactory:";
+    public final static String      COMPONENT_IMMEDIATE            = "immediate:";
+    public final static String      COMPONENT_ENABLED              = "enabled:";
+    public final static String      COMPONENT_DYNAMIC              = "dynamic:";
+    public final static String      COMPONENT_MULTIPLE             = "multiple:";
+    public final static String      COMPONENT_PROVIDE              = "provide:";
+    public final static String      COMPONENT_OPTIONAL             = "optional:";
+    public final static String      COMPONENT_PROPERTIES           = "properties:";
+    public final static String      COMPONENT_IMPLEMENTATION       = "implementation:";
+
+    // v1.1.0
+    public final static String      COMPONENT_VERSION              = "version:";
+    public final static String      COMPONENT_CONFIGURATION_POLICY = "configuration-policy:";
+    public final static String      COMPONENT_MODIFIED             = "modified:";
+    public final static String      COMPONENT_ACTIVATE             = "activate:";
+    public final static String      COMPONENT_DEACTIVATE           = "deactivate:";
+
+    public final static String[]    componentDirectives            = new String[] {
+            COMPONENT_FACTORY, COMPONENT_IMMEDIATE, COMPONENT_ENABLED,
+            COMPONENT_DYNAMIC, COMPONENT_MULTIPLE, COMPONENT_PROVIDE,
+            COMPONENT_OPTIONAL, COMPONENT_PROPERTIES, COMPONENT_IMPLEMENTATION,
+            COMPONENT_SERVICEFACTORY, COMPONENT_VERSION,
+            COMPONENT_CONFIGURATION_POLICY, COMPONENT_MODIFIED,
+            COMPONENT_ACTIVATE, COMPONENT_DEACTIVATE       };
+
+    public final static Set<String> SET_COMPONENT_DIRECTIVES       = new HashSet<String>(
+                                                                    Arrays
+                                                                            .asList(componentDirectives));
+
+    public final static Set<String> SET_COMPONENT_DIRECTIVES_1_1   = //
+                                                            new HashSet<String>(
+                                                                    Arrays
+                                                                            .asList(
+                                                                                    COMPONENT_VERSION,
+                                                                                    COMPONENT_CONFIGURATION_POLICY,
+                                                                                    COMPONENT_MODIFIED,
+                                                                                    COMPONENT_ACTIVATE,
+                                                                                    COMPONENT_DEACTIVATE));
+
+    public boolean analyzeJar(Analyzer analyzer) throws Exception {
+
+        ComponentMaker m = new ComponentMaker(analyzer);
+
+        Map<String, Map<String, String>> l = m.doServiceComponent();
+
+        if (!l.isEmpty())
+            analyzer.setProperty(Constants.SERVICE_COMPONENT, Processor
+                    .printClauses(l, ""));
+
+        analyzer.getInfo(m, "Service Component");
+        m.close();
+        return false;
+    }
+
+    private static class ComponentMaker extends Processor {
+        Analyzer analyzer;
+
+        ComponentMaker(Analyzer analyzer) {
+            super(analyzer);
+            this.analyzer = analyzer;
+        }
+
+        Map<String, Map<String, String>> doServiceComponent() throws Exception {
+            String header = getProperty(SERVICE_COMPONENT);
+            return doServiceComponent(header);
+        }
+
+        /**
+         * Check if a service component header is actually referring to a class.
+         * If so, replace the reference with an XML file reference. This makes
+         * it easier to create and use components.
+         * 
+         * @throws UnsupportedEncodingException
+         * 
+         */
+        public Map<String, Map<String, String>> doServiceComponent(
+                String serviceComponent) throws IOException {
+            Map<String, Map<String, String>> list = newMap();
+            Map<String, Map<String, String>> sc = parseHeader(serviceComponent);
+            Map<String, String> empty = Collections.emptyMap();
+
+            for (Iterator<Map.Entry<String, Map<String, String>>> i = sc
+                    .entrySet().iterator(); i.hasNext();) {
+                Map.Entry<String, Map<String, String>> entry = i.next();
+                String name = entry.getKey();
+                Map<String, String> info = entry.getValue();
+                if (name == null) {
+                    error("No name in Service-Component header: " + info);
+                    continue;
+                }
+                if (name.indexOf("*") >= 0 || analyzer.getJar().exists(name)) {
+                    // Normal service component, we do not process them
+                    list.put(name, info);
+                } else {
+                    String impl = name;
+
+                    if (info.containsKey(COMPONENT_IMPLEMENTATION))
+                        impl = info.get(COMPONENT_IMPLEMENTATION);
+
+                    if (!analyzer.checkClass(impl)) {
+                        error("Not found Service-Component header: " + name);
+                    } else {
+                        // We have a definition, so make an XML resources
+                        Resource resource = createComponentResource(name, info);
+                        analyzer.getJar().putResource(
+                                "OSGI-INF/" + name + ".xml", resource);
+                        list.put("OSGI-INF/" + name + ".xml", empty);
+                    }
+                }
+            }
+            return list;
+        }
+
+        /**
+         * Create the resource for a DS component.
+         * 
+         * @param list
+         * @param name
+         * @param info
+         * @throws UnsupportedEncodingException
+         */
+        Resource createComponentResource(String name, Map<String, String> info)
+                throws IOException {
+            String namespace = getNamespace(info);
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            PrintWriter pw = new PrintWriter(new OutputStreamWriter(out,
+                    "UTF-8"));
+            pw.println("<?xml version='1.0' encoding='utf-8'?>");
+            pw.print("<component name='" + name + "'");
+            if (namespace != null) {
+                pw.print(" xmlns='" + namespace + "'");
+            }
+
+            doAttribute(pw, info.get(COMPONENT_FACTORY), "factory");
+            doAttribute(pw, info.get(COMPONENT_IMMEDIATE), "immediate",
+                    "false", "true");
+            doAttribute(pw, info.get(COMPONENT_ENABLED), "enabled", "true",
+                    "false");
+            doAttribute(pw, info.get(COMPONENT_CONFIGURATION_POLICY),
+                    "configuration-policy", "optional", "require", "ignore");
+            doAttribute(pw, info.get(COMPONENT_ACTIVATE), "activate",
+                    JIDENTIFIER);
+            doAttribute(pw, info.get(COMPONENT_DEACTIVATE), "deactivate",
+                    JIDENTIFIER);
+            doAttribute(pw, info.get(COMPONENT_MODIFIED), "modified",
+                    JIDENTIFIER);
+
+            pw.println(">");
+
+            // Allow override of the implementation when people
+            // want to choose their own name
+            String impl = (String) info.get(COMPONENT_IMPLEMENTATION);
+            pw.println("  <implementation class='"
+                    + (impl == null ? name : impl) + "'/>");
+
+            String provides = info.get(COMPONENT_PROVIDE);
+            boolean servicefactory = Boolean.getBoolean(info
+                    .get(COMPONENT_SERVICEFACTORY)
+                    + "");
+            provides(pw, provides, servicefactory);
+            properties(pw, info);
+            reference(info, pw);
+            pw.println("</component>");
+            pw.close();
+            byte[] data = out.toByteArray();
+            out.close();
+            return new EmbeddedResource(data, 0);
+        }
+
+        private void doAttribute(PrintWriter pw, String value, String name,
+                String... matches) {
+            if (value != null) {
+                if (matches.length != 0) {
+                    if (matches.length == 1 && matches[0].equals(JIDENTIFIER)) {
+                        if (!Verifier.isIdentifier(value))
+                            error(
+                                    "Component attribute %s has value %s but is not a Java identifier",
+                                    name, value);
+                    } else {
+
+                        if (!Verifier.isMember(value, matches))
+                            error(
+                                    "Component attribute %s has value %s but is not a member of %s",
+                                    name, value, Arrays.toString(matches));
+                    }
+                }
+                pw.print(" ");
+                pw.print(name);
+                pw.print("='");
+                pw.print(value);
+                pw.print("'");
+            }
+        }
+
+        /**
+         * Check if we need to use the v1.1 namespace (or later).
+         * 
+         * @param info
+         * @return
+         */
+        private String getNamespace(Map<String, String> info) {
+            String version = info.get(COMPONENT_VERSION);
+            if (version != null) {
+                try {
+                    Version v = new Version(version);
+                    return NAMESPACE_STEM + "/v" + v;
+                } catch (Exception e) {
+                    error("version: specified on component header but not a valid version: "
+                            + version);
+                    return null;
+                }
+            }
+            for (String key : info.keySet()) {
+                if (SET_COMPONENT_DIRECTIVES_1_1.contains(key)) {
+                    return NAMESPACE_STEM + "/v1.1.0";
+                }
+            }
+            return null;
+        }
+
+        /**
+         * Print the Service-Component properties element
+         * 
+         * @param pw
+         * @param info
+         */
+        void properties(PrintWriter pw, Map<String, String> info) {
+            Collection<String> properties = split(info
+                    .get(COMPONENT_PROPERTIES));
+            for (Iterator<String> p = properties.iterator(); p.hasNext();) {
+                String clause = p.next();
+                int n = clause.indexOf('=');
+                if (n <= 0) {
+                    error("Not a valid property in service component: "
+                            + clause);
+                } else {
+                    String type = null;
+                    String name = clause.substring(0, n);
+                    if (name.indexOf('@') >= 0) {
+                        String parts[] = name.split("@");
+                        name = parts[1];
+                        type = parts[0];
+                    }
+                    String value = clause.substring(n + 1).trim();
+                    // TODO verify validity of name and value.
+                    pw.print("<property name='");
+                    pw.print(name);
+                    pw.print("'");
+
+                    if (type != null) {
+                        if (VALID_PROPERTY_TYPES.matcher(type).matches()) {
+                            pw.print(" type='");
+                            pw.print(type);
+                            pw.print("'");
+                        } else {
+                            warning("Invalid property type '" + type
+                                    + "' for property " + name);
+                        }
+                    }
+
+                    String parts[] = value.split("\\s*(\\||\\n)\\s*");
+                    if (parts.length > 1) {
+                        pw.println(">");
+                        for (String part : parts) {
+                            pw.println(part);
+                        }
+                        pw.println("</property>");
+                    } else {
+                        pw.print(" value='");
+                        pw.print(parts[0]);
+                        pw.print("'/>");
+                    }
+                }
+            }
+        }
+
+        /**
+         * @param pw
+         * @param provides
+         */
+        void provides(PrintWriter pw, String provides, boolean servicefactory) {
+            if (provides != null) {
+                if (!servicefactory)
+                    pw.println("  <service>");
+                else
+                    pw.println("  <service servicefactory='true'>");
+
+                StringTokenizer st = new StringTokenizer(provides, ",");
+                while (st.hasMoreTokens()) {
+                    String interfaceName = st.nextToken();
+                    pw.println("    <provide interface='" + interfaceName
+                            + "'/>");
+                    if (!analyzer.checkClass(interfaceName))
+                        error("Component definition provides a class that is neither imported nor contained: "
+                                + interfaceName);
+                }
+                pw.println("  </service>");
+            }
+        }
+
+        public final static Pattern REFERENCE = Pattern.compile("([^(]+)(\\(.+\\))?");
+
+        /**
+         * @param info
+         * @param pw
+         */
+
+        void reference(Map<String, String> info, PrintWriter pw) {
+            Collection<String> dynamic = new ArrayList<String>(split(info.get(COMPONENT_DYNAMIC)));
+            Collection<String> optional =  new ArrayList<String>(split(info.get(COMPONENT_OPTIONAL)));
+            Collection<String> multiple = new ArrayList<String>(split(info.get(COMPONENT_MULTIPLE)));
+
+            for (Iterator<Map.Entry<String, String>> r = info.entrySet()
+                    .iterator(); r.hasNext();) {
+                Map.Entry<String, String> ref = r.next();
+                String referenceName = (String) ref.getKey();
+                String target = null;
+                String interfaceName = (String) ref.getValue();
+                if (interfaceName == null || interfaceName.length() == 0) {
+                    error("Invalid Interface Name for references in Service Component: "
+                            + referenceName + "=" + interfaceName);
+                }
+                char c = interfaceName.charAt(interfaceName.length() - 1);
+                if ("?+*~".indexOf(c) >= 0) {
+                    if (c == '?' || c == '*' || c == '~')
+                        optional.add(referenceName);
+                    if (c == '+' || c == '*')
+                        multiple.add(referenceName);
+                    if (c == '+' || c == '*' || c == '?')
+                        dynamic.add(referenceName);
+                    interfaceName = interfaceName.substring(0, interfaceName
+                            .length() - 1);
+                }
+
+                if (referenceName.endsWith(":")) {
+                    if (!SET_COMPONENT_DIRECTIVES.contains(referenceName))
+                        error("Unrecognized directive in Service-Component header: "
+                                + referenceName);
+                    continue;
+                }
+
+                Matcher m = REFERENCE.matcher(interfaceName);
+                if (m.matches()) {
+                    interfaceName = m.group(1);
+                    target = m.group(2);
+                }
+
+                if (!analyzer.checkClass(interfaceName))
+                    error("Component definition refers to a class that is neither imported nor contained: "
+                            + interfaceName);
+
+                pw.print("  <reference name='" + referenceName
+                        + "' interface='" + interfaceName + "'");
+
+                String cardinality = optional.contains(referenceName) ? "0"
+                        : "1";
+                cardinality += "..";
+                cardinality += multiple.contains(referenceName) ? "n" : "1";
+                if (!cardinality.equals("1..1"))
+                    pw.print(" cardinality='" + cardinality + "'");
+
+                if (Character.isLowerCase(referenceName.charAt(0))) {
+                    String z = referenceName.substring(0, 1).toUpperCase()
+                            + referenceName.substring(1);
+                    pw.print(" bind='set" + z + "'");
+                    pw.print(" unbind='unset" + z + "'");
+                    // TODO Verify that the methods exist
+                }
+
+                if (dynamic.contains(referenceName)) {
+                    pw.print(" policy='dynamic'");
+                }
+
+                if (target != null) {
+                    Filter filter = new Filter(target);
+                    if (filter.verify() == null)
+                        pw.print(" target='" + filter.toString() + "'");
+                    else
+                        error("Target for " + referenceName
+                                + " is not a correct filter: " + target + " "
+                                + filter.verify());
+                }
+                pw.println("/>");
+            }
+        }
+    }
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/ServiceComponent.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/AnalyzerPlugin.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/AnalyzerPlugin.java?rev=793527&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/AnalyzerPlugin.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/AnalyzerPlugin.java Mon Jul 13 10:06:47 2009
@@ -0,0 +1,20 @@
+package aQute.bnd.service;
+
+import aQute.lib.osgi.*;
+
+public interface AnalyzerPlugin {
+
+    /**
+     * This plugin is called after analysis. The plugin is free to modify the
+     * jar and/or change the classpath information (see referred, contained).
+     * This plugin is called after analysis of the JAR but before manifest
+     * generation.
+     * 
+     * @param analyzer
+     * @return true if the classpace has been modified so that the bundle
+     *         classpath must be reanalyzed
+     * @throws Exception
+     */
+
+    boolean analyzeJar(Analyzer analyzer) throws Exception;
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/AnalyzerPlugin.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/MakePlugin.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/MakePlugin.java?rev=793527&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/MakePlugin.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/MakePlugin.java Mon Jul 13 10:06:47 2009
@@ -0,0 +1,21 @@
+package aQute.bnd.service;
+
+import java.util.*;
+
+import aQute.lib.osgi.*;
+
+public interface MakePlugin {
+
+    /**
+     * This plugin is called when Include-Resource detects a reference to a resource
+     * that it can not find in the file system.
+     * 
+     * @param builder   The current builder
+     * @param source    The source string (i.e. the place where bnd looked)
+     * @param arguments Any arguments on the clause in Include-Resource
+     * @return          A resource or null if no resource could be made
+     * @throws Exception
+     */
+    Resource make(Builder builder, String source, Map<String,String> arguments) throws Exception;
+
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/MakePlugin.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/Plugin.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/Plugin.java?rev=793527&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/Plugin.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/Plugin.java Mon Jul 13 10:06:47 2009
@@ -0,0 +1,31 @@
+package aQute.bnd.service;
+
+import java.util.*;
+
+import aQute.libg.reporter.*;
+
+/**
+ * An optional interface for plugins. If a plugin implements this interface then
+ * it can receive the reminaing attributes and directives given in its clause as
+ * well as the reporter to use.
+ * 
+ */
+public interface Plugin {
+    /**
+     * Give the plugin the remaining properties.
+     * 
+     * When a plugin is declared, the clause can contain extra properties.
+     * All the properties and directives are given to the plugin to use.
+     * 
+     * @param map attributes and directives for this plugin's clause
+     */
+    void setProperties(Map<String,String> map);
+    
+    /**
+     * Set the current reporter. This is called at init time. This plugin
+     * should report all errors and warnings to this reporter.
+     * 
+     * @param processor
+     */
+    void setReporter(Reporter processor);
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/Plugin.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/Refreshable.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/Refreshable.java?rev=793527&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/Refreshable.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/Refreshable.java Mon Jul 13 10:06:47 2009
@@ -0,0 +1,8 @@
+package aQute.bnd.service;
+
+import java.io.*;
+
+public interface Refreshable {
+    boolean refresh();
+    File getRoot();
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/Refreshable.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/RepositoryPlugin.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/RepositoryPlugin.java?rev=793527&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/RepositoryPlugin.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/RepositoryPlugin.java Mon Jul 13 10:06:47 2009
@@ -0,0 +1,59 @@
+package aQute.bnd.service;
+
+import java.io.*;
+import java.util.*;
+
+import aQute.lib.osgi.*;
+import aQute.libg.version.*;
+
+public interface RepositoryPlugin {
+    /**
+     * Return a URL to a matching version of the given bundle.
+     * 
+     * @param bsn
+     *            Bundle-SymbolicName of the searched bundle
+     * @param range
+     *            Version range for this bundle,"latest" if you only want the
+     *            latest, or null when you want all.
+     * @return A list of URLs sorted on version, lowest version is at index 0.
+     *         null is returned when no files with the given bsn ould be found.
+     * @throws Exception
+     *             when anything goes wrong
+     */
+    File[] get(String bsn, String range) throws Exception;
+    
+    /**
+     * Answer if this repository can be used to store files.
+     * 
+     * @return true if writable
+     */
+    boolean canWrite();
+    
+    /**
+     * Put a JAR file in the repository.
+     * 
+     * @param jar
+     * @throws Exception
+     */
+    File  put(Jar jar) throws Exception;
+    
+    /**
+     * Return a list of bsns that are present in the repository.
+     * 
+     * @param regex if not null, match against the bsn and if matches, return otherwise skip
+     * @return A list of bsns that match the regex parameter or all if regex is null
+     */
+    List<String> list(String regex);
+    
+    /**
+     * Return a list of versions.
+     */
+    
+    List<Version> versions(String bsn);
+    
+    /**
+     * @return The name of the repository
+     */
+    String getName();
+
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/RepositoryPlugin.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/SignerPlugin.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/SignerPlugin.java?rev=793527&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/SignerPlugin.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/SignerPlugin.java Mon Jul 13 10:06:47 2009
@@ -0,0 +1,15 @@
+package aQute.bnd.service;
+
+import aQute.lib.osgi.*;
+
+public interface SignerPlugin {
+    /**
+     * Sign the current jar. The alias is the given certificate 
+     * keystore.
+     * 
+     * @param builder   The current builder that contains the jar to sign
+     * @param alias     The keystore certificate alias
+     * @throws Exception When anything goes wrong
+     */
+    void sign(Builder builder, String alias) throws Exception;
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/SignerPlugin.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/action/Action.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/action/Action.java?rev=793527&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/action/Action.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/action/Action.java Mon Jul 13 10:06:47 2009
@@ -0,0 +1,25 @@
+package aQute.bnd.service.action;
+
+import aQute.bnd.build.*;
+
+public interface Action {
+    /**
+     * A String[] that specifies a menu entry. The entry
+     * can be hierarchical by separating the parts with a ':'.
+     * 
+     * <pre>
+     *  A:B:C
+     *  A:B:D
+     *  
+     *      A
+     *      |
+     *      B
+     *     / \
+     *    C   D
+     *    
+     * </pre>
+     */
+    String ACTION_MENU  = "bnd.action.menu";
+
+    void execute( Project project, String action) throws Exception;
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/action/Action.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/test/ProjectLauncher.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/test/ProjectLauncher.java?rev=793527&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/test/ProjectLauncher.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/test/ProjectLauncher.java Mon Jul 13 10:06:47 2009
@@ -0,0 +1,336 @@
+package aQute.bnd.test;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.regex.*;
+
+import aQute.bnd.build.*;
+import aQute.lib.osgi.*;
+import aQute.libg.header.*;
+
+public class ProjectLauncher extends Processor {
+    final Project project;
+    static File   runtime;
+    String        report;
+
+    public ProjectLauncher(Project project) {
+        super(project);
+        this.project = project;
+    }
+
+    /**
+     * Calculate the classpath. We include our own runtime.jar which includes
+     * the test framework and we include the first of the test frameworks
+     * specified.
+     */
+    public String[] getClasspath() {
+        try {
+            List<String> classpath = new ArrayList<String>();
+            classpath.add(getRuntime().getAbsolutePath());
+
+            for (Container c : project.getRunpath()) {
+                if (c.getType() != Container.TYPE.ERROR) {
+                    if (!c.getFile().getName().startsWith("ee."))
+                        classpath.add(c.getFile().getAbsolutePath());
+                } else {
+                    error("Invalid entry on the " + Constants.RUNPATH + ": "
+                            + c);
+                }
+            }
+            return classpath.toArray(new String[classpath.size()]);
+        } catch (Exception e) {
+            error("Calculating class path", e);
+        }
+        return null;
+    }
+
+    /**
+     * Extract the runtime on the file system so we can refer to it. in the
+     * remote VM.
+     * 
+     * @return
+     */
+    public static File getRuntime() {
+        if (runtime == null) {
+            try {
+                URL url = ProjectLauncher.class
+                        .getResource("aQute.runtime.jar");
+                if (url == null)
+                    throw new IllegalStateException(
+                            "Can not find my runtime.jar");
+
+                runtime = File.createTempFile("aQute.runtime", ".jar");
+                // runtime.deleteOnExit();
+                FileOutputStream out = new FileOutputStream(runtime);
+                InputStream in = url.openStream();
+                copy(in, out);
+                out.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+        return runtime;
+    }
+
+    public void doRunbundles(List<String> programArguments) throws Exception {
+        // we are going to use this for running, so we want to
+        // use the "best" version. For compile, we should
+        // use the lowest version.
+        Collection<Container> testbundles = project.getRunbundles();
+
+        for (Container c : testbundles) {
+            if (c.getError() != null)
+                error("Invalid bundle on " + Constants.RUNBUNDLES + " "
+                        + c.getError());
+            else {
+                // Do we need to build any sub projects?
+                if (c.getVersion() != null && c.getVersion().equals("project")) {
+                    Project sub = c.getProject();
+                    sub.clear();
+                    File[] outputs = sub.build(false);
+                    for (File f : outputs) {
+                        programArguments.add("-bundle");
+                        programArguments.add(f.getAbsolutePath());
+                    }
+                    getInfo(sub);
+                } else {
+                    programArguments.add("-bundle");
+                    programArguments.add(c.getFile().getAbsolutePath());
+                }
+            }
+        }
+    }
+
+    private void doRunpath(List<String> programArguments) throws Exception {
+        Collection<Container> testpath = project.getRunpath();
+        Container found = null;
+        for (Container c : testpath) {
+            if (c.getAttributes().containsKey("framework")) {
+                if (found != null) {
+                    warning("Specifying multiple framework classes on the "
+                            + Constants.RUNPATH + "\n" + "Previous found: "
+                            + found.getProject() + " " + found.getAttributes()
+                            + "\n" + "Now found     : " + c.getProject() + " "
+                            + c.getAttributes());
+                }
+                programArguments.add("-framework");
+                programArguments.add(c.getAttributes().get("framework"));
+                found = c;
+            }
+            if (c.getAttributes().containsKey("factory")) {
+                if (found != null) {
+                    warning("Specifying multiple framework factories on the "
+                            + Constants.RUNPATH + "\n" + "Previous found: "
+                            + found.getProject() + " " + found.getAttributes()
+                            + "\n" + "Now found     : " + c.getProject() + " "
+                            + c.getAttributes());
+                }
+                programArguments.add("-framework");
+                programArguments.add(c.getAttributes().get("factory"));
+                found = c;
+            }
+            String exports = c.getAttributes().get("export");
+            if (exports != null) {
+                String parts[] = exports.split("\\s*,\\s*");
+                for (String p : parts) {
+                    programArguments.add("-export");
+                    programArguments.add(p);
+                }
+            }
+        }
+
+        doSystemPackages(programArguments);
+        doRunProperties(programArguments);
+    }
+
+    private void doRunProperties(List<String> programArguments) {
+        Map<String, String> properties = OSGiHeader
+                .parseProperties(getProperty(RUNPROPERTIES));
+        for (Map.Entry<String, String> entry : properties.entrySet()) {
+            programArguments.add("-set");
+            programArguments.add(entry.getKey());
+            programArguments.add(entry.getValue());
+        }
+    }
+
+    private void doSystemPackages(List<String> programArguments) {
+        Map<String, Map<String, String>> systemPackages = parseHeader(getProperty(RUNSYSTEMPACKAGES));
+        for (Map.Entry<String, Map<String, String>> entry : systemPackages
+                .entrySet()) {
+            programArguments.add("-export");
+            StringBuffer sb = new StringBuffer();
+            sb.append(entry.getKey());
+            printClause(entry.getValue(), null, sb);
+            programArguments.add(sb.toString());
+        }
+    }
+
+    private void doStorage(List<String> programArguments) throws Exception {
+        File tmp = new File(project.getTarget(), "fwtmp");
+        tmp.mkdirs();
+        tmp.deleteOnExit();
+
+        programArguments.add("-storage");
+        programArguments.add(tmp.getAbsolutePath());
+    }
+
+    /**
+     * Utility to copy a file from a resource.
+     * 
+     * @param in
+     * @param out
+     * @throws IOException
+     */
+    private static void copy(InputStream in, FileOutputStream out)
+            throws IOException {
+        byte buf[] = new byte[8192];
+        int size = in.read(buf);
+        while (size > 0) {
+            out.write(buf, 0, size);
+            size = in.read(buf);
+        }
+        in.close();
+    }
+
+    private Process launch(File[] targets) throws Exception {
+        List<String> arguments = newList();
+        List<String> vmArguments = newList();
+
+        vmArguments.add(getProperty("java", "java"));
+        doClasspath(vmArguments);
+
+        getArguments(targets, vmArguments, arguments);
+
+        vmArguments.add("aQute.junit.runtime.Target");
+        arguments.add("-report");
+        arguments.add(getTestreport());
+
+        List<String> all = newList();
+        all.addAll(vmArguments);
+        all.addAll(arguments);
+
+        System.out.println("Cmd: " + all);
+
+        String[] cmdarray = all.toArray(new String[all.size()]);
+        if (getErrors().size() > 0)
+            return null;
+
+        return Runtime.getRuntime().exec(cmdarray, null, project.getBase());
+    }
+/*
+    static Pattern ARGUMENT = Pattern.compile("[-a-zA-Z0-9\\._]+");
+    private void doVMArguments(List<String> arguments) {
+        Map<String,String> map = OSGiHeader.parseProperties( getProperty(RUNVM));
+        for ( String key : map.keySet() ) {
+            if ( ARGUMENT.matcher(key).matches())
+                if ( key.startsWith("-"))
+                    arguments.add(key);
+                else
+                    arguments.add("-D" + key.trim() + "=" + map.get(key));
+            else
+                warning("VM Argument is not a proper property key: " + key );
+        }
+    }
+*/
+    private void doVMArguments(List<String> arguments) {
+        Map<String, String> map = OSGiHeader
+                .parseProperties(getProperty(RUNVM));
+        for (String key : map.keySet()) {
+            if (key.startsWith("-"))
+                arguments.add(key);
+            else
+                arguments.add("-D" + key.trim() + "=" + map.get(key));
+        }
+    }
+    private void doClasspath(List<String> arguments) {
+        Collection<String> cp = Arrays.asList(getClasspath());
+        if (!cp.isEmpty()) {
+            arguments.add("-classpath");
+            arguments.add(join(cp, File.pathSeparator));
+        }
+    }
+
+    public int run(File f) throws Exception {
+        final Process process = launch(new File[] { f });
+
+        Thread killer = new Thread() {
+            public void run() {
+                process.destroy();
+            }
+        };
+        Runtime.getRuntime().addShutdownHook(killer);
+        Streamer sin = new Streamer(process.getInputStream(), System.out);
+        Streamer serr = new Streamer(process.getErrorStream(), System.out);
+        try {
+            sin.start();
+            serr.start();
+            return process.waitFor();
+        } finally {
+            Runtime.getRuntime().removeShutdownHook(killer);
+            sin.join();
+            serr.join();
+        }
+    }
+
+    static class Streamer extends Thread {
+        final InputStream  in;
+        final OutputStream out;
+
+        Streamer(InputStream in, OutputStream out) {
+            this.in = in;
+            this.out = out;
+        }
+
+        public void run() {
+            try {
+                int c;
+                while ((c = in.read()) > 0) {
+                    this.out.write(c);
+                }
+            } catch (IOException ioe) {
+                // Ignore
+            }
+        };
+
+    };
+
+    public String getTestreport() {
+        if (report != null)
+            return report;
+        return report = getProperty(Constants.TESTREPORT,
+                "${target}/test-report.xml");
+
+    }
+
+    public void getArguments(List<String> vmArguments,
+            List<String> programArguments, boolean undertest) throws Exception {
+        File files[] = project.build(undertest);
+        getInfo(project);
+        if (files == null)
+            return;
+
+        getArguments(files, vmArguments, programArguments);
+    }
+
+    public void getArguments(File files[], List<String> vmArguments,
+            List<String> programArguments) throws Exception {
+        doVMArguments(vmArguments);
+        doStorage(programArguments);
+        doRunpath(programArguments);
+        doRunbundles(programArguments);
+        for (File file : files) {
+            programArguments.add("-target");
+            programArguments.add(file.getAbsolutePath());
+        }
+    }
+
+    public String getReport() {
+        return report;
+    }
+
+    public void setReport(String report) {
+        this.report = report;
+    }
+
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/test/ProjectLauncher.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/filter/Filter.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/filter/Filter.java?rev=793527&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/filter/Filter.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/filter/Filter.java Mon Jul 13 10:06:47 2009
@@ -0,0 +1,353 @@
+/**
+ * Copyright (c) 2000 Gatespace AB. All Rights Reserved.
+ *
+ * Gatespace grants Open Services Gateway Initiative (OSGi) an irrevocable,
+ * perpetual, non-exclusive, worldwide, paid-up right and license to
+ * reproduce, display, perform, prepare and have prepared derivative works
+ * based upon and distribute and sublicense this material and derivative
+ * works thereof as set out in the OSGi MEMBER AGREEMENT as of January 24
+ * 2000, for use in accordance with Section 2.2 of the BY-LAWS of the
+ * OSGi MEMBER AGREEMENT.
+ */
+
+package aQute.lib.filter;
+
+import java.lang.reflect.*;
+import java.math.*;
+import java.util.*;
+
+public class Filter {
+    final char     WILDCARD = 65535;
+
+    final int      EQ       = 0;
+    final int      LE       = 1;
+    final int      GE       = 2;
+    final int      APPROX   = 3;
+
+    private String filter;
+
+    abstract class Query {
+        static final String GARBAGE   = "Trailing garbage";
+        static final String MALFORMED = "Malformed query";
+        static final String EMPTY     = "Empty list";
+        static final String SUBEXPR   = "No subexpression";
+        static final String OPERATOR  = "Undefined operator";
+        static final String TRUNCATED = "Truncated expression";
+        static final String EQUALITY  = "Only equality supported";
+
+        private String      tail;
+
+        boolean match() throws IllegalArgumentException {
+            tail = filter;
+            boolean val = doQuery();
+            if (tail.length() > 0)
+                error(GARBAGE);
+            return val;
+        }
+
+        private boolean doQuery() throws IllegalArgumentException {
+            if (tail.length() < 3 || !prefix("("))
+                error(MALFORMED);
+            boolean val;
+
+            switch (tail.charAt(0)) {
+            case '&':
+                val = doAnd();
+                break;
+            case '|':
+                val = doOr();
+                break;
+            case '!':
+                val = doNot();
+                break;
+            default:
+                val = doSimple();
+                break;
+            }
+
+            if (!prefix(")"))
+                error(MALFORMED);
+            return val;
+        }
+
+        private boolean doAnd() throws IllegalArgumentException {
+            tail = tail.substring(1);
+            boolean val = true;
+            if (!tail.startsWith("("))
+                error(EMPTY);
+            do {
+                if (!doQuery())
+                    val = false;
+            } while (tail.startsWith("("));
+            return val;
+        }
+
+        private boolean doOr() throws IllegalArgumentException {
+            tail = tail.substring(1);
+            boolean val = false;
+            if (!tail.startsWith("("))
+                error(EMPTY);
+            do {
+                if (doQuery())
+                    val = true;
+            } while (tail.startsWith("("));
+            return val;
+        }
+
+        private boolean doNot() throws IllegalArgumentException {
+            tail = tail.substring(1);
+            if (!tail.startsWith("("))
+                error(SUBEXPR);
+            return !doQuery();
+        }
+
+        private boolean doSimple() throws IllegalArgumentException {
+            int op = 0;
+            Object attr = getAttr();
+
+            if (prefix("="))
+                op = EQ;
+            else if (prefix("<="))
+                op = LE;
+            else if (prefix(">="))
+                op = GE;
+            else if (prefix("~="))
+                op = APPROX;
+            else
+                error(OPERATOR);
+
+            return compare(attr, op, getValue());
+        }
+
+        private boolean prefix(String pre) {
+            if (!tail.startsWith(pre))
+                return false;
+            tail = tail.substring(pre.length());
+            return true;
+        }
+
+        private Object getAttr() {
+            int len = tail.length();
+            int ix = 0;
+            label: for (; ix < len; ix++) {
+                switch (tail.charAt(ix)) {
+                case '(':
+                case ')':
+                case '<':
+                case '>':
+                case '=':
+                case '~':
+                case '*':
+                case '\\':
+                    break label;
+                }
+            }
+            String attr = tail.substring(0, ix).toLowerCase();
+            tail = tail.substring(ix);
+            return getProp(attr);
+        }
+
+        abstract Object getProp(String key);
+
+        private String getValue() {
+            StringBuffer sb = new StringBuffer();
+            int len = tail.length();
+            int ix = 0;
+            label: for (; ix < len; ix++) {
+                char c = tail.charAt(ix);
+                switch (c) {
+                case '(':
+                case ')':
+                    break label;
+                case '*':
+                    sb.append(WILDCARD);
+                    break;
+                case '\\':
+                    if (ix == len - 1)
+                        break label;
+                    sb.append(tail.charAt(++ix));
+                    break;
+                default:
+                    sb.append(c);
+                    break;
+                }
+            }
+            tail = tail.substring(ix);
+            return sb.toString();
+        }
+
+        private void error(String m) throws IllegalArgumentException {
+            throw new IllegalArgumentException(m + " " + tail);
+        }
+
+        private boolean compare(Object obj, int op, String s) {
+            if (obj == null)
+                return false;
+            try {
+                Class<?> numClass = obj.getClass();
+                if (numClass == String.class) {
+                    return compareString((String) obj, op, s);
+                } else if (numClass == Character.class) {
+                    return compareString(obj.toString(), op, s);
+                } else if (numClass == Long.class) {
+                    return compareSign(op, Long.valueOf(s)
+                            .compareTo((Long) obj));
+                } else if (numClass == Integer.class) {
+                    return compareSign(op, Integer.valueOf(s).compareTo(
+                            (Integer) obj));
+                } else if (numClass == Short.class) {
+                    return compareSign(op, Short.valueOf(s).compareTo(
+                            (Short) obj));
+                } else if (numClass == Byte.class) {
+                    return compareSign(op, Byte.valueOf(s)
+                            .compareTo((Byte) obj));
+                } else if (numClass == Double.class) {
+                    return compareSign(op, Double.valueOf(s).compareTo(
+                            (Double) obj));
+                } else if (numClass == Float.class) {
+                    return compareSign(op, Float.valueOf(s).compareTo(
+                            (Float) obj));
+                } else if (numClass == Boolean.class) {
+                    if (op != EQ)
+                        return false;
+                    int a = Boolean.valueOf(s).booleanValue() ? 1 : 0;
+                    int b = ((Boolean) obj).booleanValue() ? 1 : 0;
+                    return compareSign(op, a - b);
+                } else if (numClass == BigInteger.class) {
+                    return compareSign(op, new BigInteger(s)
+                            .compareTo((BigInteger) obj));
+                } else if (numClass == BigDecimal.class) {
+                    return compareSign(op, new BigDecimal(s)
+                            .compareTo((BigDecimal) obj));
+                } else if (obj instanceof Collection) {
+                    for (Object x : (Collection<?>) obj)
+                        if (compare(x, op, s))
+                            return true;
+                } else if (numClass.isArray()) {
+                    int len = Array.getLength(obj);
+                    for (int i = 0; i < len; i++)
+                        if (compare(Array.get(obj, i), op, s))
+                            return true;
+                }
+            } catch (Exception e) {
+            }
+            return false;
+        }
+    }
+
+    class DictQuery extends Query {
+        private Dictionary<?,?> dict;
+
+        DictQuery(Dictionary<?,?> dict) {
+            this.dict = dict;
+        }
+
+        Object getProp(String key) {
+            return dict.get(key);
+        }
+    }
+
+    public Filter(String filter) throws IllegalArgumentException {
+        // NYI: Normalize the filter string?
+        this.filter = filter;
+        if (filter == null || filter.length() == 0)
+            throw new IllegalArgumentException("Null query");
+    }
+
+    public boolean match(Dictionary<?,?> dict) {
+        try {
+            return new DictQuery(dict).match();
+        } catch (IllegalArgumentException e) {
+            return false;
+        }
+    }
+
+    public String verify() {
+        try {
+            new DictQuery(new Hashtable<Object,Object>()).match();
+        } catch (IllegalArgumentException e) {
+            return e.getMessage();
+        }
+        return null;
+    }
+
+    public String toString() {
+        return filter;
+    }
+
+    public boolean equals(Object obj) {
+        return obj != null && obj instanceof Filter
+                && filter.equals(((Filter) obj).filter);
+    }
+
+    public int hashCode() {
+        return filter.hashCode();
+    }
+
+    boolean compareString(String s1, int op, String s2) {
+        switch (op) {
+        case EQ:
+            return patSubstr(s1, s2);
+        case APPROX:
+            return fixupString(s2).equals(fixupString(s1));
+        default:
+            return compareSign(op, s2.compareTo(s1));
+        }
+    }
+
+    boolean compareSign(int op, int cmp) {
+        switch (op) {
+        case LE:
+            return cmp >= 0;
+        case GE:
+            return cmp <= 0;
+        case EQ:
+            return cmp == 0;
+        default: /* APPROX */
+            return cmp == 0;
+        }
+    }
+
+    String fixupString(String s) {
+        StringBuffer sb = new StringBuffer();
+        int len = s.length();
+        boolean isStart = true;
+        boolean isWhite = false;
+        for (int i = 0; i < len; i++) {
+            char c = s.charAt(i);
+            if (Character.isWhitespace(c)) {
+                isWhite = true;
+            } else {
+                if (!isStart && isWhite)
+                    sb.append(' ');
+                if (Character.isUpperCase(c))
+                    c = Character.toLowerCase(c);
+                sb.append(c);
+                isStart = false;
+                isWhite = false;
+            }
+        }
+        return sb.toString();
+    }
+
+    boolean patSubstr(String s, String pat) {
+        if (s == null)
+            return false;
+        if (pat.length() == 0)
+            return s.length() == 0;
+        if (pat.charAt(0) == WILDCARD) {
+            pat = pat.substring(1);
+            for (;;) {
+                if (patSubstr(s, pat))
+                    return true;
+                if (s.length() == 0)
+                    return false;
+                s = s.substring(1);
+            }
+        } else {
+            if (s.length() == 0 || s.charAt(0) != pat.charAt(0))
+                return false;
+            return patSubstr(s.substring(1), pat.substring(1));
+        }
+    }
+}

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/About.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/About.java?rev=793527&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/About.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/About.java Mon Jul 13 10:06:47 2009
@@ -0,0 +1,48 @@
+/* Copyright 2006 aQute SARL 
+ * Licensed under the Apache License, Version 2.0, see http://www.apache.org/licenses/LICENSE-2.0 */
+package aQute.lib.osgi;
+
+/**
+ * This package contains a number of classes that assists by analyzing JARs and
+ * constructing bundles.
+ * 
+ * The Analyzer class can be used to analyze an existing bundle and can create a
+ * manifest specification from proposed (wildcard) Export-Package,
+ * Bundle-Includes, and Import-Package headers.
+ * 
+ * The Builder class can use the headers to construct a JAR from the classpath.
+ * 
+ * The Verifier class can take an existing JAR and verify that all headers are
+ * correctly set. It will verify the syntax of the headers, match it against the
+ * proper contents, and verify imports and exports.
+ * 
+ * A number of utility classes are available.
+ * 
+ * Jar, provides an abstraction of a Jar file. It has constructors for creating
+ * a Jar from a stream, a directory, or a jar file. A Jar, keeps a collection
+ * Resource's. There are Resource implementations for File, from ZipFile, or from
+ * a stream (which copies the data). The Jar tries to minimize the work during
+ * build up so that it is cheap to use. The Resource's can be used to iterate 
+ * over the names and later read the resources when needed.
+ * 
+ * Clazz, provides a parser for the class files. This will be used to define the
+ * imports and exports.
+ * 
+ * A key component in this library is the Map. Headers are translated to Maps of Maps. OSGi
+ * header syntax is like:
+ * <pre>
+ * 	  header = clause ( ',' clause ) *
+ *    clause = file ( ';' file ) * ( parameter ) *
+ *    param  = attr '=' value | directive ':=' value
+ * </pre>
+ * These headers are translated to a Map that contains all headers (the order is
+ * maintained). Each additional file in a header definition will have its own
+ * entry (only native code does not work this way). The clause is represented
+ * as another map. The ':' of directives is considered part of the name. This
+ * allows attributes and directives to be maintained in the clause map. 
+ * 
+ * @version $Revision: 1.1 $
+ */
+public class About {
+
+}

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/AbstractResource.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/AbstractResource.java?rev=793527&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/AbstractResource.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/AbstractResource.java Mon Jul 13 10:06:47 2009
@@ -0,0 +1,50 @@
+package aQute.lib.osgi;
+
+import java.io.*;
+
+public abstract class AbstractResource implements Resource {
+    String extra;
+    byte[] calculated;
+    long   lastModified;
+
+    protected AbstractResource(long modified) {
+        lastModified = modified;
+    }
+
+    public String getExtra() {
+        return extra;
+    }
+
+    public long lastModified() {
+        return lastModified;
+    }
+
+    public InputStream openInputStream() throws IOException {
+        return new ByteArrayInputStream(getLocalBytes());
+    }
+
+    private byte[] getLocalBytes() throws IOException {
+        try {
+            if (calculated != null)
+                return calculated;
+
+            return calculated = getBytes();
+        } catch (IOException e) {
+            throw e;
+        } catch (Exception e) {
+            IOException ee = new IOException("Opening resource");
+            ee.initCause(e);
+            throw ee;
+        }
+    }
+
+    public void setExtra(String extra) {
+        this.extra = extra;
+    }
+
+    public void write(OutputStream out) throws IOException {
+        out.write(getLocalBytes());
+    }
+
+    abstract protected byte[] getBytes() throws Exception;
+}

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