You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by ca...@apache.org on 2007/02/08 01:54:28 UTC

svn commit: r504757 - in /maven/sandbox/plugins/maven-bundle-plugin/src/main/java/aQute/lib/osgi: Analyzer.java Builder.java

Author: carlos
Date: Wed Feb  7 16:54:27 2007
New Revision: 504757

URL: http://svn.apache.org/viewvc?view=rev&rev=504757
Log:
Add patched sources of Analyzer and Builder until patched in BND

Added:
    maven/sandbox/plugins/maven-bundle-plugin/src/main/java/aQute/lib/osgi/Analyzer.java   (with props)
    maven/sandbox/plugins/maven-bundle-plugin/src/main/java/aQute/lib/osgi/Builder.java   (with props)

Added: maven/sandbox/plugins/maven-bundle-plugin/src/main/java/aQute/lib/osgi/Analyzer.java
URL: http://svn.apache.org/viewvc/maven/sandbox/plugins/maven-bundle-plugin/src/main/java/aQute/lib/osgi/Analyzer.java?view=auto&rev=504757
==============================================================================
--- maven/sandbox/plugins/maven-bundle-plugin/src/main/java/aQute/lib/osgi/Analyzer.java (added)
+++ maven/sandbox/plugins/maven-bundle-plugin/src/main/java/aQute/lib/osgi/Analyzer.java Wed Feb  7 16:54:27 2007
@@ -0,0 +1,1107 @@
+/* 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 class can calculate the required headers for a (potential) JAR file. It
+ * analyzes a directory or JAR for the packages that are contained and that are
+ * referred to by the bytecodes. The user can the use regular expressions to
+ * define the attributes and directives. The matching is not fully regex for
+ * convenience. A * and ? get a . prefixed and dots are escaped.
+ * 
+ * <pre>
+ *           			*;auto=true				any		
+ *           			org.acme.*;auto=true    org.acme.xyz
+ *           			org.[abc]*;auto=true    org.acme.xyz
+ * </pre>
+ * 
+ * Additional, the package instruction can start with a '=' or a '!'. The '!'
+ * indicates negation. Any matching package is removed. The '=' is literal, the
+ * expression will be copied verbatim and no matching will take place.
+ * 
+ * Any headers in the given properties are used in the output properties.
+ */
+import java.io.*;
+import java.net.URL;
+import java.util.*;
+import java.util.jar.*;
+import java.util.regex.*;
+
+public class Analyzer extends Processor {
+  public final static String BUNDLE_CLASSPATH                    = "Bundle-ClassPath";
+  public final static String BUNDLE_COPYRIGHT                    = "Bundle-Copyright";
+  public final static String BUNDLE_DESCRIPTION                  = "Bundle-Description";
+  public final static String BUNDLE_NAME                         = "Bundle-Name";
+  public final static String BUNDLE_NATIVECODE                   = "Bundle-NativeCode";
+  public final static String EXPORT_PACKAGE                      = "Export-Package";
+  public final static String EXPORT_SERVICE                      = "Export-Service";
+  public final static String IMPORT_PACKAGE                      = "Import-Package";
+  public final static String DYNAMICIMPORT_PACKAGE               = "DynamicImport-Package";
+  public final static String IMPORT_SERVICE                      = "Import-Service";
+  public final static String BUNDLE_VENDOR                       = "Bundle-Vendor";
+  public final static String BUNDLE_VERSION                      = "Bundle-Version";
+  public final static String BUNDLE_DOCURL                       = "Bundle-DocURL";
+  public final static String BUNDLE_CONTACTADDRESS               = "Bundle-ContactAddress";
+  public final static String BUNDLE_ACTIVATOR                    = "Bundle-Activator";
+  public final static String BUNDLE_REQUIREDEXECUTIONENVIRONMENT = "Bundle-RequiredExecutionEnvironment";
+  public final static String BUNDLE_SYMBOLICNAME                 = "Bundle-SymbolicName";
+  public final static String BUNDLE_LOCALIZATION                 = "Bundle-Localization";
+  public final static String REQUIRE_BUNDLE                      = "Require-Bundle";
+  public final static String FRAGMENT_HOST                       = "Fragment-Host";
+  public final static String BUNDLE_MANIFESTVERSION              = "Bundle-ManifestVersion";
+  public final static String SERVICE_COMPONENT                   = "Service-Component";
+  public final static String BUNDLE_LICENSE                      = "Bundle-License";
+  public static final String PRIVATE_PACKAGE                     = "Private-Package";
+  public static final String IGNORE_PACKAGE                      = "Ignore-Package";
+  public static final String INCLUDE_RESOURCE                    = "Include-Resource";
+
+  public final static String headers[]                           = {
+      BUNDLE_ACTIVATOR, BUNDLE_CONTACTADDRESS,
+      BUNDLE_COPYRIGHT, BUNDLE_DOCURL, BUNDLE_LOCALIZATION,
+      BUNDLE_NATIVECODE, BUNDLE_VENDOR, BUNDLE_VERSION,
+      BUNDLE_LICENSE                                            };
+
+  static final Pattern VALID_PROPERTY_TYPES = Pattern
+  .compile("(String|Long|Double|Float|Integer|Byte|Character|Boolean|Short)");
+  
+ private Properties         properties                         /* String->String */= new Properties();
+  File                       base                                = new File(
+                                                                     "")
+                                                                     .getAbsoluteFile();
+  Map                        contained                          /* String->Map */= new HashMap();      // package
+  Map                        referred                           /* String->Map */= new HashMap();      // package
+  Map                        uses                               /* String->Map */= new HashMap();      // package
+  Map                        classspace;
+  boolean                    analyzed;
+  Map                        exports;
+  Map                        imports;
+  Map                        bundleClasspath;                                                            // Bundle
+  Map                        ignored                            /* String -> Map */= new HashMap();    // Ignored
+  // packages
+  Jar                        dot;                                                                        // The
+  Map                        cpExports                           = new HashMap();
+  String                     activator;
+  List                       classpath                           = new ArrayList();
+  /** {@link List}$lt;{@link Jar}> with jars that may be expanded, if null all jars will be expanded */
+  List                       classpathForExpansion               = null;
+  static Pattern             doNotCopy                           = Pattern
+                                                                     .compile("CVS|.svn");
+  static String              version;
+  Macro                      replacer                            = new Macro(
+                                                                     this);
+
+  /**
+   * Get the version from the manifest, a lot of work!
+   * 
+   * @return version or unknown.
+   */
+  public String getVersion() {
+    if (version == null) {
+      version = "unknown";
+      try {
+        Enumeration e = getClass().getClassLoader()
+            .getResources("META-INF/MANIFEST.MF");
+        while (e.hasMoreElements()) {
+          URL url = (URL) e.nextElement();
+          InputStream in = url.openStream();
+
+          Manifest manifest = new Manifest(in);
+          in.close();
+          String bsn = manifest.getMainAttributes()
+              .getValue(BUNDLE_SYMBOLICNAME);
+          if (bsn != null
+              && bsn.indexOf("biz.aQute.bnd") >= 0) {
+            version = manifest.getMainAttributes()
+                .getValue(BUNDLE_VERSION);
+            return version;
+          }
+        }
+      } catch (IOException e) {
+        // Well, too bad
+        warning("bnd jar file is corrupted, can not find manifest "
+            + e);
+      }
+    }
+    return version;
+  }
+
+  public Jar setJar(Jar jar) {
+    this.dot = jar;
+    return jar;
+  }
+
+  public Jar setJar(File jar) throws IOException {
+    return setJar(buildJar(jar));
+  }
+
+  public void setClasspath(File[] classpath)
+      throws IOException {
+    List list = new ArrayList();
+    for (int i = 0; i < classpath.length; i++) {
+      Jar current = buildJar(classpath[i]);
+      if (current != null) list.add(current);
+    }
+    Jar[] cp = (Jar[]) list.toArray(new Jar[list.size()]);
+    setClasspath(cp);
+  }
+
+  public void setProperties(File propertiesFile)
+      throws FileNotFoundException, IOException {
+    this.base = propertiesFile.getAbsoluteFile()
+        .getParentFile();
+    Properties local = getProperties(propertiesFile,
+        new HashSet());
+    local.put("project.file", propertiesFile
+        .getAbsolutePath());
+    local.put("project.name", propertiesFile.getName());
+    local.put("project", stem(propertiesFile.getName()));
+
+    if (!local.containsKey(IMPORT_PACKAGE)) local.put(
+        IMPORT_PACKAGE, "*");
+    setProperties(local);
+  }
+
+  public void setProperty(String key, String value) {
+    checkheader: for (int i = 0; i < headers.length; i++) {
+      if (headers[i].equalsIgnoreCase(value)) {
+        value = headers[i];
+        break checkheader;
+      }
+    }
+    properties.put(key, value);
+  }
+
+  public void setBase(File file) {
+    base = file;
+    properties.put("project.dir", base.getAbsolutePath());
+  }
+
+  private String stem(String name) {
+    int n = name.lastIndexOf('.');
+    if (n > 0) return name.substring(0, n);
+    else return name;
+  }
+
+  Properties getProperties(File file, Set done)
+      throws IOException {
+    Properties p = new Properties();
+    InputStream in = new FileInputStream(file);
+    p.load(in);
+    in.close();
+    String includes = p.getProperty("-include");
+    if (includes != null) {
+      String parts[] = includes.split("\\s*,\\s*");
+      for (int i = 0; i < parts.length; i++) {
+        File next = new File(file.getParentFile(), parts[i]);
+        if (next.exists() && !next.isDirectory()
+            && !done.contains(next)) {
+          done.add(next);
+          Properties level = getProperties(next, done);
+          level.putAll(p);
+          p = level;
+        }
+      }
+    }
+    return p;
+  }
+
+  public void setClasspath(Jar[] classpath) {
+    this.classpath.addAll(Arrays.asList(classpath));
+  }
+
+  /**
+   * {@link Analyzer#EXPORT_PACKAGE} will be applied only to the jars in this classpath. 
+   * If null all jars will be expanded.
+   *
+   * @param classpathForExpansion {@link List}$lt;{@link Jar}>
+   */
+  public void setClasspathForExpansion(Jar[] classpathForExpansion) {
+    if (classpathForExpansion != null) {
+      this.classpathForExpansion = Arrays.asList(classpathForExpansion);
+    }
+  }
+
+  void analyzeClasspath() throws IOException {
+    cpExports = new HashMap();
+    for (Iterator c = classpath.iterator(); c.hasNext();) {
+      Jar current = (Jar) c.next();
+      checkManifest(current);
+      for (Iterator j = current.getDirectories().keySet()
+          .iterator(); j.hasNext();) {
+        String dir = (String) j.next();
+        Resource resource = current.getResource(dir
+            + "/packageinfo");
+        if (resource != null) {
+          String version = parsePackageInfo(resource
+              .openInputStream());
+          setPackageInfo(dir, "version", version);
+        }
+      }
+    }
+  }
+
+  void setPackageInfo(String dir, String key, String value) {
+    if (value != null) {
+      String pack = dir.replace('/', '.');
+      Map map = (Map) cpExports.get(pack);
+      if (map == null) {
+        map = new HashMap();
+        cpExports.put(pack, map);
+      }
+      map.put(key, value);
+    }
+  }
+
+  private void checkManifest(Jar jar) {
+    try {
+      Manifest m = jar.getManifest();
+      if (m != null) {
+        String exportHeader = m.getMainAttributes()
+            .getValue(EXPORT_PACKAGE);
+        if (exportHeader != null) {
+          Map exported = (Map) parseHeader(exportHeader);
+          if (exported != null)
+          // Hmm, moet mischien gemerged worden?
+          cpExports.putAll(exported);
+        }
+      }
+    } catch (Exception e) {
+      warning("Erroneous Manifest for " + jar + " " + e);
+    }
+  }
+
+  public String getProperty(String headerName) {
+    String value = properties.getProperty(headerName);
+    if (value != null) return replacer.process(value);
+    else return null;
+  }
+
+  String getProperty(String headerName, String deflt) {
+    String v = getProperty(headerName);
+    return v == null ? deflt : v;
+  }
+
+  public void setProperties(Properties properties) {
+    this.properties = properties;
+    replacer = new Macro(properties, this);
+
+    String doNotCopy = getProperty("-donotcopy");
+    if (doNotCopy != null) Analyzer.doNotCopy = Pattern
+        .compile(doNotCopy);
+
+    String cp = properties.getProperty("-classpath");
+    if (cp != null) {
+      String parts[] = cp.split("\\s*,\\s*");
+      for (int i = 0; i < parts.length; i++) {
+        File file = new File(base, parts[i]);
+        if (file.exists()) try {
+          classpath.add(new Jar(file.getName(), file));
+        } catch (Exception e) {
+          error("Classpath from properties had errors: "
+              + parts[i] + " " + e);
+        }
+        else {
+          error("Trying to add non existent entry to the classpath from properties: "
+              + parts[i]);
+        }
+      }
+    }
+  }
+
+//  public Signer getSigner() {
+//    String sign = getProperty("-sign");
+//    if (sign == null) return null;
+//
+//    Map parsed = parseHeader(sign);
+//    Signer signer = new Signer();
+//    String password = (String) parsed.get("password");
+//    if (password != null) {
+//      signer.setPassword(password);
+//    }
+//
+//    String keystore = (String) parsed.get("keystore");
+//    if (keystore != null) {
+//      File f = new File(keystore);
+//      if (!f.isAbsolute()) f = new File(base, keystore);
+//      signer.setKeystore(f);
+//    } else {
+//      error("Signing requires a keystore");
+//      return null;
+//    }
+//
+//    String alias = (String) parsed.get("alias");
+//    if (alias != null) {
+//      signer.setAlias(alias);
+//    } else {
+//      error("Signing requires an alias for the private key");
+//      return null;
+//    }
+//    return signer;
+//  }
+
+  Jar buildJar(File file) throws IOException {
+    if (!file.exists()) {
+      error("File to build jar from does not exist: "
+          + file);
+      return null;
+    }
+    Jar jar = new Jar(file.getName());
+    try {
+      if (file.isDirectory()) FileResource.build(jar, file,
+          doNotCopy);
+      else if (file.exists()) {
+        ZipResource.build(jar, file);
+      } else {
+        error("No such file: " + file.getAbsolutePath());
+      }
+    } catch (Exception e) {
+      error("Failed to construct ZIP file: "
+          + file.getAbsolutePath());
+    }
+    return jar;
+
+  }
+
+  public void analyze() throws IOException {
+    if (!analyzed) {
+      analyzed = true;
+      cpExports = new HashMap();
+      analyzeClasspath();
+
+      activator = getProperty(BUNDLE_ACTIVATOR);
+      bundleClasspath = parseHeader(getProperty(BUNDLE_CLASSPATH));
+
+      classspace = analyzeBundleClasspath(dot,
+          bundleClasspath, contained, referred, uses);
+
+      referred.keySet().removeAll(contained.keySet());
+
+      Map exportInstructions = parseHeader(getProperty(EXPORT_PACKAGE));
+      Map importInstructions = parseHeader(getProperty(IMPORT_PACKAGE) );
+      Map dynamicImports = parseHeader(getProperty(DYNAMICIMPORT_PACKAGE));
+
+      if (dynamicImports != null) {
+        // Remove any dynamic imports from the referred set.
+        referred.keySet()
+            .removeAll(dynamicImports.keySet());
+      }
+
+      Set superfluous = new TreeSet();
+      // Tricky!
+      for (Iterator i = exportInstructions.keySet()
+          .iterator(); i.hasNext();) {
+        String instr = (String) i.next();
+        if (!instr.startsWith("!")) superfluous.add(instr);
+      }
+
+      exports = merge("export-package", exportInstructions,
+          contained, superfluous);
+      if (!superfluous.isEmpty()) {
+        warnings
+            .add("Superfluous export-package instructions: "
+                + superfluous);
+      }
+
+      // Add all exports that do not have an -noimport: directive
+      // to the imports.
+      Map referredAndExported = new HashMap(referred);
+      referredAndExported
+          .putAll(addExportsToImports(exports));
+
+      // match the imports to the referred and exported packages,
+      // merge the info for matching packages
+      Set extra = new TreeSet(importInstructions.keySet());
+      imports = merge("import-package", importInstructions,
+          referredAndExported, extra);
+
+      // Instructions that have not been used could be superfluous
+      // or if they do not contain wildcards, should be added
+      // as extra imports, the user knows best.
+      for (Iterator i = extra.iterator(); i.hasNext();) {
+        String p = (String) i.next();
+        if (p.startsWith("!") || p.indexOf('*') > 0
+            || p.indexOf('?') > 0 || p.indexOf('[') > 0) {
+          warning("Did not find matching referal for " + p);
+        } else {
+          Map map = (Map) importInstructions.get(p);
+          imports.put(p, map);
+        }
+      }
+
+      // See what information we can find to augment the
+      // imports. I.e. look on the classpath
+      augmentImports();
+
+      // Add the uses clause to the exports
+      doUses(exports, uses);
+    }
+  }
+
+  /**
+   * For each import, find the exporter and see what you can learn from it.
+   */
+  static Pattern versionPattern = Pattern
+                                    .compile("(\\d+\\.\\d+)\\.\\d+");
+
+  void augmentImports() {
+    for (Iterator imp = imports.keySet().iterator(); imp
+        .hasNext();) {
+      String packageName = (String) imp.next();
+      Map currentAttributes = (Map) imports
+          .get(packageName);
+
+      Map exporter = (Map) cpExports.get(packageName);
+      if (exporter != null) {
+        // See if we can borrow te version
+        String version = (String) exporter.get("version");
+        if (version == null) version = (String) exporter
+            .get("specification-version");
+        if (version != null) {
+          // We remove the micro part of the version
+          // to a bit more lenient
+          Matcher m = versionPattern.matcher(version);
+          if (m.matches()) version = m.group(1);
+          currentAttributes.put("version", version);
+        }
+
+        // If we use an import with mandatory
+        // attributes we better all use them
+        String mandatory = (String) exporter
+            .get("mandatory:");
+        if (mandatory != null) {
+          String[] attrs = mandatory.split("\\w*,\\w*");
+          for (int i = 0; i < attrs.length; i++) {
+            currentAttributes.put(attrs[i], exporter
+                .get(attrs[i]));
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * We will add all exports to the imports unless there is a -noimport
+   * directive specified on an export. This directive is skipped for the
+   * manifest.
+   * 
+   */
+  Map addExportsToImports(Map exports) {
+    Map importsFromExports = new HashMap();
+    for (Iterator export = exports.entrySet().iterator(); export
+        .hasNext();) {
+      Map.Entry entry = (Map.Entry) export.next();
+      String packageName = (String) entry.getKey();
+      Map parameters = (Map) entry.getValue();
+      String noimport = (String) parameters
+          .get("-noimport:");
+      if (noimport == null
+          || !noimport.equalsIgnoreCase("true")) {
+        Map importParameters = (Map) importsFromExports
+            .get(packageName);
+        if (importParameters == null) importsFromExports
+            .put(packageName, parameters);
+      }
+    }
+    return importsFromExports;
+  }
+
+  public Manifest calcManifest() throws IOException {
+    analyze();
+    Manifest manifest = new Manifest();
+    Attributes main = manifest.getMainAttributes();
+
+    main.putValue(BUNDLE_MANIFESTVERSION, "2");
+    main.putValue("Created-By", "Bnd-" + getVersion());
+
+    String exportHeader = printClauses(exports,
+        "uses:|include:|exclude:|mandatory:");
+
+    if (exportHeader.length() > 0) main.putValue(
+        EXPORT_PACKAGE, exportHeader);
+    else main.remove(EXPORT_PACKAGE);
+
+    Map temp = removeKeys(imports, "java.");
+    if (!temp.isEmpty()) {
+      main.putValue(IMPORT_PACKAGE, printClauses(temp,
+          "resolution:"));
+    } else {
+      main.remove(IMPORT_PACKAGE);
+    }
+
+    temp = new TreeMap(contained);
+    temp.keySet().removeAll(exports.keySet());
+
+    if (!temp.isEmpty()) {
+      main
+          .putValue(PRIVATE_PACKAGE, printClauses(temp, ""));
+    } else {
+      main.remove(PRIVATE_PACKAGE);
+    }
+
+    if (!ignored.isEmpty()) {
+      main.putValue(IGNORE_PACKAGE, printClauses(ignored,
+          ""));
+    } else {
+      main.remove(IGNORE_PACKAGE);
+    }
+
+    if (bundleClasspath != null
+        && !bundleClasspath.isEmpty()) main
+        .putValue(BUNDLE_CLASSPATH, printClauses(
+            bundleClasspath, ""));
+    else main.remove(BUNDLE_CLASSPATH);
+
+    Map l = doServiceComponent(getProperty(SERVICE_COMPONENT));
+    if (!l.isEmpty()) main.putValue(SERVICE_COMPONENT,
+        printClauses(l, ""));
+    else main.remove(SERVICE_COMPONENT);
+
+    for (Iterator h = properties.keySet().iterator(); h
+        .hasNext();) {
+      String header = (String) h.next();
+      if (!Character.isUpperCase(header.charAt(0))) continue;
+
+      if (header.equals(BUNDLE_CLASSPATH)
+          || header.equals(EXPORT_PACKAGE)
+          || header.equals(IMPORT_PACKAGE)) continue;
+
+      String value = getProperty(header);
+      if (value != null && main.getValue(header) == null) main
+          .putValue(header, value);
+    }
+
+    main.put(Attributes.Name.MANIFEST_VERSION, "1");
+
+    // Copy old values into new manifest, when they
+    // exist in the old one, but not in the new one
+    merge(manifest);
+
+    // Check for some defaults
+    String p = getProperty("project");
+    if (p != null) {
+      if (main.getValue(BUNDLE_SYMBOLICNAME) == null) {
+        main.putValue(BUNDLE_SYMBOLICNAME, p);
+      }
+      if (main.getValue(BUNDLE_NAME) == null) {
+        main.putValue(BUNDLE_NAME, p);
+      }
+    }
+    if (main.getValue(BUNDLE_VERSION) == null) main
+        .putValue(BUNDLE_VERSION, "0");
+
+    dot.setManifest(manifest);
+    return manifest;
+  }
+
+  /**
+   * @param manifest
+   * @throws Exception
+   */
+  private void merge(Manifest manifest) throws IOException {
+    Manifest old = dot.getManifest();
+    if (old != null) {
+      for (Iterator e = old.getMainAttributes().entrySet()
+          .iterator(); e.hasNext();) {
+        Map.Entry entry = (Map.Entry) e.next();
+        Attributes.Name name = (Attributes.Name) entry
+            .getKey();
+        String value = (String) entry.getValue();
+        if (name.toString().equalsIgnoreCase("Created-By")) name = new Attributes.Name(
+            "Originally-Created-By");
+        if (!manifest.getMainAttributes().containsKey(name)) manifest
+            .getMainAttributes().put(name, value);
+      }
+
+      // do not overwrite existing entries
+      Map oldEntries = old.getEntries();
+      Map newEntries = manifest.getEntries();
+      for (Iterator e = oldEntries.entrySet().iterator(); e
+          .hasNext();) {
+        Map.Entry entry = (Map.Entry) e.next();
+        if (!newEntries.containsKey(entry.getKey())) {
+          newEntries.put(entry.getKey(), entry.getValue());
+        }
+      }
+    }
+  }
+
+  /**
+   * Add the uses clauses
+   * 
+   * @param exports
+   * @param uses
+   * @throws MojoExecutionException
+   */
+  void doUses(Map exports, Map uses) {
+    for (Iterator i = exports.keySet().iterator(); i
+        .hasNext();) {
+      String packageName = (String) i.next();
+      Map clause = (Map) exports.get(packageName);
+
+      Set t = (Set) uses.get(packageName);
+      if (t != null && !t.isEmpty()) {
+        StringBuffer sb = new StringBuffer();
+        String del = "";
+        for (Iterator u = t.iterator(); u.hasNext();) {
+          String usedPackage = (String) u.next();
+          if (!usedPackage.equals(packageName)
+              && !usedPackage.startsWith("java.")) {
+            sb.append(del);
+            sb.append(usedPackage);
+            del = ",";
+          }
+        }
+        String s = sb.toString();
+        if (s.length() > 0) clause.put("uses:", sb
+            .toString());
+      }
+    }
+  }
+
+  /**
+   * Print a standard Map based OSGi header.
+   * 
+   * @param exports
+   *          map { name => Map { attribute|directive => value } }
+   * @return the clauses
+   */
+  String printClauses(Map exports, String allowedDirectives) {
+    StringBuffer sb = new StringBuffer();
+    String del = "";
+    for (Iterator i = exports.keySet().iterator(); i
+        .hasNext();) {
+      String name = (String) i.next();
+      Map map = (Map) exports.get(name);
+      sb.append(del);
+      sb.append(name);
+
+      for (Iterator j = map.keySet().iterator(); j
+          .hasNext();) {
+        String key = (String) j.next();
+
+        // Skip directives we do not recognize
+        if (key.endsWith(":")
+            && allowedDirectives.indexOf(key) < 0) continue;
+
+        String value = (String) map.get(key);
+        sb.append(";");
+        sb.append(key);
+        sb.append("=");
+        boolean dirty = value.indexOf(',') >= 0
+            || value.indexOf(';') >= 0;
+        if (dirty) sb.append("\"");
+        sb.append(value);
+        if (dirty) sb.append("\"");
+      }
+      del = ",";
+    }
+    return sb.toString();
+  }
+
+  /**
+   * Merge the attributes of two maps, where the first map can contain
+   * wildcarded names. The idea is that the first map contains patterns (for
+   * example *) with a set of attributes. These patterns are matched against the
+   * found packages in actual. If they match, the result is set with the merged
+   * set of attributes. It is expected that the instructions are ordered so that
+   * the instructor can define which pattern matches first. Attributes in the
+   * instructions override any attributes from the actual.<br/>
+   * 
+   * A pattern is a modified regexp so it looks like globbing. The * becomes a .*
+   * just like the ? becomes a .?. '.' are replaced with \\. Additionally, if
+   * the pattern starts with an exclamation mark, it will remove that matches
+   * for that pattern (- the !) from the working set. So the following patterns
+   * should work:
+   * <ul>
+   * <li>com.foo.bar</li>
+   * <li>com.foo.*</li>
+   * <li>com.foo.???</li>
+   * <li>com.*.[^b][^a][^r]</li>
+   * <li>!com.foo.* (throws away any match for com.foo.*)</li>
+   * </ul>
+   * Enough rope to hang the average developer I would say.
+   * 
+   * 
+   * @param instructions
+   *          the instructions with patterns. A
+   * @param actual
+   *          the actual found packages
+   */
+
+  Map merge(String type, Map instructions, Map actual,
+      Set superfluous) {
+    actual = new HashMap(actual); // we do not want to ruin our original
+    Map result = new HashMap();
+    for (Iterator i = instructions.keySet().iterator(); i
+        .hasNext();) {
+      String instruction = (String) i.next();
+      String originalInstruction = instruction;
+
+      Map instructedAttributes = (Map) instructions
+          .get(instruction);
+
+      if (instruction.startsWith("=")) {
+        result.put(instruction.substring(1),
+            instructedAttributes);
+        superfluous.remove(originalInstruction);
+        continue;
+      }
+
+      Instruction instr = getPattern(instruction);
+
+      for (Iterator p = actual.keySet().iterator(); p
+          .hasNext();) {
+        String packageName = (String) p.next();
+
+        if (instr.matches(packageName)) {
+          superfluous.remove(originalInstruction);
+          if (!instr.isNegated()) {
+            Map newAttributes = new HashMap();
+            newAttributes.putAll((Map) actual
+                .get(packageName));
+            newAttributes.putAll(instructedAttributes);
+            result.put(packageName, newAttributes);
+          } else {
+            ignored.put(packageName, new HashMap());
+          }
+          p.remove(); // Can never match again for another pattern
+        }
+      }
+
+    }
+    return result;
+  }
+
+  public static Instruction getPattern(String string) {
+    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());
+  }
+
+  public Map getBundleClasspath() {
+    return bundleClasspath;
+  }
+
+  public Map getContained() {
+    return contained;
+  }
+
+  public Map getExports() {
+    return exports;
+  }
+
+  public Map getImports() {
+    return imports;
+  }
+
+  public Map getReferred() {
+    return referred;
+  }
+
+  public Map getUses() {
+    return uses;
+  }
+
+  /**
+   * Return the set of unreachable code depending on exports and the bundle
+   * activator.
+   * 
+   * @return
+   */
+  public Set getUnreachable() {
+    Set unreachable = new HashSet(uses.keySet()); // all
+    for (Iterator r = exports.keySet().iterator(); r
+        .hasNext();) {
+      String packageName = (String) r.next();
+      removeTransitive(packageName, unreachable);
+    }
+    if (activator != null) {
+      String pack = activator.substring(0, activator
+          .lastIndexOf('.'));
+      removeTransitive(pack, unreachable);
+    }
+    return unreachable;
+  }
+
+  void removeTransitive(String name, Set unreachable) {
+    unreachable.remove(name);
+    if (!unreachable.contains(name)) return;
+
+    Set ref = (Set) uses.get(name);
+    if (ref != null) {
+      for (Iterator r = ref.iterator(); r.hasNext();) {
+        String element = (String) r.next();
+        removeTransitive(element, unreachable);
+      }
+    }
+  }
+
+  public Jar getJar() {
+    return dot;
+  }
+
+  public Properties getProperties() {
+    return properties;
+  }
+
+  /**
+   * 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 doServiceComponent(String serviceComponent)
+      throws UnsupportedEncodingException {
+    Map list = new LinkedHashMap();
+    Map sc = parseHeader(serviceComponent);
+    if (!sc.isEmpty()) {
+      for (Iterator i = sc.entrySet().iterator(); i
+          .hasNext();) {
+        Map.Entry entry = (Map.Entry) i.next();
+        String name = (String) entry.getKey();
+        Map info = (Map) entry.getValue();
+        if (name == null) {
+          error("No name in Service-Component header: "
+              + info);
+          continue;
+        }
+        if (dot.exists(name)) {
+          // Normal service component
+          list.put(name, info);
+        } else {
+          if (!checkClass(name)) error("Not found Service-Component header: "
+              + name);
+          else {
+            // We have a definition, so make an XML resources
+            Resource resource = createComponentResource(
+                name, info);
+            dot.putResource("OSGI-INF/" + name + ".xml",
+                resource);
+            list.put("OSGI-INF/" + name + ".xml",
+                new HashMap());
+          }
+        }
+      }
+    }
+    return list;
+  }
+
+  /**
+   * @param list
+   * @param name
+   * @param info
+   * @throws UnsupportedEncodingException
+   */
+  /*
+   * <?xml version="1.0" ?> <descriptor
+   * xmlns="http://www.osgi.org/xmlns/app/v1.0.0"> <application
+   * class="com.acme.app.SampleMidlet"> <reference name="log"
+   * interface="org.osgi.service.log.LogService"/> </application> </descriptor>
+   */
+  private Resource createComponentResource(String name,
+      Map info) throws UnsupportedEncodingException {
+
+    ByteArrayOutputStream out = new ByteArrayOutputStream();
+    PrintWriter pw = new PrintWriter(
+        new OutputStreamWriter(out, "UTF-8"));
+    pw.println("<?xml version='1.0' encoding='utf-8'?>");
+    pw.println("<component name='" + name + "'>");
+    pw.println("  <implementation class='" + name + "'/>");
+    String provides = (String) info.get("provide:");
+    provides(pw, provides);
+    properties(pw, info);
+    reference(info, pw);
+    pw.println("</component>");
+    pw.close();
+    return new EmbeddedResource(out.toByteArray());
+  }
+
+ 
+  private void properties(PrintWriter pw, Map info) {
+    Set properties = getMatchSet((String) info
+        .get("properties:"));
+    for (Iterator p = properties.iterator(); p.hasNext();) {
+      String clause = (String) 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.contains("@")) {
+          String parts[] = name.split("@");
+          name = parts[1];
+          type = parts[0];
+        }
+        String value = clause.substring(n + 1);
+        // 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 {
+            warnings.add("Invalid property type '" + type + "' for property " + name );
+          }
+        }
+
+        if (value.contains("\\n")) {
+          pw.print("'>");
+          pw.print(value.replaceAll("\\n", "\n"));
+          pw.println("</property>");
+        } else {
+          pw.print(" value='");
+          pw.print(value);
+          pw.print("'/>");
+        }
+      }
+    }
+  }
+
+  /**
+   * @param info
+   * @param pw
+   */
+  private void reference(Map info, PrintWriter pw) {
+    Set dynamic = getMatchSet((String) info.get("dynamic:"));
+    Set optional = getMatchSet((String) info
+        .get("optional:"));
+    Set multiple = getMatchSet((String) info
+        .get("multiple:"));
+
+    for (Iterator r = info.entrySet().iterator(); r
+        .hasNext();) {
+      Map.Entry ref = (Map.Entry) r.next();
+      String referenceName = (String) ref.getKey();
+      String interfaceName = (String) ref.getValue();
+      // TODO check if the interface is contained or imported
+
+      if (referenceName.endsWith(":")) continue;
+
+      if (!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 + "'");
+        // TODO Verify that the methods exist
+
+        // TODO ProSyst requires both a bind and unbind :-(
+        // if ( dynamic.contains(referenceName) )
+        pw.print(" unbind='unset" + z + "'");
+        // TODO Verify that the methods exist
+      }
+      if (dynamic.contains(referenceName)) {
+        pw.print(" policy='dynamic'");
+      }
+      pw.println("/>");
+    }
+  }
+
+  /**
+   * @param pw
+   * @param provides
+   */
+  private void provides(PrintWriter pw, String provides) {
+    if (provides != null) {
+      pw.println("  <service>");
+      StringTokenizer st = new StringTokenizer(provides,
+          ",");
+      while (st.hasMoreTokens()) {
+        String interfaceName = st.nextToken();
+        pw.println("    <provide interface='"
+            + interfaceName + "'/>");
+        if (!checkClass(interfaceName)) error("Component definition provides a class that is neither imported nor contained: "
+            + interfaceName);
+      }
+      pw.println("  </service>");
+    }
+  }
+
+  private boolean checkClass(String interfaceName) {
+    String path = interfaceName.replace('.', '/')
+        + ".class";
+    if (classspace.containsKey(path)) return true;
+
+    String pack = interfaceName;
+    int n = pack.lastIndexOf('.');
+    if (n > 0) pack = pack.substring(0, n);
+    else pack = ".";
+
+    return imports.containsKey(pack);
+  }
+
+  private Set getMatchSet(String list) {
+    if (list == null) return new HashSet();
+
+    String parts[] = list.split("\\s*,\\s*");
+    return new HashSet(Arrays.asList(parts));
+  }
+
+  /**
+   * Specifically for Maven
+   * 
+   * @param properties
+   *          the properties
+   */
+
+  public static Properties getManifest(File dirOrJar)
+      throws IOException {
+    Analyzer analyzer = new Analyzer();
+    analyzer.setJar(dirOrJar);
+    Properties properties = new Properties();
+    properties.put(IMPORT_PACKAGE, "*");
+    properties.put(EXPORT_PACKAGE, "*");
+    analyzer.setProperties(properties);
+    Manifest m = analyzer.calcManifest();
+    Properties result = new Properties();
+    for (Iterator i = m.getMainAttributes().keySet()
+        .iterator(); i.hasNext();) {
+      Attributes.Name name = (Attributes.Name) i.next();
+      result.put(name.toString(), m.getMainAttributes()
+          .getValue(name));
+    }
+    return result;
+  }
+
+}

