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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Verifier.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Verifier.java?rev=1185095&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Verifier.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Verifier.java Mon Oct 17 10:31:43 2011
@@ -0,0 +1,959 @@
+package aQute.lib.osgi;
+
+import java.util.*;
+import java.util.jar.*;
+import java.util.regex.*;
+
+import aQute.libg.qtokens.*;
+
+public class Verifier extends Analyzer {
+
+    Jar                              dot;
+    Manifest                         manifest;
+    Map<String, Map<String, String>> referred  = newHashMap();
+    Map<String, Map<String, String>> contained = newHashMap();
+    Map<String, Set<String>>         uses      = newHashMap();
+    Map<String, Map<String, String>> mimports;
+    Map<String, Map<String, String>> mdynimports;
+    Map<String, Map<String, String>> mexports;
+    List<Jar>                        bundleClasspath;
+    Map<String, Map<String, String>> ignore    = newHashMap();                                    // Packages
+    // to
+    // ignore
+
+    Map<String, Clazz>               classSpace;
+    boolean                          r3;
+    boolean                          usesRequire;
+    boolean                          fragment;
+    Attributes                       main;
+
+    final static Pattern             EENAME    = Pattern
+                                                       .compile("CDC-1\\.0/Foundation-1\\.0"
+                                                               + "|CDC-1\\.1/Foundation-1\\.1"
+                                                               + "|OSGi/Minimum-1\\.[1-9]"
+                                                               + "|JRE-1\\.1"
+                                                               + "|J2SE-1\\.2"
+                                                               + "|J2SE-1\\.3"
+                                                               + "|J2SE-1\\.4"
+                                                               + "|J2SE-1\\.5"
+                                                               + "|JavaSE-1\\.6"
+                                                               + "|JavaSE-1\\.7"
+                                                               + "|PersonalJava-1\\.1"
+                                                               + "|PersonalJava-1\\.2"
+                                                               + "|CDC-1\\.0/PersonalBasis-1\\.0"
+                                                               + "|CDC-1\\.0/PersonalJava-1\\.0");
+
+    final static int                 V1_1      = 45;
+    final static int                 V1_2      = 46;
+    final static int                 V1_3      = 47;
+    final static int                 V1_4      = 48;
+    final static int                 V1_5      = 49;
+    final static int                 V1_6      = 50;
+    final static int                 V1_7      = 51;
+
+    static class EE {
+        String name;
+        int    target;
+
+        EE(String name, int source, int target) {
+            this.name = name;
+            this.target = target;
+        }
+    }
+
+    final static EE[]           ees                            = {
+            new EE("CDC-1.0/Foundation-1.0", V1_3, V1_1),
+            new EE("CDC-1.1/Foundation-1.1", V1_3, V1_2),
+            new EE("OSGi/Minimum-1.0", V1_3, V1_1),
+            new EE("OSGi/Minimum-1.1", V1_3, V1_2),
+            new EE("JRE-1.1", V1_1, V1_1), //
+            new EE("J2SE-1.2", V1_2, V1_1), //
+            new EE("J2SE-1.3", V1_3, V1_1), //
+            new EE("J2SE-1.4", V1_3, V1_2), //
+            new EE("J2SE-1.5", V1_5, V1_5), //
+            new EE("JavaSE-1.6", V1_6, V1_6),
+            new EE("PersonalJava-1.1", V1_1, V1_1),
+            new EE("PersonalJava-1.2", V1_1, V1_1),
+            new EE("CDC-1.0/PersonalBasis-1.0", V1_3, V1_1),
+            new EE("CDC-1.0/PersonalJava-1.0", V1_3, V1_1),
+            new EE("CDC-1.1/PersonalBasis-1.1", V1_3, V1_2),
+            new EE("CDC-1.1/PersonalJava-1.1", V1_3, V1_2)    };
+
+    final static Pattern        BUNDLEMANIFESTVERSION          = Pattern
+                                                                       .compile("2");
+    public final static String  SYMBOLICNAME_STRING            = "[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)*";
+    public final static Pattern SYMBOLICNAME                   = Pattern
+                                                                       .compile(SYMBOLICNAME_STRING);
+
+    public final static String  VERSION_STRING                 = "[0-9]+(\\.[0-9]+(\\.[0-9]+(\\.[0-9A-Za-z_-]+)?)?)?";
+    public final static Pattern VERSION                        = Pattern
+                                                                       .compile(VERSION_STRING);
+    final static Pattern        FILTEROP                       = Pattern
+                                                                       .compile("=|<=|>=|~=");
+    public final static Pattern VERSIONRANGE                   = Pattern
+                                                                       .compile("((\\(|\\[)"
+                                                                               + VERSION_STRING
+                                                                               + ","
+                                                                               + VERSION_STRING
+                                                                               + "(\\]|\\)))|"
+                                                                               + VERSION_STRING);
+    final static Pattern        FILE                           = Pattern
+                                                                       .compile("/?[^/\"\n\r\u0000]+(/[^/\"\n\r\u0000]+)*");
+    final static Pattern        WILDCARDPACKAGE                = Pattern
+                                                                       .compile("((\\p{Alnum}|_)+(\\.(\\p{Alnum}|_)+)*(\\.\\*)?)|\\*");
+    public final static Pattern ISO639                         = Pattern
+                                                                       .compile("[A-Z][A-Z]");
+    public final static Pattern HEADER_PATTERN                 = Pattern
+                                                                       .compile("[A-Za-z0-9][-a-zA-Z0-9_]+");
+    public final static Pattern TOKEN                          = Pattern
+                                                                       .compile("[-a-zA-Z0-9_]+");
+
+    public final static Pattern NUMBERPATTERN                  = Pattern
+                                                                       .compile("\\d+");
+    public final static Pattern PATHPATTERN                    = Pattern
+                                                                       .compile(".*");
+    public final static Pattern FQNPATTERN                     = Pattern
+                                                                       .compile(".*");
+    public final static Pattern URLPATTERN                     = Pattern
+                                                                       .compile(".*");
+    public final static Pattern ANYPATTERN                     = Pattern
+                                                                       .compile(".*");
+    public final static Pattern FILTERPATTERN                  = Pattern
+                                                                       .compile(".*");
+    public final static Pattern TRUEORFALSEPATTERN             = Pattern
+                                                                       .compile("true|false|TRUE|FALSE");
+    public static final Pattern WILDCARDNAMEPATTERN            = Pattern
+                                                                       .compile(".*");
+    public static final Pattern BUNDLE_ACTIVATIONPOLICYPATTERN = Pattern
+                                                                       .compile("lazy");
+
+    public final static String  EES[]                          = {
+            "CDC-1.0/Foundation-1.0", "CDC-1.1/Foundation-1.1",
+            "OSGi/Minimum-1.0", "OSGi/Minimum-1.1", "OSGi/Minimum-1.2",
+            "JRE-1.1", "J2SE-1.2", "J2SE-1.3", "J2SE-1.4", "J2SE-1.5",
+            "JavaSE-1.6", "JavaSE-1.7", "PersonalJava-1.1", "PersonalJava-1.2",
+            "CDC-1.0/PersonalBasis-1.0", "CDC-1.0/PersonalJava-1.0" };
+
+    public final static String  OSNAMES[]                      = {
+            "AIX", // IBM
+            "DigitalUnix", // Compaq
+            "Embos", // Segger Embedded Software Solutions
+            "Epoc32", // SymbianOS Symbian OS
+            "FreeBSD", // Free BSD
+            "HPUX", // hp-ux Hewlett Packard
+            "IRIX", // Silicon Graphics
+            "Linux", // Open source
+            "MacOS", // Apple
+            "NetBSD", // Open source
+            "Netware", // Novell
+            "OpenBSD", // Open source
+            "OS2", // OS/2 IBM
+            "QNX", // procnto QNX
+            "Solaris", // Sun (almost an alias of SunOS)
+            "SunOS", // Sun Microsystems
+            "VxWorks", // WindRiver Systems
+            "Windows95", "Win32", "Windows98", "WindowsNT", "WindowsCE",
+            "Windows2000", // Win2000
+            "Windows2003", // Win2003
+            "WindowsXP", "WindowsVista",                      };
+
+    public final static String  PROCESSORNAMES[]               = { "68k", // Motorola
+            // 68000
+            "ARM_LE", // Intel Strong ARM. Deprecated because it does not
+            // specify the endianness. See the following two rows.
+            "arm_le", // Intel Strong ARM Little Endian mode
+            "arm_be", // Intel String ARM Big Endian mode
+            "Alpha", //
+            "ia64n",// Hewlett Packard 32 bit
+            "ia64w",// Hewlett Packard 64 bit mode
+            "Ignite", // psc1k PTSC
+            "Mips", // SGI
+            "PArisc", // Hewlett Packard
+            "PowerPC", // power ppc Motorola/IBM Power PC
+            "Sh4", // Hitachi
+            "Sparc", // SUN
+            "Sparcv9", // SUN
+            "S390", // IBM Mainframe 31 bit
+            "S390x", // IBM Mainframe 64-bit
+            "V850E", // NEC V850E
+            "x86", // pentium i386
+            "i486", // i586 i686 Intel& AMD 32 bit
+            "x86-64",                                         };
+
+    Properties                  properties;
+
+    public Verifier(Jar jar) throws Exception {
+        this(jar, null);
+    }
+
+    public Verifier(Jar jar, Properties properties) throws Exception {
+        this.dot = jar;
+        this.properties = properties;
+        this.manifest = jar.getManifest();
+        if (manifest == null) {
+            manifest = new Manifest();
+            error("This file contains no manifest and is therefore not a bundle");
+        }
+        main = this.manifest.getMainAttributes();
+        verifyHeaders(main);
+        r3 = getHeader(Analyzer.BUNDLE_MANIFESTVERSION) == null;
+        usesRequire = getHeader(Analyzer.REQUIRE_BUNDLE) != null;
+        fragment = getHeader(Analyzer.FRAGMENT_HOST) != null;
+
+        bundleClasspath = getBundleClassPath();
+        mimports = parseHeader(manifest.getMainAttributes().getValue(
+                Analyzer.IMPORT_PACKAGE));
+        mdynimports = parseHeader(manifest.getMainAttributes().getValue(
+                Analyzer.DYNAMICIMPORT_PACKAGE));
+        mexports = parseHeader(manifest.getMainAttributes().getValue(
+                Analyzer.EXPORT_PACKAGE));
+
+        ignore = parseHeader(manifest.getMainAttributes().getValue(
+                Analyzer.IGNORE_PACKAGE));
+    }
+
+    public Verifier() {
+        // TODO Auto-generated constructor stub
+    }
+
+    private void verifyHeaders(Attributes main) {
+        for (Object element : main.keySet()) {
+            Attributes.Name header = (Attributes.Name) element;
+            String h = header.toString();
+            if (!HEADER_PATTERN.matcher(h).matches())
+                error("Invalid Manifest header: " + h + ", pattern="
+                        + HEADER_PATTERN);
+        }
+    }
+
+    private List<Jar> getBundleClassPath() {
+        List<Jar> list = newList();
+        String bcp = getHeader(Analyzer.BUNDLE_CLASSPATH);
+        if (bcp == null) {
+            list.add(dot);
+        } else {
+            Map<String, Map<String, String>> entries = parseHeader(bcp);
+            for (Map.Entry<String, Map<String, String>> ex : entries.entrySet()) {
+                String jarOrDir = ex.getKey();
+                if (jarOrDir.equals(".")) {
+                    list.add(dot);
+                } else {
+                    if (jarOrDir.equals("/"))
+                        jarOrDir = "";
+                    if (jarOrDir.endsWith("/")) {
+                        error("Bundle-Classpath directory must not end with a slash: "
+                                + jarOrDir);
+                        jarOrDir = jarOrDir.substring(0, jarOrDir.length() - 1);
+                    }
+
+                    Resource resource = dot.getResource(jarOrDir);
+                    if (resource != null) {
+                        try {
+                            Jar sub = new Jar(jarOrDir);
+                            addClose(sub);
+                            EmbeddedResource.build(sub, resource);
+                            if (!jarOrDir.endsWith(".jar"))
+                                warning("Valid JAR file on Bundle-Classpath does not have .jar extension: "
+                                        + jarOrDir);
+                            list.add(sub);
+                        } catch (Exception e) {
+                            error("Invalid embedded JAR file on Bundle-Classpath: "
+                                    + jarOrDir + ", " + e);
+                        }
+                    } else if (dot.getDirectories().containsKey(jarOrDir)) {
+                        if (r3)
+                            error("R3 bundles do not support directories on the Bundle-ClassPath: "
+                                    + jarOrDir);
+
+                        try {
+                            Jar sub = new Jar(jarOrDir);
+                            addClose(sub);
+                            for (Map.Entry<String, Resource> entry : dot
+                                    .getResources().entrySet()) {
+                                if (entry.getKey().startsWith(jarOrDir))
+                                    sub.putResource(entry.getKey().substring(
+                                            jarOrDir.length() + 1), entry
+                                            .getValue());
+                            }
+                            list.add(sub);
+                        } catch (Exception e) {
+                            error("Invalid embedded directory file on Bundle-Classpath: "
+                                    + jarOrDir + ", " + e);
+                        }
+                    } else {
+                        // Map<String, String> info = ex.getValue();
+                        // if (! "optional".equals(
+                        // info.get(RESOLUTION_DIRECTIVE)))
+                        // warning("Cannot find a file or directory for
+                        // Bundle-Classpath entry: %s",
+                        // jarOrDir);
+                    }
+                }
+            }
+        }
+        return list;
+    }
+
+    /*
+     * Bundle-NativeCode ::= nativecode ( ',' nativecode )* ( ’,’ optional) ?
+     * nativecode ::= path ( ';' path )* // See 1.4.2 ( ';' parameter )+
+     * optional ::= ’*’
+     */
+    public void verifyNative() {
+        String nc = getHeader("Bundle-NativeCode");
+        doNative(nc);
+    }
+
+    public void doNative(String nc) {
+        if (nc != null) {
+            QuotedTokenizer qt = new QuotedTokenizer(nc, ",;=", false);
+            char del;
+            do {
+                do {
+                    String name = qt.nextToken();
+                    if (name == null) {
+                        error("Can not parse name from bundle native code header: "
+                                + nc);
+                        return;
+                    }
+                    del = qt.getSeparator();
+                    if (del == ';') {
+                        if (dot != null && !dot.exists(name)) {
+                            error("Native library not found in JAR: " + name);
+                        }
+                    } else {
+                        String value = null;
+                        if (del == '=')
+                            value = qt.nextToken();
+
+                        String key = name.toLowerCase();
+                        if (key.equals("osname")) {
+                            // ...
+                        } else if (key.equals("osversion")) {
+                            // verify version range
+                            verify(value, VERSIONRANGE);
+                        } else if (key.equals("language")) {
+                            verify(value, ISO639);
+                        } else if (key.equals("processor")) {
+                            // verify(value, PROCESSORS);
+                        } else if (key.equals("selection-filter")) {
+                            // verify syntax filter
+                            verifyFilter(value);
+                        } else if (name.equals("*") && value == null) {
+                            // Wildcard must be at end.
+                            if (qt.nextToken() != null)
+                                error("Bundle-Native code header may only END in wildcard: nc");
+                        } else {
+                            warning("Unknown attribute in native code: " + name
+                                    + "=" + value);
+                        }
+                        del = qt.getSeparator();
+                    }
+                } while (del == ';');
+            } while (del == ',');
+        }
+    }
+
+    public boolean verifyFilter(String value) {
+        try {
+            verifyFilter(value, 0);
+            return true;
+        } catch (Exception e) {
+            error("Not a valid filter: " + value + e.getMessage());
+            return false;
+        }
+    }
+
+    private void verifyActivator() {
+        String bactivator = getHeader("Bundle-Activator");
+        if (bactivator != null) {
+            Clazz cl = loadClass(bactivator);
+            if (cl == null) {
+                int n = bactivator.lastIndexOf('.');
+                if (n > 0) {
+                    String pack = bactivator.substring(0, n);
+                    if (mimports.containsKey(pack))
+                        return;
+                    error("Bundle-Activator not found on the bundle class path nor in imports: "
+                            + bactivator);
+                } else
+                    error("Activator uses default package and is not local (default package can not be imported): "
+                            + bactivator);
+            }
+        }
+    }
+
+    private Clazz loadClass(String className) {
+        String path = className.replace('.', '/') + ".class";
+        return (Clazz) classSpace.get(path);
+    }
+
+    private void verifyComponent() {
+        String serviceComponent = getHeader("Service-Component");
+        if (serviceComponent != null) {
+            Map<String, Map<String, String>> map = parseHeader(serviceComponent);
+            for (String component : map.keySet()) {
+                if (component.indexOf("*") < 0 && !dot.exists(component)) {
+                    error("Service-Component entry can not be located in JAR: "
+                            + component);
+                } else {
+                    // validate component ...
+                }
+            }
+        }
+    }
+
+    public void info() {
+        System.out.println("Refers                           : " + referred);
+        System.out.println("Contains                         : " + contained);
+        System.out.println("Manifest Imports                 : " + mimports);
+        System.out.println("Manifest Exports                 : " + mexports);
+    }
+
+    /**
+     * Invalid exports are exports mentioned in the manifest but not found on
+     * the classpath. This can be calculated with: exports - contains.
+     * 
+     * Unfortunately, we also must take duplicate names into account. These
+     * duplicates are of course no erroneous.
+     */
+    private void verifyInvalidExports() {
+        Set<String> invalidExport = newSet(mexports.keySet());
+        invalidExport.removeAll(contained.keySet());
+
+        // We might have duplicate names that are marked for it. These
+        // should not be counted. Should we test them against the contained
+        // set? Hmm. If someone wants to hang himself by using duplicates than
+        // I guess he can go ahead ... This is not a recommended practice
+        for (Iterator<String> i = invalidExport.iterator(); i.hasNext();) {
+            String pack = i.next();
+            if (isDuplicate(pack))
+                i.remove();
+        }
+
+        if (!invalidExport.isEmpty())
+            error("Exporting packages that are not on the Bundle-Classpath"
+                    + bundleClasspath + ": " + invalidExport);
+    }
+
+    /**
+     * Invalid imports are imports that we never refer to. They can be
+     * calculated by removing the refered packages from the imported packages.
+     * This leaves packages that the manifest imported but that we never use.
+     */
+    private void verifyInvalidImports() {
+        Set<String> invalidImport = newSet(mimports.keySet());
+        invalidImport.removeAll(referred.keySet());
+        // TODO Added this line but not sure why it worked before ...
+        invalidImport.removeAll(contained.keySet());
+        String bactivator = getHeader(Analyzer.BUNDLE_ACTIVATOR);
+        if (bactivator != null) {
+            int n = bactivator.lastIndexOf('.');
+            if (n > 0) {
+                invalidImport.remove(bactivator.substring(0, n));
+            }
+        }
+        if (isPedantic() && !invalidImport.isEmpty())
+            warning("Importing packages that are never refered to by any class on the Bundle-Classpath"
+                    + bundleClasspath + ": " + invalidImport);
+    }
+
+    /**
+     * Check for unresolved imports. These are referals that are not imported by
+     * the manifest and that are not part of our bundle classpath. The are
+     * calculated by removing all the imported packages and contained from the
+     * refered packages.
+     */
+    private void verifyUnresolvedReferences() {
+        Set<String> unresolvedReferences = new TreeSet<String>(referred
+                .keySet());
+        unresolvedReferences.removeAll(mimports.keySet());
+        unresolvedReferences.removeAll(contained.keySet());
+
+        // Remove any java.** packages.
+        for (Iterator<String> p = unresolvedReferences.iterator(); p.hasNext();) {
+            String pack = p.next();
+            if (pack.startsWith("java.") || ignore.containsKey(pack))
+                p.remove();
+            else {
+                // Remove any dynamic imports
+                if (isDynamicImport(pack))
+                    p.remove();
+            }
+        }
+
+        if (!unresolvedReferences.isEmpty()) {
+            // Now we want to know the
+            // classes that are the culprits
+            Set<String> culprits = new HashSet<String>();
+            for (Clazz clazz : classSpace.values()) {
+                if (hasOverlap(unresolvedReferences, clazz.getReferred()))
+                    culprits.add(clazz.getPath());
+            }
+
+            error("Unresolved references to " + unresolvedReferences
+                    + " by class(es) on the Bundle-Classpath" + bundleClasspath
+                    + ": " + culprits);
+        }
+    }
+
+    /**
+     * @param p
+     * @param pack
+     */
+    private boolean isDynamicImport(String pack) {
+        for (String pattern : mdynimports.keySet()) {
+            // Wildcard?
+            if (pattern.equals("*"))
+                return true; // All packages can be dynamically imported
+
+            if (pattern.endsWith(".*")) {
+                pattern = pattern.substring(0, pattern.length() - 2);
+                if (pack.startsWith(pattern)
+                        && (pack.length() == pattern.length() || pack
+                                .charAt(pattern.length()) == '.'))
+                    return true;
+            } else {
+                if (pack.equals(pattern))
+                    return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean hasOverlap(Set<?> a, Set<?> b) {
+        for (Iterator<?> i = a.iterator(); i.hasNext();) {
+            if (b.contains(i.next()))
+                return true;
+        }
+        return false;
+    }
+
+    public void verify() throws Exception {
+        if (classSpace == null)
+            classSpace = analyzeBundleClasspath(dot,
+                    parseHeader(getHeader(Analyzer.BUNDLE_CLASSPATH)),
+                    contained, referred, uses);
+        
+        
+        verifyDirectives("Export-Package", "uses:|mandatory:|include:|exclude:|" + IMPORT_DIRECTIVE);
+        verifyDirectives("Import-Package", "resolution:");
+        verifyDirectives("Require-Bundle", "visibility:|resolution:");
+        verifyDirectives("Fragment-Host", "resolution:");
+        verifyDirectives("Provide-Capability", "effective:|uses:");
+        verifyDirectives("Require-Capability", "effective:|resolve:|filter:");
+        verifyDirectives("Bundle-SymbolicName", "singleton:|fragment-attachment:|mandatory:");
+        
+        
+        verifyManifestFirst();
+        verifyActivator();
+        verifyActivationPolicy();
+        verifyComponent();
+        verifyNative();
+        verifyInvalidExports();
+        verifyInvalidImports();
+        verifyUnresolvedReferences();
+        verifySymbolicName();
+        verifyListHeader("Bundle-RequiredExecutionEnvironment", EENAME, false);
+        verifyHeader("Bundle-ManifestVersion", BUNDLEMANIFESTVERSION, false);
+        verifyHeader("Bundle-Version", VERSION, true);
+        verifyListHeader("Bundle-Classpath", FILE, false);
+        verifyDynamicImportPackage();
+        verifyBundleClasspath();
+        verifyUses();
+        if (usesRequire) {
+            if (!getErrors().isEmpty()) {
+                getWarnings()
+                        .add(
+                                0,
+                                "Bundle uses Require Bundle, this can generate false errors because then not enough information is available without the required bundles");
+            }
+        }
+    }
+
+    /**
+     * Verify if the header does not contain any other directives
+     * 
+     * @param header
+     * @param directives
+     */
+    private void verifyDirectives(String header, String directives) {
+    	Pattern pattern = Pattern.compile(directives);
+    	Map<String,Map<String,String>> map = parseHeader(manifest.getMainAttributes().getValue(header));
+    	for ( Map.Entry<String, Map<String,String>> entry : map.entrySet()) {
+    		for ( String key : entry.getValue().keySet()) {
+    			if ( key.endsWith(":")) {
+    				if ( ! key.startsWith("x-")) {
+    					Matcher m = pattern.matcher(key);
+    					if ( m.matches())
+    						continue;
+    					
+    					warning("Unknown directive %s in %s, allowed directives are %s, and 'x-*'.", key, header, directives.replace('|', ','));
+    				}
+    			}
+    		}
+    	}
+	}
+
+	/**
+     * Verify the use clauses
+     */
+    private void verifyUses() {
+    }
+
+    public boolean verifyActivationPolicy() {
+        String policy = getHeader(Constants.BUNDLE_ACTIVATIONPOLICY);
+        if (policy == null)
+            return true;
+
+        return verifyActivationPolicy(policy);
+    }
+
+    public boolean verifyActivationPolicy(String policy) {
+        Map<String, Map<String, String>> map = parseHeader(policy);
+        if (map.size() == 0)
+            warning("Bundle-ActivationPolicy is set but has no argument %s",
+                    policy);
+        else if (map.size() > 1)
+            warning("Bundle-ActivationPolicy has too many arguments %s", policy);
+        else {
+            Map<String, String> s = map.get("lazy");
+            if (s == null)
+                warning(
+                        "Bundle-ActivationPolicy set but is not set to lazy: %s",
+                        policy);
+            else
+                return true;
+        }
+
+        return false;
+    }
+
+    public void verifyBundleClasspath() {
+        Map<String, Map<String, String>> bcp = parseHeader(getHeader(Analyzer.BUNDLE_CLASSPATH));
+        if (bcp.isEmpty() || bcp.containsKey("."))
+            return;
+
+        for ( String path : bcp.keySet() ) {
+            if ( path.endsWith("/"))
+                error("A Bundle-ClassPath entry must not end with '/': %s", path);
+            
+            if ( dot.getDirectories().containsKey(path))
+                // We assume that any classes are in a directory
+                // and therefore do not care when the bundle is included
+                return;
+        }
+        
+        for (String path : dot.getResources().keySet()) {
+            if (path.endsWith(".class")) {
+                warning("The Bundle-Classpath does not contain the actual bundle JAR (as specified with '.' in the Bundle-Classpath) but the JAR does contain classes. Is this intentional?");
+                return;
+            }
+        }
+    }
+
+    /**
+     * <pre>
+     *          DynamicImport-Package ::= dynamic-description
+     *              ( ',' dynamic-description )*
+     *              
+     *          dynamic-description::= wildcard-names ( ';' parameter )*
+     *          wildcard-names ::= wildcard-name ( ';' wildcard-name )*
+     *          wildcard-name ::= package-name 
+     *                         | ( package-name '.*' ) // See 1.4.2
+     *                         | '*'
+     * </pre>
+     */
+    private void verifyDynamicImportPackage() {
+        verifyListHeader("DynamicImport-Package", WILDCARDPACKAGE, true);
+        String dynamicImportPackage = getHeader("DynamicImport-Package");
+        if (dynamicImportPackage == null)
+            return;
+
+        Map<String, Map<String, String>> map = parseHeader(dynamicImportPackage);
+        for (String name : map.keySet()) {
+            name = name.trim();
+            if (!verify(name, WILDCARDPACKAGE))
+                error("DynamicImport-Package header contains an invalid package name: "
+                        + name);
+
+            Map<String, String> sub = map.get(name);
+            if (r3 && sub.size() != 0) {
+                error("DynamicPackage-Import has attributes on import: "
+                        + name
+                        + ". This is however, an <=R3 bundle and attributes on this header were introduced in R4. ");
+            }
+        }
+    }
+
+    private void verifyManifestFirst() {
+        if (!dot.manifestFirst) {
+            error("Invalid JAR stream: Manifest should come first to be compatible with JarInputStream, it was not");
+        }
+    }
+
+    private void verifySymbolicName() {
+        Map<String, Map<String, String>> bsn = parseHeader(getHeader(Analyzer.BUNDLE_SYMBOLICNAME));
+        if (!bsn.isEmpty()) {
+            if (bsn.size() > 1)
+                error("More than one BSN specified " + bsn);
+
+            String name = (String) bsn.keySet().iterator().next();
+            if (!SYMBOLICNAME.matcher(name).matches()) {
+                error("Symbolic Name has invalid format: " + name);
+            }
+        }
+    }
+
+    /**
+     * <pre>
+     *         filter ::= ’(’ filter-comp ’)’
+     *         filter-comp ::= and | or | not | operation
+     *         and ::= ’&amp;’ filter-list
+     *         or ::= ’|’ filter-list
+     *         not ::= ’!’ filter
+     *         filter-list ::= filter | filter filter-list
+     *         operation ::= simple | present | substring
+     *         simple ::= attr filter-type value
+     *         filter-type ::= equal | approx | greater | less
+     *         equal ::= ’=’
+     *         approx ::= ’&tilde;=’
+     *         greater ::= ’&gt;=’
+     *         less ::= ’&lt;=’
+     *         present ::= attr ’=*’
+     *         substring ::= attr ’=’ initial any final
+     *         inital ::= () | value
+     *         any ::= ’*’ star-value
+     *         star-value ::= () | value ’*’ star-value
+     *         final ::= () | value
+     *         value ::= &lt;see text&gt;
+     * </pre>
+     * 
+     * @param expr
+     * @param index
+     * @return
+     */
+
+    public static int verifyFilter(String expr, int index) {
+        try {
+            while (Character.isWhitespace(expr.charAt(index)))
+                index++;
+
+            if (expr.charAt(index) != '(')
+                throw new IllegalArgumentException(
+                        "Filter mismatch: expected ( at position " + index
+                                + " : " + expr);
+
+            index++; // skip (
+
+            while (Character.isWhitespace(expr.charAt(index)))
+                index++;
+
+            switch (expr.charAt(index)) {
+            case '!':
+                index++; // skip !
+                while (Character.isWhitespace(expr.charAt(index)))
+                    index++;
+
+                if (expr.charAt(index) != '(')
+                    throw new IllegalArgumentException(
+                            "Filter mismatch: ! (not) must have one sub expression "
+                                    + index + " : " + expr);
+                while (Character.isWhitespace(expr.charAt(index)))
+                    index++;
+
+                index = verifyFilter(expr, index);
+                while (Character.isWhitespace(expr.charAt(index)))
+                    index++;
+                if (expr.charAt(index) != ')')
+                    throw new IllegalArgumentException(
+                            "Filter mismatch: expected ) at position " + index
+                                    + " : " + expr);
+                return index + 1;
+
+            case '&':
+            case '|':
+                index++; // skip operator
+                while (Character.isWhitespace(expr.charAt(index)))
+                    index++;
+                while (expr.charAt(index) == '(') {
+                    index = verifyFilter(expr, index);
+                    while (Character.isWhitespace(expr.charAt(index)))
+                        index++;
+                }
+
+                if (expr.charAt(index) != ')')
+                    throw new IllegalArgumentException(
+                            "Filter mismatch: expected ) at position " + index
+                                    + " : " + expr);
+                return index + 1; // skip )
+
+            default:
+                index = verifyFilterOperation(expr, index);
+                if (expr.charAt(index) != ')')
+                    throw new IllegalArgumentException(
+                            "Filter mismatch: expected ) at position " + index
+                                    + " : " + expr);
+                return index + 1;
+            }
+        } catch (IndexOutOfBoundsException e) {
+            throw new IllegalArgumentException(
+                    "Filter mismatch: early EOF from " + index);
+        }
+    }
+
+    static private int verifyFilterOperation(String expr, int index) {
+        StringBuffer sb = new StringBuffer();
+        while ("=><~()".indexOf(expr.charAt(index)) < 0) {
+            sb.append(expr.charAt(index++));
+        }
+        String attr = sb.toString().trim();
+        if (attr.length() == 0)
+            throw new IllegalArgumentException(
+                    "Filter mismatch: attr at index " + index + " is 0");
+        sb = new StringBuffer();
+        while ("=><~".indexOf(expr.charAt(index)) >= 0) {
+            sb.append(expr.charAt(index++));
+        }
+        String operator = sb.toString();
+        if (!verify(operator, FILTEROP))
+            throw new IllegalArgumentException(
+                    "Filter error, illegal operator " + operator + " at index "
+                            + index);
+
+        sb = new StringBuffer();
+        while (")".indexOf(expr.charAt(index)) < 0) {
+            switch (expr.charAt(index)) {
+            case '\\':
+                if ("\\)(*".indexOf(expr.charAt(index + 1)) >= 0 )
+                    index++;
+                else
+                    throw new IllegalArgumentException(
+                            "Filter error, illegal use of backslash at index "
+                                    + index
+                                    + ". Backslash may only be used before * or () or \\");
+            }
+            sb.append(expr.charAt(index++));
+        }
+        return index;
+    }
+
+    private String getHeader(String string) {
+        return main.getValue(string);
+    }
+
+    private boolean verifyHeader(String name, Pattern regex, boolean error) {
+        String value = manifest.getMainAttributes().getValue(name);
+        if (value == null)
+            return false;
+
+        QuotedTokenizer st = new QuotedTokenizer(value.trim(), ",");
+        for (Iterator<String> i = st.getTokenSet().iterator(); i.hasNext();) {
+            if (!verify((String) i.next(), regex)) {
+                String msg = "Invalid value for " + name + ", " + value
+                        + " does not match " + regex.pattern();
+                if (error)
+                    error(msg);
+                else
+                    warning(msg);
+            }
+        }
+        return true;
+    }
+
+    static private boolean verify(String value, Pattern regex) {
+        return regex.matcher(value).matches();
+    }
+
+    private boolean verifyListHeader(String name, Pattern regex, boolean error) {
+        String value = manifest.getMainAttributes().getValue(name);
+        if (value == null)
+            return false;
+
+        Map<String, Map<String, String>> map = parseHeader(value);
+        for (String header : map.keySet()) {
+            if (!regex.matcher(header).matches()) {
+                String msg = "Invalid value for " + name + ", " + value
+                        + " does not match " + regex.pattern();
+                if (error)
+                    error(msg);
+                else
+                    warning(msg);
+            }
+        }
+        return true;
+    }
+
+    public String getProperty(String key, String deflt) {
+        if (properties == null)
+            return deflt;
+        return properties.getProperty(key, deflt);
+    }
+
+    public void setClassSpace(Map<String, Clazz> classspace,
+            Map<String, Map<String, String>> contained,
+            Map<String, Map<String, String>> referred,
+            Map<String, Set<String>> uses) {
+        this.classSpace = classspace;
+        this.contained = contained;
+        this.referred = referred;
+        this.uses = uses;
+    }
+
+    public static boolean isVersion(String version) {
+        return VERSION.matcher(version).matches();
+    }
+
+    public static boolean isIdentifier(String value) {
+        if (value.length() < 1)
+            return false;
+
+        if (!Character.isJavaIdentifierStart(value.charAt(0)))
+            return false;
+
+        for (int i = 1; i < value.length(); i++) {
+            if (!Character.isJavaIdentifierPart(value.charAt(i)))
+                return false;
+        }
+        return true;
+    }
+
+    public static boolean isMember(String value, String[] matches) {
+        for (String match : matches) {
+            if (match.equals(value))
+                return true;
+        }
+        return false;
+    }
+
+	public static boolean isFQN(String name) {
+		if ( name.length() == 0)
+			return false;
+		if ( !Character.isJavaIdentifierStart(name.charAt(0)))
+			return false;
+		
+		for ( int i=1; i<name.length(); i++) {
+			char c = name.charAt(i);
+			if (Character.isJavaIdentifierPart(c) || c == '$' || c == '.')
+				continue;
+			
+			return false;
+		}
+		
+		return true;
+	}
+
+    /*
+     * public int verifyFilter(StringBuffer sb, String s, int rover) { rover =
+     * skip(s, rover); char c = s.charAt(rover); if (c == '(') { sb.append('(');
+     * char type; rover = skip(s, ++rover); c = s.charAt(rover); switch (c) {
+     * case '!': // not case '&': // and case '|': // or sb.append(c); type = c;
+     * while(true) { rover = skip(++rover); c = s.charAt(rover); if ( c != '(')
+     * break; rover = verifyFilter(s, rover); } break;
+     * 
+     * case ')': return rover + 1;
+     * 
+     * default: rover = skip(s,rover); c = s.charAt(rover); while (
+     * Character.isLetterOrDigit(c) || ) } } }
+     */
+}

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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/WriteResource.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/WriteResource.java?rev=1185095&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/WriteResource.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/WriteResource.java Mon Oct 17 10:31:43 2011
@@ -0,0 +1,42 @@
+package aQute.lib.osgi;
+
+import java.io.*;
+
+public abstract class WriteResource implements Resource {
+	long 	lastModified;
+	String	extra;
+
+	public InputStream openInputStream() throws Exception {
+	    PipedInputStream pin = new PipedInputStream();
+	    final PipedOutputStream pout = new PipedOutputStream(pin);
+	    Thread t = new Thread() {
+	        public void run() {
+	            try {
+                    write(pout);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                } finally {
+                    try {
+                        pout.close();
+                    } catch (IOException e) {
+                        // Ignore
+                    }
+                }
+	        }
+	    };
+	    t.start();
+	    return pin;
+	}
+
+	public abstract void write(OutputStream out) throws IOException, Exception;
+	
+	public abstract long lastModified();
+
+	public String getExtra() {
+		return extra;
+	}
+
+	public void setExtra(String extra) {
+		this.extra = extra;
+	}
+}

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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/ZipResource.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/ZipResource.java?rev=1185095&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/ZipResource.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/ZipResource.java Mon Oct 17 10:31:43 2011
@@ -0,0 +1,84 @@
+package aQute.lib.osgi;
+
+import java.io.*;
+import java.util.*;
+import java.util.regex.*;
+import java.util.zip.*;
+
+public class ZipResource implements Resource {
+    ZipFile  zip;
+    ZipEntry entry;
+    long     lastModified;
+    String   extra;
+
+    ZipResource(ZipFile zip, ZipEntry entry, long lastModified) {
+        this.zip = zip;
+        this.entry = entry;
+        this.lastModified = lastModified;
+        byte[] data = entry.getExtra();
+        if (data != null)
+            this.extra = new String(data);
+    }
+
+    public InputStream openInputStream() throws IOException {
+        return zip.getInputStream(entry);
+    }
+
+    public String toString() {
+        return ":" + zip.getName() + "(" + entry.getName() + "):";
+    }
+
+    public static ZipFile build(Jar jar, File file) throws ZipException,
+            IOException {
+        return build(jar, file, null);
+    }
+
+    public static ZipFile build(Jar jar, File file, Pattern pattern)
+            throws ZipException, IOException {
+
+        try {
+            ZipFile zip = new ZipFile(file);
+            nextEntry: for (Enumeration<? extends ZipEntry> e = zip.entries(); e
+                    .hasMoreElements();) {
+                ZipEntry entry = e.nextElement();
+                if (pattern != null) {
+                    Matcher m = pattern.matcher(entry.getName());
+                    if (!m.matches())
+                        continue nextEntry;
+                }
+                if (!entry.isDirectory()) {
+                    long time = entry.getTime();
+                    if (time <= 0)
+                        time = file.lastModified();
+                    jar.putResource(entry.getName(), new ZipResource(zip,
+                            entry, time), true);
+                }
+            }
+            return zip;
+        } catch (ZipException ze) {
+            throw new ZipException("The JAR/ZIP file ("
+                    + file.getAbsolutePath() + ") seems corrupted, error: "
+                    + ze.getMessage());
+        } catch (FileNotFoundException e) {
+            throw new IllegalArgumentException("Problem opening JAR: "
+                    + file.getAbsolutePath());
+        }
+    }
+
+    public void write(OutputStream out) throws Exception {
+        FileResource.copy(this, out);
+    }
+
+    public long lastModified() {
+        return lastModified;
+    }
+
+    public String getExtra() {
+        return extra;
+    }
+
+    public void setExtra(String extra) {
+        this.extra = extra;
+    }
+
+}

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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/eclipse/EclipseClasspath.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/eclipse/EclipseClasspath.java?rev=1185095&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/eclipse/EclipseClasspath.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/eclipse/EclipseClasspath.java Mon Oct 17 10:31:43 2011
@@ -0,0 +1,248 @@
+package aQute.lib.osgi.eclipse;
+
+import java.io.*;
+import java.util.*;
+import java.util.regex.*;
+
+import javax.xml.parsers.*;
+
+import org.w3c.dom.*;
+import org.xml.sax.*;
+
+import aQute.libg.reporter.*;
+
+/**
+ * Parse the Eclipse project information for the classpath. Unfortunately, it is
+ * impossible to read the variables. They are ignored but that can cause
+ * problems.
+ * 
+ * @version $Revision$
+ */
+public class EclipseClasspath {
+    static DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory
+                                                                 .newInstance();
+    DocumentBuilder               db;
+    File                          project;
+    File                          workspace;
+    Set<File>                     sources                = new LinkedHashSet<File>();
+    Set<File>                     allSources                = new LinkedHashSet<File>();
+    
+    Set<File>                     classpath              = new LinkedHashSet<File>();
+    List<File>                    dependents             = new ArrayList<File>();
+    File                          output;
+    boolean                       recurse                = true;
+    Set<File>                     exports                = new LinkedHashSet<File>();
+    Map<String, String>           properties             = new HashMap<String, String>();
+    Reporter                      reporter;
+    int                           options;
+    Set<File>                     bootclasspath          = new LinkedHashSet<File>();
+
+    public final static int       DO_VARIABLES           = 1;
+
+    /**
+     * Parse an Eclipse project structure to discover the classpath.
+     * 
+     * @param workspace
+     *            Points to workspace
+     * @param project
+     *            Points to project
+     * @throws ParserConfigurationException
+     * @throws SAXException
+     * @throws IOException
+     */
+
+    public EclipseClasspath(Reporter reporter, File workspace, File project,
+            int options) throws Exception {
+        this.project = project.getCanonicalFile();
+        this.workspace = workspace.getCanonicalFile();
+        this.reporter = reporter;
+        db = documentBuilderFactory.newDocumentBuilder();
+        parse(this.project, true);
+        db = null;
+    }
+
+    public EclipseClasspath(Reporter reporter, File workspace, File project)
+            throws Exception {
+        this(reporter, workspace, project, 0);
+    }
+
+    /**
+     * Recursive routine to parse the files. If a sub project is detected, it is
+     * parsed before the parsing continues. This should give the right order.
+     * 
+     * @param project
+     *            Project directory
+     * @param top
+     *            If this is the top project
+     * @throws ParserConfigurationException
+     * @throws SAXException
+     * @throws IOException
+     */
+    void parse(File project, boolean top) throws ParserConfigurationException,
+            SAXException, IOException {
+        File file = new File(project, ".classpath");
+        if (!file.exists())
+            throw new FileNotFoundException(".classpath file not found: "
+                    + file.getAbsolutePath());
+
+        Document doc = db.parse(file);
+        NodeList nodelist = doc.getDocumentElement().getElementsByTagName(
+                "classpathentry");
+
+        if (nodelist == null)
+            throw new IllegalArgumentException(
+                    "Can not find classpathentry in classpath file");
+
+        for (int i = 0; i < nodelist.getLength(); i++) {
+            Node node = nodelist.item(i);
+            NamedNodeMap attrs = node.getAttributes();
+            String kind = get(attrs, "kind");
+            if ("src".equals(kind)) {
+                String path = get(attrs, "path");
+                // TODO boolean exported = "true".equalsIgnoreCase(get(attrs,
+                // "exported"));
+                if (path.startsWith("/")) {
+                    // We have another project
+                    File subProject = getFile(workspace, project, path);
+                    if (recurse)
+                        parse(subProject, false);
+                    dependents.add(subProject.getCanonicalFile());
+                } else {
+                    File src = getFile(workspace, project, path);
+                    allSources.add(src);
+                    if (top) {
+                        // We only want the sources for our own project
+                        // or we'll compile all at once. Not a good idea
+                        // because project settings can differ.
+                        sources.add(src);
+                    }
+                }
+            } else if ("lib".equals(kind)) {
+                String path = get(attrs, "path");
+                boolean exported = "true".equalsIgnoreCase(get(attrs,
+                        "exported"));
+                if (top || exported) {
+                    File jar = getFile(workspace, project, path);
+                    if (jar.getName().startsWith("ee."))
+                        bootclasspath.add(jar);
+                    else
+                        classpath.add(jar);
+                    if (exported)
+                        exports.add(jar);
+                }
+            } else if ("output".equals(kind)) {
+                String path = get(attrs, "path");
+                path = path.replace('/', File.separatorChar);
+                output = getFile(workspace, project, path);
+                classpath.add(output);
+                exports.add(output);
+            } else if ("var".equals(kind)) {
+                boolean exported = "true".equalsIgnoreCase(get(attrs,
+                        "exported"));
+                File lib = replaceVar(get(attrs, "path"));
+                File slib = replaceVar(get(attrs, "sourcepath"));
+                if (lib != null) {
+                    classpath.add(lib);
+                    if (exported)
+                        exports.add(lib);
+                }
+                if (slib != null)
+                    sources.add(slib);
+            } else if ("con".equals(kind)) {
+                // Should do something useful ...
+            }
+        }
+    }
+
+    private File getFile(File abs, File relative, String opath) {
+        String path = opath.replace('/', File.separatorChar);
+        File result = new File(path);
+        if (result.isAbsolute() && result.isFile()) {
+            return result;
+        }
+        if (path.startsWith(File.separator)) {
+            result = abs;
+            path = path.substring(1);
+        } else
+            result = relative;
+
+        StringTokenizer st = new StringTokenizer(path, File.separator);
+        while (st.hasMoreTokens()) {
+            String token = st.nextToken();
+            result = new File(result, token);
+        }
+
+        if (!result.exists())
+            System.err.println("File not found: project=" + project
+                    + " workspace=" + workspace + " path=" + opath + " file="
+                    + result);
+        return result;
+    }
+
+    static Pattern PATH = Pattern.compile("([A-Z_]+)/(.*)");
+
+    private File replaceVar(String path) {
+        if ((options & DO_VARIABLES) == 0)
+            return null;
+
+        Matcher m = PATH.matcher(path);
+        if (m.matches()) {
+            String var = m.group(1);
+            String remainder = m.group(2);
+            String base = (String) properties.get(var);
+            if (base != null) {
+                File b = new File(base);
+                File f = new File(b, remainder.replace('/', File.separatorChar));
+                return f;
+            } else
+                reporter.error("Can't find replacement variable for: " + path);
+        } else
+            reporter.error("Cant split variable path: " + path);
+        return null;
+    }
+
+    private String get(NamedNodeMap map, String name) {
+        Node node = map.getNamedItem(name);
+        if (node == null)
+            return null;
+
+        return node.getNodeValue();
+    }
+
+    public Set<File> getClasspath() {
+        return classpath;
+    }
+
+    public Set<File> getSourcepath() {
+        return sources;
+    }
+
+    public File getOutput() {
+        return output;
+    }
+
+    public List<File> getDependents() {
+        return dependents;
+    }
+
+    public void setRecurse(boolean recurse) {
+        this.recurse = recurse;
+    }
+
+    public Set<File> getExports() {
+        return exports;
+    }
+
+    public void setProperties(Map<String, String> map) {
+        this.properties = map;
+    }
+
+    public Set<File> getBootclasspath() {
+        return bootclasspath;
+    }
+
+    public Set<File> getAllSources() {
+        return allSources;
+    }
+
+}

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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/packageinfo
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/packageinfo?rev=1185095&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/packageinfo (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/packageinfo Mon Oct 17 10:31:43 2011
@@ -0,0 +1 @@
+version 1.43.1

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/putjar/DirectoryInputStream.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/putjar/DirectoryInputStream.java?rev=1185095&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/putjar/DirectoryInputStream.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/putjar/DirectoryInputStream.java Mon Oct 17 10:31:43 2011
@@ -0,0 +1,281 @@
+package aQute.lib.putjar;
+
+import java.io.*;
+import java.util.zip.*;
+
+import aQute.libg.fileiterator.*;
+
+public class DirectoryInputStream extends InputStream {
+    final File               root;
+    final FileIterator       fi;
+    File                     element;
+    int                      entries   = 0;
+    int                      state     = START;
+    long                     where     = 0;
+
+    final static int         START     = 0;
+    final static int         HEADER    = 1;
+    final static int         DATA      = 2;
+    final static int         DIRECTORY = 4;
+    final static int         EOF       = 5;
+
+    final static InputStream eof       = new ByteArrayInputStream(new byte[0]);
+    ByteArrayOutputStream    directory = new ByteArrayOutputStream();
+    InputStream              current   = eof;
+
+    public DirectoryInputStream(File dir) {
+        root = dir;
+        fi = new FileIterator(dir);
+    }
+
+    @Override
+    public int read() throws IOException {
+        if (fi == null)
+            return -1;
+
+        int c = current.read();
+        if (c < 0) {
+            next();
+            c = current.read();
+        }
+        if (c >= 0)
+            where++;
+
+        return c;
+    }
+
+    void next() throws IOException {
+        switch (state) {
+        case START:
+        case DATA:
+            nextHeader();
+            break;
+
+        case HEADER:
+            if (element.isFile() && element.length() > 0) {
+                current = new FileInputStream(element);
+                state = DATA;
+            } else
+                nextHeader();
+            break;
+
+        case DIRECTORY:
+            state = EOF;
+            current = eof;
+            break;
+
+        case EOF:
+            break;
+        }
+    }
+
+    private void nextHeader() throws IOException {
+        if (fi.hasNext()) {
+            element = fi.next();
+            state = HEADER;
+            current = getHeader(root, element);
+            entries++;
+        } else {
+            current = getDirectory();
+            state = DIRECTORY;
+        }
+    }
+
+    /**
+     * <pre>
+     *     end of central dir signature    4 bytes  (0x06054b50)
+     *         number of this disk             2 bytes
+     *         number of the disk with the
+     *         start of the central directory  2 bytes
+     *         total number of entries in the
+     *         central directory on this disk  2 bytes
+     *         total number of entries in
+     *         the central directory           2 bytes
+     *         size of the central directory   4 bytes
+     *         offset of start of central
+     *         directory with respect to
+     *         the starting disk number        4 bytes
+     *         .ZIP file comment length        2 bytes
+     *         .ZIP file comment       (variable size)
+     * </pre>
+     * 
+     * @return
+     */
+    InputStream getDirectory() throws IOException {
+        long where = this.where;
+        int sizeDirectory = directory.size();
+
+        writeInt(directory, 0x504b0506); // Signature
+        writeShort(directory, 0); // # of disk
+        writeShort(directory, 0); // # of the disk with start of the central
+        // dir
+        writeShort(directory, entries); // # of entries
+        writeInt(directory, sizeDirectory); // Size of central dir
+        writeInt(directory, (int) where);
+        writeShort(directory, 0);
+
+        directory.close();
+
+        byte[] data = directory.toByteArray();
+        return new ByteArrayInputStream(data);
+    }
+
+    private void writeShort(OutputStream out, int v) throws IOException {
+        for (int i = 0; i < 2; i++) {
+            out.write((byte) (v & 0xFF));
+            v = v >> 8;
+        }
+    }
+
+    private void writeInt(OutputStream out, int v) throws IOException {
+        for (int i = 0; i < 4; i++) {
+            out.write((byte) (v & 0xFF));
+            v = v >> 8;
+        }
+    }
+
+    /**
+     * Local file header:
+     * 
+     * <pre>
+     * 
+     *         local file header signature     4 bytes  (0x04034b50)
+     *         version needed to extract       2 bytes
+     *         general purpose bit flag        2 bytes
+     *         compression method              2 bytes
+     *         last mod file time              2 bytes
+     *         last mod file date              2 bytes
+     *         crc-32                          4 bytes
+     *         compressed size                 4 bytes
+     *         uncompressed size               4 bytes
+     *         file name length                2 bytes
+     *         extra field length              2 bytes
+     * 
+     *         file name (variable size)
+     *         extra field (variable size)
+     * 
+     *     central file header signature   4 bytes  (0x02014b50)
+     *         version made by                 2 bytes
+     *         version needed to extract       2 bytes
+     *         general purpose bit flag        2 bytes
+     *         compression method              2 bytes
+     *         last mod file time              2 bytes
+     *         last mod file date              2 bytes
+     *         crc-32                          4 bytes
+     *         compressed size                 4 bytes
+     *         uncompressed size               4 bytes
+     *         file name length                2 bytes
+     *         extra field length              2 bytes
+     *         file comment length             2 bytes
+     *         disk number start               2 bytes
+     *         internal file attributes        2 bytes
+     *         external file attributes        4 bytes
+     *         relative offset of local header 4 bytes
+     * 
+     *         file name (variable size)
+     *         extra field (variable size)
+     *         file comment (variable size)
+     * </pre>
+     * </pre>
+     * 
+     * @param file
+     * @return
+     */
+    private InputStream getHeader(File root, File file) throws IOException {
+        long where = this.where;
+        ByteArrayOutputStream bout = new ByteArrayOutputStream();
+        // Signature
+        writeInt(bout, 0x04034b50);
+        writeInt(directory, 0x504b0102);
+
+        // Version needed to extract
+        writeShort(directory, 0);
+
+        // Version needed to extract
+        writeShort(bout, 10);
+        writeShort(directory, 10);
+
+        // General purpose bit flag (use descriptor)
+        writeShort(bout, 0); // descriptor follows data
+        writeShort(directory, 0); // descriptor follows data
+
+        // Compresson method (stored)
+        writeShort(bout, 0);
+        writeShort(directory, 0);
+
+        // Mod time
+        writeInt(bout, 0);
+        writeInt(directory, 0);
+
+        if (file.isDirectory()) {
+            writeInt(bout, 0); // CRC
+            writeInt(bout, 0); // Compressed size
+            writeInt(bout, 0); // Uncompressed Size
+            writeInt(directory, 0);
+            writeInt(directory, 0);
+            writeInt(directory, 0);
+        } else {
+            CRC32 crc = getCRC(file);
+            writeInt(bout, (int) crc.getValue());
+            writeInt(bout, (int) file.length());
+            writeInt(bout, (int) file.length());
+            writeInt(directory, (int) crc.getValue());
+            writeInt(directory, (int) file.length());
+            writeInt(directory, (int) file.length());
+        }
+
+        String p = getPath(root, file);
+        if (file.isDirectory())
+            p = p + "/";
+        byte[] path = p.getBytes("UTF-8");
+        writeShort(bout, path.length);
+        writeShort(directory, path.length);
+
+        writeShort(bout, 0); // extra length
+        writeShort(directory, 0);
+
+        bout.write(path);
+
+        writeShort(directory, 0); // File comment length
+        writeShort(directory, 0); // disk number start 2 bytes
+        writeShort(directory, 0); // internal file attributes 2 bytes
+        writeInt(directory, 0); // external file attributes 4 bytes
+        writeInt(directory, (int) where); // relative offset of local header 4
+        // bytes
+
+        directory.write(path);
+
+        byte[] bytes = bout.toByteArray();
+        return new ByteArrayInputStream(bytes);
+    }
+
+    private String getPath(File root, File file) {
+        if (file.equals(root))
+            return "";
+
+        String p = getPath(root, file.getParentFile());
+        if (p.length() == 0)
+            p = file.getName();
+        else {
+            p = p + "/" + file.getName();
+        }
+        return p;
+    }
+
+    private CRC32 getCRC(File file) throws IOException {
+        CRC32 crc = new CRC32();
+        FileInputStream in = new FileInputStream(file);
+        try {
+            byte data[] = new byte[10000];
+            int size = in.read(data);
+            while (size > 0) {
+                crc.update(data, 0, size);
+                size = in.read(data);
+            }
+        } finally {
+            in.close();
+        }
+        return crc;
+    }
+
+}

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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/putjar/packageinfo
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/putjar/packageinfo?rev=1185095&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/putjar/packageinfo (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/putjar/packageinfo Mon Oct 17 10:31:43 2011
@@ -0,0 +1 @@
+version 1.0

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/spring/JPAComponent.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/spring/JPAComponent.java?rev=1185095&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/spring/JPAComponent.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/spring/JPAComponent.java Mon Oct 17 10:31:43 2011
@@ -0,0 +1,25 @@
+package aQute.lib.spring;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import aQute.lib.osgi.Analyzer;
+
+/**
+ * This component is called when we find a resource in the META-INF/*.xml
+ * pattern. We parse the resource and and the imports to the builder.
+ * 
+ * Parsing is done with XSLT (first time I see the use of having XML for the
+ * Spring configuration files!).
+ * 
+ * @author aqute
+ * 
+ */
+public class JPAComponent extends XMLTypeProcessor {
+    
+    protected List<XMLType> getTypes(Analyzer analyzer) throws Exception {
+        List<XMLType> types = new ArrayList<XMLType>();        
+        process(types,"jpa.xsl", "META-INF", "persistence.xml");
+        return types;
+    }
+}

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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/spring/SpringComponent.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/spring/SpringComponent.java?rev=1185095&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/spring/SpringComponent.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/spring/SpringComponent.java Mon Oct 17 10:31:43 2011
@@ -0,0 +1,97 @@
+package aQute.lib.spring;
+
+import java.io.*;
+import java.util.*;
+import java.util.regex.*;
+
+import javax.xml.transform.*;
+import javax.xml.transform.stream.*;
+
+import aQute.bnd.service.*;
+import aQute.lib.osgi.*;
+
+/**
+ * This component is called when we find a resource in the META-INF/*.xml
+ * pattern. We parse the resource and and the imports to the builder.
+ * 
+ * Parsing is done with XSLT (first time I see the use of having XML for the
+ * Spring configuration files!).
+ * 
+ * @author aqute
+ * 
+ */
+public class SpringComponent implements AnalyzerPlugin {
+	static Transformer transformer;
+	static Pattern SPRING_SOURCE = Pattern.compile("META-INF/spring/.*\\.xml");
+	static Pattern QN = Pattern.compile("[_A-Za-z$][_A-Za-z0-9$]*(\\.[_A-Za-z$][_A-Za-z0-9$]*)*");
+
+	public static Set<CharSequence> analyze(InputStream in) throws Exception {
+		if (transformer == null) {
+			TransformerFactory tf = TransformerFactory.newInstance();
+			Source source = new StreamSource(SpringComponent.class
+					.getResourceAsStream("extract.xsl"));
+			transformer = tf.newTransformer(source);
+		}
+
+		Set<CharSequence> refers = new HashSet<CharSequence>();
+
+		ByteArrayOutputStream bout = new ByteArrayOutputStream();
+		Result r = new StreamResult(bout);
+		Source s = new StreamSource(in);
+		transformer.transform(s, r);
+
+		ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
+		bout.close();
+
+		BufferedReader br = new BufferedReader(new InputStreamReader(bin, "UTF8"));
+
+		String line = br.readLine();
+		while (line != null) {
+			line = line.trim();
+			if (line.length() > 0) {
+				String parts[] = line.split("\\s*,\\s*");
+				for (int i = 0; i < parts.length; i++) {
+					int n = parts[i].lastIndexOf('.');
+					if (n > 0) {
+						refers.add(parts[i].subSequence(0, n));
+					}
+				}
+			}
+			line = br.readLine();
+		}
+		br.close();
+		return refers;
+	}
+
+	@SuppressWarnings("unchecked")
+    public boolean analyzeJar(Analyzer analyzer) throws Exception {
+	    Jar jar = analyzer.getJar();
+		Map dir = (Map) jar.getDirectories().get("META-INF/spring");
+		if ( dir == null || dir.isEmpty())
+			return false;
+		
+		for (Iterator i = dir.entrySet().iterator(); i.hasNext();) {
+			Map.Entry entry = (Map.Entry) i.next();
+			String path = (String) entry.getKey();
+			Resource resource = (Resource) entry.getValue();
+			if (SPRING_SOURCE.matcher(path).matches()) {
+				try {
+				InputStream in = resource.openInputStream();
+				Set set = analyze(in);
+				in.close();
+				for (Iterator r = set.iterator(); r.hasNext();) {
+					String pack = (String) r.next();
+					if ( !QN.matcher(pack).matches())
+					    analyzer.warning("Package does not seem a package in spring resource ("+path+"): " + pack );
+					if (!analyzer.getReferred().containsKey(pack))
+						analyzer.getReferred().put(pack, new LinkedHashMap());
+				}
+				} catch( Exception e ) {
+					analyzer.error("Unexpected exception in processing spring resources("+path+"): " + e );
+				}
+			}
+		}
+		return false;
+	}
+
+}

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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/spring/SpringXMLType.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/spring/SpringXMLType.java?rev=1185095&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/spring/SpringXMLType.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/spring/SpringXMLType.java Mon Oct 17 10:31:43 2011
@@ -0,0 +1,33 @@
+package aQute.lib.spring;
+
+import java.util.*;
+
+import aQute.lib.osgi.*;
+
+/**
+ * This component is called when we find a resource in the META-INF/*.xml
+ * pattern. We parse the resource and and the imports to the builder.
+ * 
+ * Parsing is done with XSLT (first time I see the use of having XML for the
+ * Spring configuration files!).
+ * 
+ * @author aqute
+ * 
+ */
+public class SpringXMLType extends XMLTypeProcessor {
+
+    protected List<XMLType> getTypes(Analyzer analyzer) throws Exception {
+        List<XMLType> types = new ArrayList<XMLType>();
+        
+        String header = analyzer.getProperty("Bundle-Blueprint", "META-INF/blueprint");
+        process(types,"extract.xsl", header, ".*\\.xml");
+        header = analyzer.getProperty("Spring-Context", "META-INF/spring");
+        process(types,"extract.xsl", header, ".*\\.xml"); 
+        
+        return types;
+    }
+
+ 
+
+
+}

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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/spring/XMLType.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/spring/XMLType.java?rev=1185095&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/spring/XMLType.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/spring/XMLType.java Mon Oct 17 10:31:43 2011
@@ -0,0 +1,108 @@
+package aQute.lib.spring;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.regex.*;
+
+import javax.xml.transform.*;
+import javax.xml.transform.stream.*;
+
+import aQute.lib.osgi.*;
+
+public class XMLType {
+    
+    Transformer    transformer;
+    Pattern        paths;
+    String          root;
+    
+    
+    static Pattern QN = Pattern
+                              .compile("[_A-Za-z$][_A-Za-z0-9$]*(\\.[_A-Za-z$][_A-Za-z0-9$]*)*");
+
+    public XMLType(URL source, String root, String paths ) throws Exception {
+        transformer = getTransformer(source);
+        this.paths = Pattern.compile(paths);
+        this.root = root;
+    }
+    
+    public Set<String> analyze(InputStream in) throws Exception {
+        Set<String> refers = new HashSet<String>();
+
+        ByteArrayOutputStream bout = new ByteArrayOutputStream();
+        Result r = new StreamResult(bout);
+        Source s = new StreamSource(in);
+        transformer.transform(s, r);
+
+        ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
+        bout.close();
+
+        BufferedReader br = new BufferedReader(new InputStreamReader(bin, "UTF8"));
+
+        String line = br.readLine();
+        while (line != null) {
+            line = line.trim();
+            if (line.length() > 0) {
+                String parts[] = line.split("\\s*,\\s*");
+                for (int i = 0; i < parts.length; i++) {
+                    int n = parts[i].lastIndexOf('.');
+                    if (n > 0) {
+                        refers.add(parts[i].subSequence(0, n).toString());
+                    }
+                }
+            }
+            line = br.readLine();
+        }
+        br.close();
+        return refers;
+    }
+
+    public boolean analyzeJar(Analyzer analyzer) throws Exception {
+        Jar jar = analyzer.getJar();
+        Map<String,Resource> dir = jar.getDirectories().get(root);
+        if (dir == null || dir.isEmpty()) {
+            Resource resource  = jar.getResource(root);
+            if ( resource != null )
+                process(analyzer, root, resource);
+            return false;
+        }
+
+        for (Iterator<Map.Entry<String,Resource>> i = dir.entrySet().iterator(); i.hasNext();) {
+            Map.Entry<String,Resource> entry = i.next();
+            String path = entry.getKey();
+            Resource resource = entry.getValue();
+            if (paths.matcher(path).matches()) {
+                process(analyzer, path, resource);
+            }
+        }
+        return false;
+    }
+
+    private void process(Analyzer analyzer, String path, Resource resource) {
+        try {
+            InputStream in = resource.openInputStream();
+            Set<String> set = analyze(in);
+            in.close();
+            for (Iterator<String> r = set.iterator(); r.hasNext();) {
+                String pack = r.next();
+                if (!QN.matcher(pack).matches())
+                    analyzer
+                            .warning("Package does not seem a package in spring resource ("
+                                    + path + "): " + pack);
+                if (!analyzer.getReferred().containsKey(pack))
+                    analyzer.getReferred().put(pack,
+                            new LinkedHashMap<String,String>());
+            }
+        } catch (Exception e) {
+            analyzer
+                    .error("Unexpected exception in processing spring resources("
+                            + path + "): " + e);
+        }
+    }
+
+    protected Transformer getTransformer(java.net.URL url) throws Exception {
+        TransformerFactory tf = TransformerFactory.newInstance();
+        Source source = new StreamSource(url.openStream());
+        return tf.newTransformer(source);
+    }
+}

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

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

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/spring/XMLTypeProcessor.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/spring/XMLTypeProcessor.java?rev=1185095&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/spring/XMLTypeProcessor.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/spring/XMLTypeProcessor.java Mon Oct 17 10:31:43 2011
@@ -0,0 +1,33 @@
+package aQute.lib.spring;
+
+import java.util.*;
+
+import aQute.bnd.service.*;
+import aQute.lib.osgi.*;
+
+public class XMLTypeProcessor implements AnalyzerPlugin {
+    
+    public boolean analyzeJar(Analyzer analyzer) throws Exception {
+        List<XMLType> types = getTypes(analyzer);
+        for ( XMLType type : types ) {
+            type.analyzeJar(analyzer);
+        }
+        return false;
+    }
+    
+    protected List<XMLType> getTypes(Analyzer analyzer) throws Exception {
+        return new ArrayList<XMLType>();
+    }
+
+
+    protected void process(List<XMLType> types, String resource, String paths,
+            String pattern) throws Exception {
+        
+        Map<String,Map<String,String>> map = Processor.parseHeader(paths,null);
+        for ( String path : map.keySet() ) {
+            types.add( new XMLType( getClass().getResource(resource), path, pattern ));
+        }
+    }
+
+
+}

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

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