Propchange: maven/sandbox/plugins/maven-bundle-plugin/src/main/java/aQute/lib/osgi/Analyzer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/sandbox/plugins/maven-bundle-plugin/src/main/java/aQute/lib/osgi/Analyzer.java
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Added: maven/sandbox/plugins/maven-bundle-plugin/src/main/java/aQute/lib/osgi/Builder.java
URL: http://svn.apache.org/viewvc/maven/sandbox/plugins/maven-bundle-plugin/src/main/java/aQute/lib/osgi/Builder.java?view=auto&rev=504757
==============================================================================
--- maven/sandbox/plugins/maven-bundle-plugin/src/main/java/aQute/lib/osgi/Builder.java (added)
+++ maven/sandbox/plugins/maven-bundle-plugin/src/main/java/aQute/lib/osgi/Builder.java Wed Feb  7 16:54:27 2007
@@ -0,0 +1,351 @@
+/* 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;
+
+import java.io.*;
+import java.util.*;
+import java.util.regex.*;
+import java.util.zip.ZipException;
+
+/**
+ * Include-Resource: ( [name '=' ] file )+
+ * 
+ * Private-Package: package-decl ( ',' package-decl )*
+ * 
+ * Export-Package: package-decl ( ',' package-decl )*
+ * 
+ * Import-Package: package-decl ( ',' package-decl )*
+ * 
+ * @version $Revision: 1.2 $
+ */
+public class Builder extends Analyzer {
+	boolean			sources	= false;
+	private File[]	sourcePath;
+
+	public void setProperties(Properties properties) {
+		super.setProperties(properties);
+	}
+
+	public Jar build() throws Exception {
+		sources = getProperty("-sources") != null;
+
+		dot = new Jar("dot");
+		doExpand(dot);
+		doIncludes(dot);
+		dot.setManifest(calcManifest());
+		// This must happen after we analyzed so
+		// we know what it is on the classpath
+		addSources(dot);
+		doVerify(dot);
+		if (dot.getResources().isEmpty())
+			error("The JAR is empty");
+		
+//		Signer signer = getSigner();
+//		if ( signer != null ) {
+//			signer.signJar(dot);
+//		}
+		return dot;
+	}
+
+	/**
+	 * 
+	 */
+	private void addSources(Jar dot) {
+		if (!sources)
+			return;
+
+		try {
+			ByteArrayOutputStream out = new ByteArrayOutputStream();
+			getProperties().store(out, "Generated by BND, at " + new Date());
+			dot.putResource("OSGI-OPT/bnd.bnd", new EmbeddedResource(out
+					.toByteArray()));
+		}
+		catch (Exception e) {
+			error("Can not embed bnd file in JAR: " + e);
+		}
+
+		for (Iterator cpe = classspace.keySet().iterator(); cpe.hasNext();) {
+			String path = (String) cpe.next();
+			path = path.substring(0, path.length() - ".class".length())
+					+ ".java";
+
+			for (int i = 0; i < sourcePath.length; i++) {
+				File root = sourcePath[i];
+				File f = new File(root, path);
+				if (f.exists()) {
+					dot
+							.putResource(
+									"OSGI-OPT/src/" + path,
+									new FileResource(f));
+				}
+			}
+		}
+	}
+
+	private void doVerify(Jar dot) throws Exception {
+		Verifier verifier = new Verifier(dot);
+		verifier.verify();
+		errors.addAll(verifier.getErrors());
+		warnings.addAll(verifier.getWarnings());
+	}
+
+	private void doExpand(Jar jar) throws IOException {
+		Map prive = replaceWithPattern(getHeader(Analyzer.PRIVATE_PACKAGE));
+		Map export = replaceWithPattern(getHeader(Analyzer.EXPORT_PACKAGE));
+		if (prive.isEmpty() && export.isEmpty()) {
+			warnings
+					.add("Neither Export-Package nor Private-Package is set, therefore no packages will be included");
+		}
+		doExpand(jar, "Export-Package ", export);
+		doExpand(jar, "Private-Package ", prive);
+	}
+
+	private void doExpand(Jar jar, String name, Map instructions) {
+		Set superfluous = new HashSet(instructions.keySet());
+        if (classpathForExpansion == null) {
+            classpathForExpansion = classpath ;
+        }
+		for (Iterator c = classpathForExpansion.iterator(); c.hasNext();) {
+			Jar now = (Jar) c.next();
+            doExpand(jar, instructions, now, superfluous);
+		}
+		if (superfluous.size() > 0) {
+			StringBuffer sb = new StringBuffer();
+			String del = "Instructions for " + name + " that are never used: ";
+			for (Iterator i = superfluous.iterator(); i.hasNext();) {
+				Instruction p = (Instruction) i.next();
+				sb.append(del);
+				sb.append(p.getPattern());
+				del = ", ";
+			}
+			warning(sb.toString());
+		}
+	}
+
+	/**
+	 * Iterate over each directory in the class path entry and check if that
+	 * directory is a desired package.
+	 * 
+	 * @param included
+	 * @param classpathEntry
+	 */
+	private void doExpand(Jar jar, Map included, Jar classpathEntry,
+			Set superfluous) {
+		for (Iterator p = classpathEntry.getDirectories().entrySet().iterator(); p
+				.hasNext();) {
+			Map.Entry directory = (Map.Entry) p.next();
+			String path = (String) directory.getKey();
+
+			if (doNotCopy.matcher(getName(path)).matches())
+				continue;
+
+			String pack = path.replace('/', '.');
+			Instruction instr = matches(included, pack, superfluous);
+			if ( instr != null && ! instr.isNegated() ) {
+				Map contents = (Map) directory.getValue();
+				jar.addDirectory(contents);
+			}
+		}
+	}
+
+	private Map replaceWithPattern(Map header) {
+		Map map = new LinkedHashMap();
+		for (Iterator e = header.entrySet().iterator(); e.hasNext();) {
+			Map.Entry entry = (Map.Entry) e.next();
+			String pattern = (String) entry.getKey();
+			Instruction instr = Analyzer.getPattern(pattern);
+			map.put(instr, entry.getValue());
+		}
+		return map;
+	}
+
+	private Instruction matches(Map instructions, String pack, Set superfluousPatterns) {
+		for (Iterator i = instructions.keySet().iterator(); i.hasNext();) {
+			Instruction pattern = (Instruction) i.next();
+			if (pattern.matches(pack)) {
+				superfluousPatterns.remove(pattern);
+				return pattern;
+			}
+		}
+		return null;
+	}
+
+	private Map getHeader(String string) {
+		if (string == null)
+			return new LinkedHashMap();
+		return parseHeader(getProperty(string));
+	}
+
+	/**
+	 * Parse the Bundle-Includes header. Files in the bundles Include header are
+	 * included in the jar. The source can be a directory or a file.
+	 * 
+	 * @throws IOException
+	 * @throws FileNotFoundException
+	 */
+	private void doIncludes(Jar jar) throws Exception {
+		Macro macro = new Macro(getProperties(), this);
+
+		String includes = getProperty("Bundle-Includes");
+		if (includes == null)
+			includes = getProperty("Include-Resource");
+		else
+			warnings
+					.add("Please use Include-Resource instead of Bundle-Includes");
+
+		if (includes == null)
+			return;
+
+		String clauses[] = includes.split("\\s*,\\s*");
+		for (int i = 0; i < clauses.length; i++) {
+			boolean preprocess = false;
+			if (clauses[i].startsWith("{") && clauses[i].endsWith("}")) {
+				preprocess = true;
+				clauses[i] = clauses[i].substring(1, clauses[i].length() - 1)
+						.trim();
+			}
+
+			if (clauses[i].startsWith("@")) {
+				extractFromJar(jar, clauses[i]);
+			}
+			else {
+				String parts[] = clauses[i].split("\\s*=\\s*");
+
+				File source;
+				String destinationPath;
+
+				if (parts.length == 1) {
+					// Just a copy, destination path defined by
+					// source path.
+					source = new File(base, parts[0]);
+
+					// Directories should be copied to the root
+					// but files to their file name ...
+					if (source.isDirectory())
+						destinationPath = "";
+					else
+						destinationPath = source.getName();
+				}
+				else {
+					source = new File(base, parts[1]);
+					destinationPath = parts[0];
+				}
+
+				// Some people insist on ending a directory with
+				// a slash ... it now also works if you do /=dir
+				if (destinationPath.endsWith("/"))
+					destinationPath = destinationPath.substring(
+							0,
+							destinationPath.length() - 1);
+
+				copy(jar, destinationPath, source, preprocess ? macro : null);
+			}
+		}
+	}
+
+	/**
+	 * @param jar
+	 * @param clauses
+	 * @param i
+	 * @throws ZipException
+	 * @throws IOException
+	 */
+	private void extractFromJar(Jar jar, String clause )
+			throws ZipException, IOException {
+		// Inline all resources and classes from another jar
+		// optionally appended with a modified regular expression
+		// like @zip.jar!/META-INF/MANIFEST.MF
+		String name = clause.substring(1);
+		int n = name.lastIndexOf("!/");
+		Pattern filter = null;
+		if (n > 0) {
+			String fstring = name.substring(n+2);
+			name = name.substring(0, n);
+			filter = wildcard(fstring);
+		}
+		File file = new File(base.getAbsoluteFile(), name);
+		ZipResource.build(jar, file, filter );
+	}
+
+	private Pattern wildcard(String spec) {
+		StringBuffer sb = new StringBuffer();
+		for (int j = 0; j < spec.length(); j++) {
+			char c = spec.charAt(j);
+			switch (c) {
+				case '.' :
+					sb.append("\\.");
+					break;
+
+				case '*' :
+					// test for ** (all directories)
+					if (j < spec.length() - 1 && spec.charAt(j + 1) == '*') {
+						sb.append(".*");
+						j++;
+					}
+					else
+						sb.append("[^/]*");
+					break;
+				default :
+					sb.append(c);
+					break;
+			}
+		}
+		String s = sb.toString();
+		try {
+			return Pattern.compile(s);
+		}
+		catch (Exception e) {
+			error("Invalid regular expression on wildcarding: "
+					+ spec + " used *");
+		}
+		return null;
+	}
+
+	private void copy(Jar jar, String path, File from, Macro macro)
+			throws Exception {
+		if (doNotCopy.matcher(from.getName()).matches())
+			return;
+
+		if (from.isDirectory()) {
+			String next = path;
+			if (next.length() != 0)
+				next += '/';
+
+			File files[] = from.listFiles();
+			for (int i = 0; i < files.length; i++) {
+				copy(jar, next + files[i].getName(), files[i], macro);
+			}
+		}
+		else {
+			if (macro != null) {
+				String content = read(from);
+				content = macro.process(content);
+				jar.putResource(path, new EmbeddedResource(content
+						.getBytes("UTF-8")));
+			}
+			else
+				jar.putResource(path, new FileResource(from));
+		}
+	}
+
+	private String read(File from) throws Exception {
+		long size = from.length();
+		byte[] buffer = new byte[(int) size];
+		FileInputStream in = new FileInputStream(from);
+		in.read(buffer);
+		in.close();
+		return new String(buffer, "UTF-8");
+	}
+
+	private String getName(String where) {
+		int n = where.lastIndexOf('/');
+		if (n < 0)
+			return where;
+
+		return where.substring(n + 1);
+	}
+
+	public void setSourcepath(File[] files) {
+		sourcePath = files;
+	}
+}

Propchange: maven/sandbox/plugins/maven-bundle-plugin/src/main/java/aQute/lib/osgi/Builder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/sandbox/plugins/maven-bundle-plugin/src/main/java/aQute/lib/osgi/Builder.java
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"