You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by wa...@apache.org on 2006/08/24 13:05:00 UTC

svn commit: r434371 [1/3] - in /incubator/felix/trunk: ./ tools/mangen/ tools/mangen/src/ tools/mangen/src/main/ tools/mangen/src/main/java/ tools/mangen/src/main/java/org/ tools/mangen/src/main/java/org/apache/ tools/mangen/src/main/java/org/apache/fe...

Author: walkerr
Date: Thu Aug 24 04:04:56 2006
New Revision: 434371

URL: http://svn.apache.org/viewvc?rev=434371&view=rev
Log:
[FELIX-56] Initial submission of mangen 1.0.1 from Ascert's inhouse mangen repository. At present mangen builds but will need further work for either "distribution" creation to be used as an external tool, and to assess if and how it can be integrated within Felix build.

Added:
    incubator/felix/trunk/tools/mangen/
    incubator/felix/trunk/tools/mangen/pom.xml
    incubator/felix/trunk/tools/mangen/src/
    incubator/felix/trunk/tools/mangen/src/main/
    incubator/felix/trunk/tools/mangen/src/main/java/
    incubator/felix/trunk/tools/mangen/src/main/java/org/
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/ASMClassScanner.java
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/BCELScanner.java
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/BundleJar.java
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/ClassScanner.java
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/GenericHandler.java
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/GenericHandlerItem.java
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/GenericHandlerItemImpl.java
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/MangenMain.java
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/OsgiPackage.java
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/OsgiR4Package.java
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/OsgiStringPackage.java
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/PropertyManager.java
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/Report.java
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/ReportHandler.java
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/Rule.java
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/RuleHandler.java
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/report/
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/report/BundleReport.java
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/report/ObrReport.java
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/report/RuleReport.java
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/AttributeStamp.java
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/DontImportOwnExports.java
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/GenericRule.java
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/Ignore.java
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/Merge.java
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/ProcessBundles.java
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/ResolveImportsToExports.java
    incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/UpdateBundles.java
    incubator/felix/trunk/tools/mangen/src/main/manifest/
    incubator/felix/trunk/tools/mangen/src/main/manifest/Manifest.mf
    incubator/felix/trunk/tools/mangen/src/main/resources/
    incubator/felix/trunk/tools/mangen/src/main/resources/mangen.properties
    incubator/felix/trunk/tools/mangen/src/main/resources/obr.properties
Modified:
    incubator/felix/trunk/pom.xml

Modified: incubator/felix/trunk/pom.xml
URL: http://svn.apache.org/viewvc/incubator/felix/trunk/pom.xml?rev=434371&r1=434370&r2=434371&view=diff
==============================================================================
--- incubator/felix/trunk/pom.xml (original)
+++ incubator/felix/trunk/pom.xml Thu Aug 24 04:04:56 2006
@@ -80,6 +80,8 @@
     <module>ipojo.metadata</module>
     <module>ipojo.plugin</module>
     <module>ipojo.arch</module>
+    
+    <module>tools/mangen</module>
   </modules>
 
   <repositories>

Added: incubator/felix/trunk/tools/mangen/pom.xml
URL: http://svn.apache.org/viewvc/incubator/felix/trunk/tools/mangen/pom.xml?rev=434371&view=auto
==============================================================================
--- incubator/felix/trunk/tools/mangen/pom.xml (added)
+++ incubator/felix/trunk/tools/mangen/pom.xml Thu Aug 24 04:04:56 2006
@@ -0,0 +1,57 @@
+<project>
+  <parent>
+    <groupId>org.apache.felix</groupId>
+    <artifactId>felix</artifactId>
+    <version>0.8.0-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <packaging>jar</packaging>
+  <name>Apache Felix Bundle Manifest Generator (mangen)</name>
+  <artifactId>org.apache.felix.tool.mangen</artifactId>
+  <version>1.0.1</version>
+  <dependencies>
+    <dependency>
+      <groupId>${pom.groupId}</groupId>
+      <artifactId>org.apache.felix.framework</artifactId>
+      <version>0.8.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>${pom.groupId}</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <version>0.8.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>asm</groupId>
+      <artifactId>asm</artifactId>
+      <version>2.2.1</version>
+    </dependency>
+    <dependency>
+      <groupId>bcel</groupId>
+      <artifactId>bcel</artifactId>
+      <version>5.1</version>
+    </dependency>
+  </dependencies>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifest>
+              <mainClass>org.apache.felix.tool.mangen.MangenMain</mainClass>
+              <addClasspath>true</addClasspath>
+            </manifest>
+            <manifestEntries/>
+          </archive>
+        </configuration>
+      </plugin>
+    </plugins>
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>true</filtering>
+      </resource>
+    </resources>
+  </build>
+</project>

Added: incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/ASMClassScanner.java
URL: http://svn.apache.org/viewvc/incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/ASMClassScanner.java?rev=434371&view=auto
==============================================================================
--- incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/ASMClassScanner.java (added)
+++ incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/ASMClassScanner.java Thu Aug 24 04:04:56 2006
@@ -0,0 +1,398 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.tool.mangen;
+
+import java.io.IOException;
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ *
+ * @version $Revision: 32 $
+ * @author <A HREF="mailto:heavy@ungoverned.org">Richard S. Hall</A> 
+ */
+public class ASMClassScanner implements ClassScanner, ClassVisitor, MethodVisitor
+{
+    private static final int DEFAULT_INCREMENT = 10;
+
+    private int m_fieldCount = 0;
+    private String[] m_fieldNames = new String[DEFAULT_INCREMENT];
+    private String[] m_fieldSignatures = new String[DEFAULT_INCREMENT];
+    private boolean[] m_fieldSynthFlags = new boolean[DEFAULT_INCREMENT];
+
+    private int m_methodCount = 0;
+    private String[] m_methodNames = new String[DEFAULT_INCREMENT];
+    private String[] m_methodSignatures = new String[DEFAULT_INCREMENT];
+    private boolean[] m_methodSynthFlags = new boolean[DEFAULT_INCREMENT];
+
+    private int m_classCount = 0;
+    private String[] m_classSignatures = new String[DEFAULT_INCREMENT];
+
+    public ASMClassScanner()
+    {
+    }
+
+    //
+    // Methods for ClassScanner interface.
+    //
+
+    public void scan(java.io.InputStream is, String name) throws IOException
+    {
+        ClassReader cr = new ClassReader(is);
+        //TODO: below is ASM 3.0 form, will need to use once migrate from 2.2.1
+        //cr.accept(this, ClassReader.SKIP_DEBUG + ClassReader.SKIP_FRAMES);
+        cr.accept(this, false);
+    }
+
+    public int getFieldCount()
+    {
+        return m_fieldCount;
+    }
+
+    public String getFieldName(int index)
+    {
+        return m_fieldNames[index];
+    }
+
+    public String getFieldSignature(int index)
+    {
+        return m_fieldSignatures[index];
+    }
+
+    public boolean isSyntheticField(int index)
+    {
+        return m_fieldSynthFlags[index];
+    }
+
+    public int getMethodCount()
+    {
+        return m_methodCount;
+    }
+
+    public String getMethodName(int index)
+    {
+        return m_methodNames[index];
+    }
+
+    public String getMethodSignature(int index)
+    {
+        return m_methodSignatures[index];
+    }
+
+    public boolean isSyntheticMethod(int index)
+    {
+        return m_methodSynthFlags[index];
+    }
+
+    public int getConstantClassCount()
+    {
+        return m_classCount;
+    }
+
+    public String getConstantClassSignature(int index)
+    {
+        return m_classSignatures[index];
+    }
+
+    //
+    // Methods for ClassVisitor interface.
+    //
+
+    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces)
+    {
+        //System.out.println("visit: " + name + " : " + signature + " : " + superName);
+        // Capture super type dependency.
+        addConstantClass(superName);
+        // Capture implemented interface type dependencies.
+        for (int i = 0; (interfaces != null) && (i < interfaces.length); i++)
+        {
+            //System.out.println("visit interfaces: " + interfaces[i]);
+            addConstantClass(interfaces[i]);
+        }
+        // Capture class type itself, since it depends on itself.
+        addConstantClass(name);
+    }
+
+    public void visitAttribute(org.objectweb.asm.Attribute attr)
+    {
+    }
+
+    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value)
+    {
+        //System.out.println("visitField: " + name + " : " + desc + " : " + signature + " : " + value);
+        // Capture field type dependency.
+        addField(name, desc, (access & Opcodes.ACC_SYNTHETIC) != 0);
+        return null;
+    }
+
+    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions)
+    {
+        //System.out.println("visitMethod: " + name + " : " + desc + " : " + signature);
+        // Capture declared method exception type dependencies.
+        for (int i = 0; (exceptions != null) && (i < exceptions.length); i++)
+        {
+        //System.out.println("visitField exceptions: " + exceptions[i]);
+            addConstantClass(exceptions[i]);
+        }
+        // Capture declared method signature type dependencies.
+        addMethod(name, desc, (access & Opcodes.ACC_SYNTHETIC) != 0);
+        return this;
+    }
+
+    public void visitInnerClass(String name, String outerName, String innerName, int access)
+    {
+        //System.out.println("visitInnerClass: " + name + " : " + outerName + " : " + innerName);
+    }
+
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible)
+    {
+        //System.out.println("visitAnnotation " + desc + " : " + visible);
+        return null;
+    }
+
+    public void visitSource(String source, String debug)
+    {
+    }
+
+    public void visitOuterClass(String owner, String name, String desc)
+    {
+        //System.out.println("visitOuterClass: " + name + " : " + desc);
+    }
+
+    public void visitEnd()
+    {
+    }
+
+    //
+    // Methods for MethodVisitor interface.
+    //
+
+    public AnnotationVisitor visitAnnotationDefault()
+    {
+        return null;
+    }
+
+// A method with this name is already provided in the ClassVisitor interface
+// above, but since neither method does anything we can just ignore it.
+//
+//    public AnnotationVisitor visitAnnotation(String desc, boolean visible)
+//    {
+//        return null;
+//    }
+
+    public AnnotationVisitor visitParameterAnnotation(
+            int parameter,
+            String desc,
+            boolean visible)
+    {
+        //System.out.println("visitParameterAnnotation: " + desc + " : " + visible);
+        return null;
+    }
+
+// A method with this name is already provided in the ClassVisitor interface
+// above, but since neither method does anything we can just ignore it.
+//
+//    public void visitAttribute(Attribute attr)
+//    {
+//    }
+
+    public void visitCode()
+    {
+    }
+
+    public void visitInsn(int opcode)
+    {
+    }
+
+    public void visitIntInsn(int opcode, int operand)
+    {
+    }
+
+    public void visitVarInsn(int opcode, int var)
+    {
+    }
+
+    public void visitTypeInsn(int opcode, String desc)
+    {
+        //System.out.println("visitTypeInsn: " + desc);
+        // This captures type operation type dependency (e.g., new, instanceof).
+        addConstantClass(desc);
+    }
+
+    public void visitFieldInsn(int opcode, String owner, String name, String desc)
+    {
+        //System.out.println("visitFieldInsn: " + owner + " : " + name + " : " + desc);
+        // This captures the owner type dependency of fields we access.
+        addConstantClass(owner);
+    }
+
+    public void visitMethodInsn(int opcode, String owner, String name, String desc)
+    {
+        //System.out.println("visitMethodInsn: " + owner + " : " + name + " : " + desc);
+        // Capture the owner type dependency of the method we invoke.
+        // This is necessary to capture the use of static methods,
+        // but it also captures the use of methods on return arguments.
+        // Capturing the type of return objects is not strictly necessary,
+        // since the type will be captured if assigned to a local variable.
+        // However, not all returned objects are assigned to a local
+        // variable, so this will capture types of return objects that we use
+        // directly (e.g., obj.getFoo().getBar()).
+        addConstantClass(owner);
+    }
+
+    public void visitJumpInsn(int opcode, Label label)
+    {
+    }
+
+    public void visitLabel(Label label)
+    {
+    }
+
+    public void visitLdcInsn(Object cst)
+    {
+        //System.out.println("visitLdcInsn: " + cst);
+    }
+
+    public void visitIincInsn(int var, int increment)
+    {
+    }
+
+    public void visitTableSwitchInsn(int min, int max, Label dflt, Label labels[])
+    {
+    }
+
+    public void visitLookupSwitchInsn(Label dflt, int keys[], Label labels[])
+    {
+    }
+
+    public void visitMultiANewArrayInsn(String desc, int dims)
+    {
+        //System.out.println("visitMultiANewArrayInsn: " + desc);
+    }
+
+    public void visitTryCatchBlock(Label start, Label end, Label handler, String type)
+    {
+    }
+
+    public void visitLocalVariable(
+            String name,
+            String desc,
+            String signature,
+            Label start,
+            Label end,
+            int index)
+    {
+        //System.out.println("visitLocalVariable: " + name + " : " + desc + " : " + signature);
+        // Capture local variable type dependency, but ignore
+        // primitive types.
+        if (desc.startsWith("L"))
+        {
+            // The "desc" variable is in the form "L<class>;", so
+            // extract just the class name, since mangen expects
+            // a class name only or an array.
+            addConstantClass(desc.substring(1, desc.length() - 1));
+        }
+        else if (desc.indexOf("[L") >= 0)
+        {
+            addConstantClass(desc);
+        }
+    }
+
+    public void visitLineNumber(int line, Label start)
+    {
+    }
+
+    public void visitMaxs(int maxStack, int maxLocals)
+    {
+    }
+    
+    public void visitFrame(int type, int nLocal, Object[] local, int nStack, 
+            Object[] stack)
+    {
+    }
+
+// A method with this name is already provided in the ClassVisitor interface
+// above, but since neither method does anything we can just ignore it.
+//
+//    public void visitEnd()
+//    {
+//    }
+
+    //
+    // Utility methods.
+    //
+
+    private void addField(String name, String signature, boolean synth)
+    {
+        m_fieldNames = addToStringArray(m_fieldCount, m_fieldNames, name);
+        m_fieldSignatures = addToStringArray(m_fieldCount, m_fieldSignatures, signature);
+        m_fieldSynthFlags = addToBooleanArray(m_fieldCount, m_fieldSynthFlags, synth);
+        m_fieldCount++;
+    }
+
+    private void addMethod(String name, String signature, boolean synth)
+    {
+        m_methodNames = addToStringArray(m_methodCount, m_methodNames, name);
+        m_methodSignatures = addToStringArray(m_methodCount, m_methodSignatures, signature);
+        m_methodSynthFlags = addToBooleanArray(m_methodCount, m_methodSynthFlags, synth);
+        m_methodCount++;
+    }
+
+    private void addConstantClass(String signature)
+    {
+        m_classSignatures = addToStringArray(m_classCount, m_classSignatures, signature);
+        m_classCount++;
+    }
+
+    public static boolean[] addToBooleanArray(int count, boolean[] bs, boolean b)
+    {
+        if (count < bs.length)
+        {
+            bs[count] = b;
+        }
+        else
+        {
+            boolean[] bs2 = new boolean[bs.length + DEFAULT_INCREMENT];
+            System.arraycopy(bs, 0, bs2, 0, bs.length);
+            bs2[bs.length] = b;
+            bs = bs2;
+        }
+        return bs;
+    }
+
+    public static String[] addToStringArray(int count, String[] ss, String s)
+    {
+        if (count < ss.length)
+        {
+            ss[count] = s;
+        }
+        else
+        {
+            String[] ss2 = new String[ss.length + DEFAULT_INCREMENT];
+            System.arraycopy(ss, 0, ss2, 0, ss.length);
+            ss2[ss.length] = s;
+            ss = ss2;
+        }
+        return ss;
+    }
+}

Added: incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/BCELScanner.java
URL: http://svn.apache.org/viewvc/incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/BCELScanner.java?rev=434371&view=auto
==============================================================================
--- incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/BCELScanner.java (added)
+++ incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/BCELScanner.java Thu Aug 24 04:04:56 2006
@@ -0,0 +1,190 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.tool.mangen;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+import java.util.ArrayList;
+
+import org.apache.bcel.classfile.*;
+
+/**
+ *
+ * @version $Revision: 14 $
+ * @author <A HREF="mailto:robw@ascert.com">Rob Walker</A> 
+ */
+public class BCELScanner
+        implements ClassScanner
+{
+    //////////////////////////////////////////////////
+    // STATIC VARIABLES
+    //////////////////////////////////////////////////
+
+    //////////////////////////////////////////////////
+    // STATIC PUBLIC METHODS
+    //////////////////////////////////////////////////
+    
+    //////////////////////////////////////////////////
+    // INSTANCE VARIABLES
+    //////////////////////////////////////////////////
+    
+    //////////////////////////////////////////////////
+    // CONSTRUCTORS
+    //////////////////////////////////////////////////
+    
+    public BCELScanner()
+    {
+    }
+    
+    //////////////////////////////////////////////////
+    // ACCESSOR METHODS
+    //////////////////////////////////////////////////
+
+    //////////////////////////////////////////////////
+    // PUBLIC INSTANCE METHODS
+    //////////////////////////////////////////////////
+    
+    public JavaClass        jc;
+    public Constant[]       constants = new Constant[0];
+    public Method[]         methods = new Method[0];
+    public Field[]          fields = new Field[0];
+    public ConstantClass[]  constantClasses = new ConstantClass[0]; 
+    
+    //////////////////////////////////////////////////
+    // INTERFACE METHODS - ClassScanner
+    //////////////////////////////////////////////////
+
+    public void scan(InputStream is, String name)
+            throws IOException
+    {
+        ClassParser parser = new ClassParser(is, name);
+        jc = parser.parse();
+        
+        constants = jc.getConstantPool().getConstantPool();
+        methods = jc.getMethods();
+        fields = jc.getFields();
+        
+        // extract out constant classes for later
+        ArrayList cls = new ArrayList();
+        for(int ix=0; ix < constants.length; ix++) 
+        {
+            if (constants[ix] instanceof ConstantClass)
+            {
+                cls.add(constants[ix]); 
+            }
+        }
+        constantClasses = (ConstantClass[]) cls.toArray(constantClasses);
+    }
+
+    
+    public int getMethodCount()
+    {
+        return methods.length;
+    }
+    
+    
+    public String getMethodName(int index)
+    {
+        return methods[index].getName();
+    }
+
+    
+    public String getMethodSignature(int index)
+    {
+        return methods[index].getSignature();
+    }
+
+    
+    public boolean isSyntheticMethod(int index)
+    {
+        return isSynthetic(methods[index]);
+    }
+    
+    
+    public int getFieldCount()
+    {
+        return fields.length;        
+    }
+
+    
+    public String getFieldName(int index)
+    {
+        return fields[index].getName();
+    }
+
+    
+    public String getFieldSignature(int index)
+    {
+        return fields[index].getSignature();
+    }
+    
+    public boolean isSyntheticField(int index)
+    {
+        return isSynthetic(fields[index]);
+    }
+
+    
+    public int getConstantClassCount()
+    {
+        return constantClasses.length;        
+    }
+    
+    
+    public String getConstantClassSignature(int index)
+    {
+        int nameIndex = constantClasses[index].getNameIndex();
+        return ((ConstantUtf8) constants[nameIndex]).getBytes();
+    }
+
+    //////////////////////////////////////////////////
+    // PROTECTED INSTANCE METHODS
+    //////////////////////////////////////////////////
+    
+    /**
+     * Scan any attributes present and return true if the Synthetic attribute
+     * is found.
+     */
+    protected boolean isSynthetic(FieldOrMethod fOrM)
+    {
+        boolean found = false;
+        
+        Attribute[] att = fOrM.getAttributes();
+        for (int ix = 0; ix < att.length && !found; ix++)
+        {
+            if (att[ix] instanceof Synthetic)
+            {
+                found = true;
+            }
+        }
+        
+        return found;
+    }
+    
+    //////////////////////////////////////////////////
+    // PRIVATE INSTANCE METHODS
+    //////////////////////////////////////////////////
+    
+    //////////////////////////////////////////////////
+    // STATIC INNER CLASSES
+    //////////////////////////////////////////////////
+
+    //////////////////////////////////////////////////
+    // NON-STATIC INNER CLASSES
+    //////////////////////////////////////////////////
+    
+}

Added: incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/BundleJar.java
URL: http://svn.apache.org/viewvc/incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/BundleJar.java?rev=434371&view=auto
==============================================================================
--- incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/BundleJar.java (added)
+++ incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/BundleJar.java Thu Aug 24 04:04:56 2006
@@ -0,0 +1,701 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.tool.mangen;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarInputStream;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import java.util.zip.ZipEntry;
+
+import org.osgi.framework.Constants;
+
+/**
+ *
+ * @version $Revision: 26 $
+ * @author <A HREF="mailto:robw@ascert.com">Rob Walker</A> 
+ */
+public class BundleJar
+{
+    //////////////////////////////////////////////////
+    // STATIC VARIABLES
+    //////////////////////////////////////////////////
+
+    /**  class scanner {@link Property} key */
+    public static final String SCANNER_KEY = "mangen.scanner.class";
+    /** Default ClassScanner implementation class name */
+    public static final String DLFT_SCANNER_CLASS = "org.apache.felix.tool.mangen.BCELScanner";
+    
+    /** Crude match pattern for L<classname>; signatures */ 
+    public static Pattern   classnamePattern = Pattern.compile("L[^;]+?;");
+    
+    /** Buffer for jar copying is static. No need to sweat the GC. */
+    public static byte[]    copyBuf = new byte[32767];
+    
+    //////////////////////////////////////////////////
+    // STATIC PUBLIC METHODS
+    //////////////////////////////////////////////////
+
+    /**
+    * Gets a {@link ClassScanner} instance usinng the configured scanner implementation
+     * class.
+     */
+    public static ClassScanner getScanner()
+            throws Exception
+    {
+        String name = PropertyManager.getProperty(SCANNER_KEY, DLFT_SCANNER_CLASS);
+        Class scanClass = Class.forName(name);
+        return (ClassScanner) scanClass.newInstance();
+    }
+    
+    /**
+     * Put the supplied key and value in the specified {@link Attributes}  
+     * if the value is not an empty {@link String}, otherwise remove the key 
+     * from the {@link Attributes}.
+     */
+    public static void putValueIfNotEmpty(Attributes atts, String key, String val)
+    {
+        if (!val.trim().equals(""))
+        {
+            atts.putValue(key, val);
+        }
+        else
+        {
+            // Note that Attributes entry keys are not Strings, so we have to remove
+            // them using the correct object type.
+            Attributes.Name nm = new Attributes.Name(key);
+            atts.remove(nm);
+        }
+    }
+        
+    //////////////////////////////////////////////////
+    // INSTANCE VARIABLES
+    //////////////////////////////////////////////////
+    
+    /** bundle JAR file */
+    public JarFile      jarFile;
+    /** {@link Manifest} from existing bundle JAR */ 
+    public Manifest     manifest;
+    /** Main {@link Attributes} entry from existing bundle JAR */
+    public Attributes   mainAttributes;
+    /** mangen {@link Attributes} entry from existing bundle JAR */
+    public Attributes   mangenAttributes;
+    /** Set of inner JARs processed from the bundle JAR */
+    public Set          currentInnerJars = new HashSet();
+    
+    public Set          possibleExports = OsgiPackage.getNewSet();
+    public Set          possibleImports = OsgiPackage.getNewSet();
+    /** Record of all inner classes, used for tracking awkward synthetic references to inner classes */
+    public Set          innerClasses = new HashSet();
+    /** Sun javac synthetic class references */
+    public Set          syntheticClasses = new HashSet();
+
+    public RuleHandler  bundleRuleHandler;
+    
+    //////////////////////////////////////////////////
+    // CONSTRUCTORS
+    //////////////////////////////////////////////////
+    
+    /**
+     * Create a new bundle JAR instance. Processing will only be performed if
+     * a rule calls the {@see #process()} method.
+     */
+    public BundleJar(String filename) 
+            throws Exception
+    {
+        jarFile = new JarFile(filename);
+        processManifest();
+    }
+
+    //////////////////////////////////////////////////
+    // ACCESSOR METHODS
+    //////////////////////////////////////////////////
+
+    /**
+     * Gets the name of this jar.
+     */
+    public String getName()
+    {
+        return jarFile.getName();
+    }        
+    
+    /**
+     * Returns the set of possible export packages.
+     */
+    public Set getPossibleExports()
+    {
+        return possibleExports;
+    }
+    
+    /**
+     * Returns the set of possible import packages.
+     */
+    public Set getPossibleImports()
+    {
+        return possibleImports;
+    }
+    
+    /**
+     * Returns the set of current Manifest export packages.
+     */
+    public Set getCurrentExports()
+    {
+        return OsgiPackage.createFromHeaders(mainAttributes.getValue(Constants.EXPORT_PACKAGE));
+    }
+    
+    /**
+     * Returns the set of current Manifest import packages.
+     */
+    public Set getCurrentImports()
+    {
+        return OsgiPackage.createFromHeaders(mainAttributes.getValue(Constants.IMPORT_PACKAGE));
+    }
+    
+    /**
+     * Returns the set of "fixed" export packages. These use the same manifest key
+     * but specified in the mangen attributes section.
+     */
+    public Set getFixedExports()
+    {
+        return OsgiPackage.createFromHeaders(mangenAttributes.getValue(Constants.EXPORT_PACKAGE));
+    }
+    
+    /**
+     * Returns the set of "fixed" imports packages. These use the same manifest key
+     * but specified in the mangen attributes section.
+     */
+    public Set getFixedImports()
+    {
+        return OsgiPackage.createFromHeaders(mangenAttributes.getValue(Constants.IMPORT_PACKAGE));
+    }
+    
+    /**
+     * Returns a specified Manifest header value, optionally checking the mangen
+     * attribute set first before the main attribute set.
+     */
+    public String getManifestHeader(String key, boolean checkMangenAtts)
+    {
+        String retval = null;
+        
+        if (checkMangenAtts)
+        {
+            retval = mangenAttributes.getValue(key);
+        }
+        
+        if (retval == null)
+        {
+            retval = mainAttributes.getValue(key);
+        }
+        
+        return retval != null ? retval : "";
+    }
+    
+    
+    //////////////////////////////////////////////////
+    // PUBLIC INSTANCE METHODS
+    //////////////////////////////////////////////////
+    
+    /**
+     * Process the bundle JAR. Every class's package name will be added to the
+     * list of possible exports. Class files will be parsed, and the packages
+     * for all referenced classes contained within them will be added to the 
+     * list of possible imports.
+     */
+    public void process() 
+            throws Exception
+    {
+        processJarEntries();
+        processSunJDKSyntheticClassRefs();        
+        // final step is to execute our own local rules
+        executeBundleRules();
+    }
+    
+   /**
+     * Update the bundle jar's manifest to contain the optimised set of imports
+     * and exports. Note that because of limitations in the standard JDK classes,
+     * this requires copying to a new jar at present and renaming over the current
+     * jar.
+     */
+    public void update(boolean overwrite)
+            throws IOException
+    {
+        Manifest newManifest = updateHeaders();
+        String origName = getName();
+        
+        File newJar = new File(origName + ".new.jar");
+        JarOutputStream jos = new JarOutputStream(new FileOutputStream(newJar), newManifest);
+        
+        Enumeration en = jarFile.entries();
+        while (en.hasMoreElements())
+        {
+            ZipEntry ze = (ZipEntry) en.nextElement();
+            if (ze.getName().compareToIgnoreCase("META-INF/MANIFEST.MF") != 0)
+            {
+                jos.putNextEntry(ze);
+                copy(jarFile.getInputStream(ze), jos);
+            }
+        }
+        
+        jos.close();
+        
+        // replace existing file if needed
+        if (overwrite)
+        {
+            jarFile.close();
+            File origFile = new File(origName);
+            
+            if (!origFile.delete())
+            {
+                throw new IOException("delete of original JAR failed");
+            }
+            
+            if (!newJar.renameTo(origFile))
+            {
+                throw new IOException("rename of new JAR failed");
+            }
+        }
+    }
+    
+    //////////////////////////////////////////////////
+    // INTERFACE METHODS
+    //////////////////////////////////////////////////
+
+    //////////////////////////////////////////////////
+    // PROTECTED INSTANCE METHODS
+    //////////////////////////////////////////////////
+
+    /**
+     * Process the Manifest for this Jar file. Need to retrieve any existing
+     * imports and exports for this Jar file and also determine which inner
+     * jars we should scan.
+     */
+    protected void processManifest()
+        throws IOException
+    {
+        manifest = jarFile.getManifest();
+        if (manifest == null)
+        {
+            manifest = new Manifest();
+        }
+        
+        mainAttributes = manifest.getMainAttributes();        
+        if (mainAttributes == null)
+        {
+            mainAttributes = new Attributes();
+        }
+        
+        String val = mainAttributes.getValue(Constants.BUNDLE_CLASSPATH);
+        if (val != null)
+        {
+            parseBundleClassPath(val, currentInnerJars);
+        }
+
+        // look for mangen rules in manifest 
+        mangenAttributes = manifest.getAttributes("com/ascert/openosgi/mangen");
+        if (mangenAttributes == null)
+        {
+            mangenAttributes = new Attributes();
+        }
+        
+        bundleRuleHandler = new RuleHandler(mangenAttributes);
+    }
+
+    /** 
+     * Parse the OSGi bundle classpath and add all jars to set of inner jars
+     */
+    public void parseBundleClassPath(String path, Set innerJars)
+    {
+        StringTokenizer tok = new StringTokenizer(path, ",");
+        while (tok.hasMoreTokens())
+        {
+            String name = tok.nextToken();
+            if (name.endsWith(".jar"))
+            {
+                innerJars.add(name.trim());
+            }
+        }
+    }
+    
+    /**
+     * Process the set of entries in the main Jar file. Every .class entry will
+     * have it's package name added to the list of possible exports if needed, and
+     * will be parsed to determine if it contains new imports.
+     */
+    protected void processJarEntries()
+        throws Exception
+    {
+        Enumeration en = jarFile.entries();
+        while (en.hasMoreElements())
+        {
+            ZipEntry ze = (ZipEntry) en.nextElement();
+            String name = ze.getName();
+            if (name.endsWith(".class"))
+            {
+                addPossibleExport(name);
+                InputStream is = jarFile.getInputStream(ze);
+                processClassEntry(is, name);
+                is.close();
+            }
+            else if (name.endsWith(".jar"))
+            {
+                JarInputStream jis = new JarInputStream(jarFile.getInputStream(ze));
+                processInnerJar(jis, name);
+                jis.close();
+            }
+        }
+    }
+    
+    /**
+     * Parse and process an inner jar in the supplied InputStream. 
+     *
+     * At present we only process inner jars that are on the current bundle classpath. 
+     * Since we're a manifest generator, we could also have rules to automatically
+     * process matching inner jars we find and also generate an appropriate 
+     * bundle classpath. 
+     */
+    public void processInnerJar(JarInputStream jis, String jarName)
+        throws Exception
+    {
+        if (currentInnerJars.contains(jarName))
+        {
+            // Loop through JAR entries.
+            for (JarEntry je = jis.getNextJarEntry(); je != null; je = jis.getNextJarEntry())
+            {
+                String name = je.getName();
+                if (name.endsWith(".class"))
+                {
+                    addPossibleExport(name);
+                    processClassEntry(jis, name);
+                }
+            }
+        }
+    }
+    
+    /**
+     * Parse and process a class entry in the supplied InputStream.
+     *
+     */
+    public void processClassEntry(InputStream is, String name)
+        throws Exception    
+    {
+        // need to track inner classes for Sun synthetic class name handling
+        if (name.indexOf('$') != -1)
+        {
+            addToInnerClasses(name);
+        }
+        
+        ClassScanner scanner = getScanner();
+        scanner.scan(is, name);
+        scanConstantsClasses(scanner);
+        scanFields(scanner);
+        scanMethods(scanner);
+    }
+    
+    /**
+     * Map the supplied name into a package name and add to the target set if 
+     * it is a new package name.
+     */
+    protected void addToPackageSet(String itemName, Set targetSet)
+    {
+        int lastPathSep = itemName.lastIndexOf('/');
+        if (lastPathSep != -1)
+        {
+            String pkg = itemName.substring(0, lastPathSep);
+            pkg = pkg.replace('/', '.');
+            
+            if (!targetSet.contains(pkg))
+            {
+                targetSet.add(OsgiPackage.createStringPackage(pkg));
+            }
+        }
+    }
+    
+    
+    /**
+     * Add name possible exports if it contains a new package name.
+     */
+    protected void addPossibleExport(String name)
+    {
+        addToPackageSet(name, possibleExports);
+    }
+    
+    /**
+     * Add classname to list of inner classes
+     */
+    protected void addToInnerClasses(String name)
+    {
+        int suffix = name.lastIndexOf(".class");
+        String justName = name.substring(0, suffix);
+        
+        if (!innerClasses.contains(justName))
+        {
+            innerClasses.add(justName);
+        }
+    }
+    
+    /**
+     * Parse the supplied signature string, extract all L<class>; format
+     * class references and adding them to the specified set.
+     */
+    protected boolean extractClassesFromSignature(String signature, Set set)
+    {
+        boolean matched = false;
+        Matcher m = classnamePattern.matcher(signature);
+        
+        while (m.find())
+        {
+            matched = true;
+            String classname = m.group();
+            //System.out.println("match: " + classname);
+            addToPackageSet(classname.substring(1, classname.length() - 1), set);
+        }
+        
+        return matched;
+    }
+    
+    
+    /**
+     * Scan the constant pool of the parsed java class for any ConstantClass references.
+     * Add any found into the list of possible import packages.
+     */
+    protected void scanConstantsClasses(ClassScanner scanner) 
+    {
+        for(int ix=0; ix < scanner.getConstantClassCount(); ix++)
+        {
+            String classRef = scanner.getConstantClassSignature(ix);
+            
+            MangenMain.trace("ConstantClass : " + classRef);
+            
+            if (classRef.startsWith("["))
+            {
+              // array classname
+              extractClassesFromSignature(classRef, possibleImports);
+            }
+            else
+            {
+              // simple classname
+              addToPackageSet(classRef, possibleImports);
+            }
+        }
+    }
+    
+    /**
+     * Scan the fields of the parsed java class for all class references.
+     * Add any found into the list of possible import packages.
+     */
+    protected void scanFields(ClassScanner scanner) 
+    {
+        for(int ix=0; ix < scanner.getFieldCount(); ix++) 
+        {
+            String name = scanner.getFieldName(ix);
+            String sig = scanner.getFieldSignature(ix);
+            
+            MangenMain.trace("Field : name=" + name + ", sig=" + sig);
+
+            if (scanner.isSyntheticField(ix))
+            {
+                handleSunJDKSyntheticClassRefs(name);
+            }
+            extractClassesFromSignature(sig, possibleImports);
+        }
+    }
+    
+    /**
+     * Scan the methods of the parsed java class for all class references.
+     * Add any found into the list of possible import packages.
+     */
+    protected void scanMethods(ClassScanner scanner) 
+    {
+        for(int ix=0; ix < scanner.getMethodCount(); ix++) 
+        {
+            String name = scanner.getMethodName(ix);
+            String sig = scanner.getMethodSignature(ix);
+            
+            MangenMain.trace("Method : name=" + name + ", sig=" + sig);
+            
+            extractClassesFromSignature(sig, possibleImports);
+        }
+    }
+    
+    /**
+     * The Sun JDK javac generates synthetic fields with a name of 
+     *  class$packagename$classname for classes that are directly referenced 
+     * in code as opposed to being used in methods and fields.
+     *
+     * First stage is to store all of these references ready for post-processing.
+     */
+    protected void handleSunJDKSyntheticClassRefs(String name)
+    {
+        if (name.startsWith("class$"))
+        {
+            syntheticClasses.add(name.substring(6));
+        }
+    }
+    
+    /**
+     * Post-processing of Sun JDK javac generated synthetic fields.
+     *
+     * The general case is to handle these by unmangling the generated name and
+     * create an import reference for it. A special case exists for inner class
+     * references which need the last inner class reference removed.
+     *
+     * Not a perfect solution, but since this is a special case of dynamic
+     * classloading without actually executing the bytecode or looking for code
+     * patterns it's a reasonable compromise.
+     */
+    protected void processSunJDKSyntheticClassRefs()
+    {
+        for(Iterator i = syntheticClasses.iterator(); i.hasNext(); )
+        {
+            String name = (String) i.next();
+            
+            // check for inner class case
+            int lastSep = name.lastIndexOf('$');
+            if (lastSep != -1)
+            {
+                String possInnerClass = name.substring(0, lastSep).replace('$','/') +
+                                        name.substring(lastSep);
+                                        
+                if (innerClasses.contains(possInnerClass))
+                {
+                    // strip off last $ component, which is the inner class name
+                    name = name.substring(0, lastSep);
+                }
+            }
+            
+            String classname = name.replace('$','/');
+            addToPackageSet(classname, possibleImports);
+        }
+    }
+
+    /**
+     * Execute any local i.e. bundle specific rules.
+     */
+    protected void executeBundleRules()
+    {
+        if (bundleRuleHandler != null)
+        {
+            ArrayList dummyList = new ArrayList();
+            dummyList.add(this);
+            bundleRuleHandler.executeRules(dummyList);
+        }
+    }
+    
+    /**
+     * Copy inputstream to output stream. Main use is Jar updating to create new
+     * jar.
+     */
+    protected void copy(InputStream is, OutputStream os)
+            throws IOException
+    {
+        int len = 0;
+        
+        while(len != -1)
+        {
+            len = is.read(copyBuf, 0, copyBuf.length);
+            if (len > 0)
+            {
+                os.write(copyBuf, 0, len);
+            }
+        }
+    }
+    
+    /**
+     * Update the manifest headers based on the processed state of imports, 
+     * exports etc.
+     */
+    protected Manifest updateHeaders()
+    {
+        Manifest newManifest = new Manifest(manifest);
+        Attributes newAtts = newManifest.getMainAttributes();
+        
+        // First determine whether to mark for R3 or R4 usage
+        String val = PropertyManager.getProperty("mangen.osgi.level", "3");
+        if (val.equals("4"))
+        {
+            newAtts.putValue(Constants.BUNDLE_MANIFESTVERSION, "2");
+        }
+        else
+        {
+            newAtts.putValue(Constants.BUNDLE_MANIFESTVERSION, "1");
+        }
+
+        putValueIfNotEmpty(newAtts, Constants.EXPORT_PACKAGE, getAsHeaderValue(possibleExports)); 
+        putValueIfNotEmpty(newAtts, Constants.IMPORT_PACKAGE, getAsHeaderValue(possibleImports)); 
+        //TODO: implement generation of bundle classpath if mangen.innerjar.auto set
+        
+        return newManifest;
+    }
+    
+    /**
+     * Get the specified set of packages as a String of values suitable for use in 
+     * a manifest header. 
+     */
+    protected String getAsHeaderValue(Set set)
+    {
+        StringBuffer str = new StringBuffer();
+        boolean first = true;
+        
+        for(Iterator i = set.iterator(); i.hasNext(); )
+        {
+            OsgiPackage pkg = (OsgiPackage) i.next();
+            if (first)
+            {
+                str.append(pkg.toString());
+                first = false;
+            }
+            else
+            {
+                str.append(", " + pkg.toString());
+            }
+        }
+        
+        return str.toString();
+    }
+    
+    //////////////////////////////////////////////////
+    // PRIVATE INSTANCE METHODS
+    //////////////////////////////////////////////////
+    
+    //////////////////////////////////////////////////
+    // STATIC INNER CLASSES
+    //////////////////////////////////////////////////
+
+    //////////////////////////////////////////////////
+    // NON-STATIC INNER CLASSES
+    //////////////////////////////////////////////////
+    
+}

Added: incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/ClassScanner.java
URL: http://svn.apache.org/viewvc/incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/ClassScanner.java?rev=434371&view=auto
==============================================================================
--- incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/ClassScanner.java (added)
+++ incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/ClassScanner.java Thu Aug 24 04:04:56 2006
@@ -0,0 +1,137 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.tool.mangen;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * Wrapper interface to class bytecode scanning tools. This interface exposes
+ * just the methods required by mangen for class scanning. Specific implementations
+ * should be created for different scanners e.g. ASM, BCEL etc.
+ * <p>
+ * At present the interface has been deliberately kept small and simple. It would be
+ * possible to use a {@link java.util.Collection#toArray()} or {@link java.util.Iterator} style models
+ * for retrieving class information but in both cases these would be likely to require
+ * additional object creation with possibly detrimental performance impacts.
+ * <p>
+ * The ordering of return values for each of the methods taking an <code>index</code> is not
+ * important. All that matters is that the method can be called with an index of <code>0</code> 
+ * up to <code>getXXCount()</code> times to retrieve the complete set of values.
+ * 
+ * @version $Revision: 14 $
+ * @author <A HREF="mailto:robw@ascert.com">Rob Walker</A> 
+ * @see <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/VMSpecTOC.doc.html">The JavaTM Virtual Machine Specification, Second Edition</a> 
+ */
+public interface ClassScanner
+{
+    //////////////////////////////////////////////////
+    // STATIC VARIABLES
+    //////////////////////////////////////////////////
+
+    //////////////////////////////////////////////////
+    // SIGNATURES
+    //////////////////////////////////////////////////
+
+    /**
+    * Scan the class bytes by reading the supplied {@link InputStream} The name field
+     * specifies the name of the class to be scanned, and may not be needed 
+     * by all scanners.
+     */
+    public void scan(InputStream is, String name)
+            throws IOException;
+
+    /**
+     * Return the number of methods for the scanned class
+     */
+    public int getMethodCount();
+    
+    /**
+     * Return the name of the specific method index.
+     */
+    public String getMethodName(int index);
+
+    /**
+     * Return the signature of the specific method index. 
+     * <p>
+     * Method signatures should be returned in the form as specified in 
+     * <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#7035">section 4.3.3</a>
+     * of the VM spec.
+     */
+    public String getMethodSignature(int index);
+
+    /**
+     * Determines whether the specified method has a Synthetic attribute present.
+     * <p>
+     * Section <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#80128">section 4.7.6</a>
+     * describes the <code>Synthetic</code> attribute. A typical use of this is to mark
+     * methods and fields generated by the Java compiler which weren't actually present in
+     * the source code. There are a few cases where <code>mangen</code> uses these to
+     * look for <i>generated name</i> patterns to detect class references that would
+     * otherwise be missed.
+     */
+    public boolean isSyntheticMethod(int index);
+    
+    /**
+     * Return the number of fields for the scanned class
+     */
+    public int getFieldCount();
+
+    /**
+     * Return the name of the specific field index.
+     */
+    public String getFieldName(int index);
+
+    /**
+     * Return the signature of the specific method index.
+     * <p>
+     * Field signatures should be returned in the form as specified in 
+     * <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#14152">section 4.3.2</a>
+     * of the VM spec.
+     */
+    public String getFieldSignature(int index);
+
+    /**
+     * Determines whether the specified field has a Synthetic attribute present.
+     * {@see #getMethodSignature(int)}
+     */
+    public boolean isSyntheticField(int index);
+
+    /**
+     * Return the number of constant classes for the scanned class. Depending on the
+     * approach taking by specific scanner implementations it may be possible to detect all
+     * required class references without accessing the constant classes. If so, 
+     * the implementation of this method should return 0.
+     */
+    public int getConstantClassCount();
+    
+    /**
+     * Return the signature of the specific constant class.
+     * <p>
+     * ConstantClass signatures should be returned in the form as specified in 
+     * <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#1221">section 4.4.1</a>
+     * of the VM spec.
+     * <p>
+     * <i>Note:</i> in general, a simple class name should be returned as a name string rather
+     * than an <code>L&lt;classname&gt;;</code> signature string. However, if an implementation
+     * has a signature format and wishes to avoid parsing it then simply pre-pending a <code>[</code> will
+     * force the {@link BundleJar} calling method to treat it as a signature and perform the
+     * parsing.
+     */
+    public String getConstantClassSignature(int index);
+    
+}

Added: incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/GenericHandler.java
URL: http://svn.apache.org/viewvc/incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/GenericHandler.java?rev=434371&view=auto
==============================================================================
--- incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/GenericHandler.java (added)
+++ incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/GenericHandler.java Thu Aug 24 04:04:56 2006
@@ -0,0 +1,175 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.tool.mangen;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Properties;
+
+import java.util.jar.Attributes;
+
+/** 
+ * The GenericHandler class provides a general purpose mechanism for providing
+ * an extensible set of classname based handler items. 
+ *
+ * @version $Revision: 14 $
+ * @author <A HREF="mailto:robw@ascert.com">Rob Walker</A> 
+ */
+public abstract class GenericHandler
+{
+    //////////////////////////////////////////////////
+    // STATIC VARIABLES
+    //////////////////////////////////////////////////
+
+    /** List of usable handler items in order of their declaration */
+    public ArrayList handlerList = new ArrayList();
+    
+    //////////////////////////////////////////////////
+    // STATIC PUBLIC METHODS
+    //////////////////////////////////////////////////
+    
+    //////////////////////////////////////////////////
+    // INSTANCE VARIABLES
+    //////////////////////////////////////////////////
+    
+    //////////////////////////////////////////////////
+    // CONSTRUCTORS
+    //////////////////////////////////////////////////
+    
+    /**
+     * For every property which matches the name itemKey.<n> create a handler
+     * item and check the handler item class matches the specified class. Handler items
+     * should contain either fully qualified classnames or classnames relative to the 
+     * supplied defaultPkg.
+     */
+    public GenericHandler(String itemKey, Class clazz, String defaultPkg)
+    {
+        int ix = 0;
+        String itemString = PropertyManager.getProperty(itemKey + ix++);
+        while (itemString != null)
+        {
+            handlerList.add(create(itemString, clazz, defaultPkg));
+            itemString = PropertyManager.getProperty(itemKey + ix++);
+        }
+    }
+    
+    /**
+     * Create a handler list based on a set of manifest attributes.
+     */
+    public GenericHandler(Attributes atts, String itemKey, Class clazz, String defaultPkg)
+    {
+        int ix = 0;
+        String itemString = atts.getValue(itemKey + ix++);
+        while (itemString != null)
+        {
+            GenericHandlerItem item = create(itemString, clazz, defaultPkg);
+            if (item != null)
+            {
+                handlerList.add(item);
+            }
+            itemString = atts.getValue(itemKey + ix++);
+        }
+    }
+    
+    //////////////////////////////////////////////////
+    // ACCESSOR METHODS
+    //////////////////////////////////////////////////
+
+    //////////////////////////////////////////////////
+    // PUBLIC INSTANCE METHODS
+    //////////////////////////////////////////////////
+    
+    //////////////////////////////////////////////////
+    // INTERFACE METHODS
+    //////////////////////////////////////////////////
+
+    //////////////////////////////////////////////////
+    // PROTECTED INSTANCE METHODS
+    //////////////////////////////////////////////////
+    
+    /**
+     * Create the handler item object for the specified itemString. 
+     * HandlerItems are in the following format
+     * 
+     *      <item-class> : <item-options>
+     *
+     *      <item-class>    is either a fully qualified classname, or a classname within 
+     *                      defaultPkg. 
+     *      <item-options>  is a free-format string of item specific options.
+     *
+     */
+    protected GenericHandlerItem create(String itemString, Class clazz, String defaultPkg)
+    {
+        GenericHandlerItem item = null;
+        String itemName = itemString;
+        String itemOptions = "";
+
+        int itemSepPos = itemString.indexOf(' ');
+        if (itemSepPos != -1)
+        {
+            itemName    = itemString.substring(0, itemSepPos).trim();
+            itemOptions = itemString.substring(itemSepPos+1).trim();
+        }
+        
+        try
+        {
+            Class itemClass;
+            
+            if (itemName.indexOf('.') == -1)
+            {
+                itemClass = Class.forName(defaultPkg + "." + itemName);
+            }
+            else
+            {
+                itemClass = Class.forName(itemName);
+            }
+
+            if (! clazz.isAssignableFrom(itemClass))
+            {
+                throw new ClassCastException("mismatched class type");
+            }
+            
+            item = (GenericHandlerItem) itemClass.newInstance();
+            item.setOptions(itemOptions);
+        }
+        catch (ClassNotFoundException cnfe)
+        {
+            System.err.println("Unable to load class for handler item: " + itemName);
+        }
+        catch (Exception e)
+        {
+            System.err.println("Exception creating handler item object: " + itemName
+                               + "(" + e + ")");
+        }
+        
+        return item;
+    }
+    
+    
+    //////////////////////////////////////////////////
+    // PRIVATE INSTANCE METHODS
+    //////////////////////////////////////////////////
+    
+    //////////////////////////////////////////////////
+    // STATIC INNER CLASSES
+    //////////////////////////////////////////////////
+
+    //////////////////////////////////////////////////
+    // NON-STATIC INNER CLASSES
+    //////////////////////////////////////////////////
+    
+}

Added: incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/GenericHandlerItem.java
URL: http://svn.apache.org/viewvc/incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/GenericHandlerItem.java?rev=434371&view=auto
==============================================================================
--- incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/GenericHandlerItem.java (added)
+++ incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/GenericHandlerItem.java Thu Aug 24 04:04:56 2006
@@ -0,0 +1,36 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.tool.mangen;
+
+/**
+ *
+ * @version $Revision: 14 $
+ * @author <A HREF="mailto:robw@ascert.com">Rob Walker</A> 
+ */
+public interface GenericHandlerItem
+{
+    //////////////////////////////////////////////////
+    // STATIC VARIABLES
+    //////////////////////////////////////////////////
+
+    //////////////////////////////////////////////////
+    // SIGNATURES
+    //////////////////////////////////////////////////
+
+    public void setOptions(String options);
+
+}

Added: incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/GenericHandlerItemImpl.java
URL: http://svn.apache.org/viewvc/incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/GenericHandlerItemImpl.java?rev=434371&view=auto
==============================================================================
--- incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/GenericHandlerItemImpl.java (added)
+++ incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/GenericHandlerItemImpl.java Thu Aug 24 04:04:56 2006
@@ -0,0 +1,268 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.tool.mangen;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Holder for common item handler methods, such as standard option processing
+ *
+ * @version $Revision: 14 $
+ * @author <A HREF="mailto:robw@ascert.com">Rob Walker</A> 
+ */
+public abstract class GenericHandlerItemImpl
+        implements GenericHandlerItem
+{
+    //////////////////////////////////////////////////
+    // STATIC VARIABLES
+    //////////////////////////////////////////////////
+
+    //////////////////////////////////////////////////
+    // STATIC PUBLIC METHODS
+    //////////////////////////////////////////////////
+    
+    /**
+     * Makes perfect sense that JDK Set would have no "get" operation since, 
+     * in theory to do a contains(), you must alreay have the Object. Since we
+     * subvert that a little by allowing comparisons with different object types,
+     * it doesn't quite work for our case though.
+     */
+    public static OsgiPackage getPackageFromSet(OsgiPackage pkg, Set set)
+    {
+        OsgiPackage retval = null;
+        // do search based on package name only, to ensure we find occurences of
+        // different types
+        String name = pkg.getName();
+        
+        for(Iterator i = set.iterator(); retval == null && i.hasNext(); )
+        {
+            OsgiPackage setPkg = (OsgiPackage) i.next();
+            if (setPkg.equals(name))
+            {
+                retval = setPkg;
+            }
+        }
+        
+        return retval;
+    }
+    
+    //////////////////////////////////////////////////
+    // INSTANCE VARIABLES
+    //////////////////////////////////////////////////
+    
+    /** Map contain parsed options */
+    public Map                      optionMap = new HashMap();
+    
+    //////////////////////////////////////////////////
+    // CONSTRUCTORS
+    //////////////////////////////////////////////////
+
+    public GenericHandlerItemImpl()
+    {
+    }
+    
+    //////////////////////////////////////////////////
+    // ACCESSOR METHODS
+    //////////////////////////////////////////////////
+
+    //////////////////////////////////////////////////
+    // PUBLIC INSTANCE METHODS
+    //////////////////////////////////////////////////
+    
+    /**
+     * Test whether the supplied package matches one of the set of patterns
+     * for the specified qualifier.
+     */
+    protected boolean isPackageMatch(OsgiPackage pkg, String qualName)
+    {
+        return getMatchingPatternString(pkg, qualName, false) != null ? true : false;
+    }
+    
+    /**
+     * Get's the pattern string that matches the supplied package in the specified
+     * qualifier set. Optionally, the matching algorithm can be instructed to only
+     * match on package name, which will exclude matching any package attributes 
+     * after the ";" specifier.
+     */
+    protected String getMatchingPatternString(OsgiPackage pkg, String qualName, boolean nameMatchOnly)
+    {
+        String pattString = null;
+        Set pattSet = (Set) optionMap.get(qualName);
+
+        if (pattSet != null)
+        {
+            for(Iterator i = pattSet.iterator(); pattString == null && i.hasNext(); )
+            {
+                Pattern origPatt = (Pattern) i.next();
+                Pattern patt = origPatt;
+                String matchString = pkg.toString();
+                
+                if (nameMatchOnly)
+                {
+                    matchString = pkg.getName();
+                    // need to strip off any attributes and recompile pattern
+                    String fullStr = patt.pattern();
+                    int delim = fullStr.indexOf(';');
+                    if (delim != -1)
+                    {
+                        patt = Pattern.compile(fullStr.substring(0, delim));
+                    }
+                    
+                }
+                
+                Matcher matcher = patt.matcher(matchString);
+                if (matcher.matches())
+                {
+                    pattString = origPatt.pattern();
+                }
+            }
+        }
+        
+        return pattString;
+    }
+    
+    /**
+     * Test whether the supplied jar name matches one of the set of patterns
+     * for the specified qualifier.
+     */
+    protected boolean isJarNameMatch(String jarName, String qualName)
+    {
+        boolean found = false;
+        Set pattSet = (Set) optionMap.get(qualName);
+
+        if (pattSet != null)
+        {
+            for(Iterator i = pattSet.iterator(); !found && i.hasNext(); )
+            {
+                Pattern patt = (Pattern) i.next();
+                
+                Matcher matcher = patt.matcher(jarName);
+                if (matcher.matches())
+                {
+                    found = true;
+                }
+            }
+        }
+        
+        return found;
+    }
+    
+    
+    //////////////////////////////////////////////////
+    // INTERFACE METHODS - GenericHandlerItem
+    //////////////////////////////////////////////////
+    
+    /**
+     * Process the option set. The GenericRule will parse and separate a space 
+     * separated list of qualifiers in the following format.
+     * 
+     *      qual1(<qual1-options>) qual2(<qual2-options>) 
+     */
+    public void setOptions(String options)
+    {
+        for (StringTokenizer tok = new StringTokenizer(options, " "); tok.hasMoreTokens(); )
+        {
+            String qualifier = tok.nextToken().trim();
+            if (qualifier.startsWith("sys-packages") || 
+                qualifier.startsWith("imports") ||
+                qualifier.startsWith("exports") ||
+                qualifier.startsWith("skip-jars") )
+            {
+                processStandardQualifier(qualifier);
+            }
+            else
+            {
+                processNonStandardQualifier(qualifier);
+            }
+        }
+    }
+    
+    //////////////////////////////////////////////////
+    // PROTECTED INSTANCE METHODS
+    //////////////////////////////////////////////////
+    
+    /**
+     * Hook for subclasses to include own qualifier processing if they wish to
+     * use this model with non-standard qualifiers
+     */
+    protected void processNonStandardQualifier(String qual)
+    {
+    }
+    
+    /**
+     * Process a standard qualifier. This will contain a qualifier keyword, followed
+     * by a comma separated list of patterns enclosed in brackets:
+     *
+     *      qual1(patt1, patt2, patt3)
+     *
+     */
+    protected void processStandardQualifier(String qual)
+    {
+        int start = qual.indexOf('(');
+        int end = qual.lastIndexOf(')');
+        
+        if (start == -1 || end == -1)
+        {
+            throw new IllegalArgumentException("badly formed rule qualifier: " + qual);
+        }
+            
+        String qualName = qual.substring(0, start).trim();
+        
+        // Process the comma separated list of packages
+        String list = qual.substring(start+1, end);
+        
+        // get any existing set for this option name, create new set if none
+        Set pattSet = (Set) optionMap.get(qualName);
+        if (pattSet == null)
+        {
+            pattSet = new HashSet();
+        }
+        
+        for (StringTokenizer tok = new StringTokenizer(list, ","); tok.hasMoreTokens(); )
+        {
+            Pattern patt = Pattern.compile(tok.nextToken().trim());
+            pattSet.add(patt);
+        }
+        
+        optionMap.put(qualName, pattSet);
+    }
+    
+    //////////////////////////////////////////////////
+    // PRIVATE INSTANCE METHODS
+    //////////////////////////////////////////////////
+    
+    //////////////////////////////////////////////////
+    // STATIC INNER CLASSES
+    //////////////////////////////////////////////////
+
+    //////////////////////////////////////////////////
+    // NON-STATIC INNER CLASSES
+    //////////////////////////////////////////////////
+    
+}

Added: incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/MangenMain.java
URL: http://svn.apache.org/viewvc/incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/MangenMain.java?rev=434371&view=auto
==============================================================================
--- incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/MangenMain.java (added)
+++ incubator/felix/trunk/tools/mangen/src/main/java/org/apache/felix/tool/mangen/MangenMain.java Thu Aug 24 04:04:56 2006
@@ -0,0 +1,283 @@
+/*
+ *   Copyright 2005 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.tool.mangen;
+
+import java.io.File;
+import java.io.PrintStream;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+
+/**
+ *
+ * @version $Revision: 29 $
+ * @author <A HREF="mailto:robw@ascert.com">Rob Walker</A> 
+ */
+public class MangenMain
+{    
+    //////////////////////////////////////////////////
+    // STATIC VARIABLES
+    //////////////////////////////////////////////////
+
+    public static final String  PROP_FILE = "mangen.properties";
+    
+    public static ArrayList     jarList = new ArrayList();
+    public static HashMap       fileMap = new HashMap();
+    
+    public static RuleHandler[] ruleSets;
+    public static ReportHandler reportHandler;
+
+    public static boolean optTrace;   
+    public static boolean optFailOnError;
+    public static boolean optFailOnWarning;
+    
+    public static int errorCount = 0;
+    public static int warningCount = 0;
+    
+    //////////////////////////////////////////////////
+    // STATIC PUBLIC METHODS
+    //////////////////////////////////////////////////
+    
+    /** 
+     * Main entry point to mangen from command line. Argument list is expected to 
+     * be a list of jars, or directories which will be scanned for jar, or a
+     * combination of both.
+     */
+    public static void main(String[] args)
+            throws Exception
+    {
+        long start = System.currentTimeMillis();
+        
+        PropertyManager.initProperties(PROP_FILE);
+        processOptions();
+        
+        ruleSets = RuleHandler.initRuleSets();
+        reportHandler = new ReportHandler();
+        
+        initBundleJarList(args);
+        
+        RuleHandler.runRuleSets(ruleSets, jarList);
+        reportHandler.runReports(System.out, jarList);
+        
+        long stop = System.currentTimeMillis();
+        System.out.println("Time elapsed: " + ((double) (stop - start)/1000));
+
+        int exitCode = 0;        
+        if (optFailOnError && errorCount > 0)
+        {
+            exitCode = 3;
+        }
+        else if (optFailOnWarning && warningCount > 0)
+        {
+            exitCode = 5;
+        }
+        
+        System.exit(exitCode);
+    }
+        
+    /**
+     * Increment count of errors raised and send string to output stream. Also
+     * show message on <code>stderr</code> if set to fail on errors.
+     */
+    public static void error(PrintStream out, String msg)
+    {
+        errorCount++;
+        out.println(msg);
+        
+        if (optFailOnError)
+        {
+            System.err.println(msg);
+        }
+    }
+
+    /**
+     * Increment count of warnings raised and send string to output stream. Also
+     * show message on <code>stderr</code> if set to fail on warnings.
+     */
+    public static void warning(PrintStream out, String msg)
+    {
+        warningCount++;
+        out.println(msg);
+        
+        if (optFailOnWarning)
+        {
+            System.err.println(msg);
+        }
+    }
+    
+    /**
+     * Generate trace output if enabled
+     */
+    public static void trace(String msg)
+    {
+        if (optTrace)
+        {
+            System.out.println("TRACE - " + msg);
+        }
+    }
+    
+    //////////////////////////////////////////////////
+    // STATIC PRIVATE METHODS
+    //////////////////////////////////////////////////
+    
+    /**
+     * Process each of the command line arguments. Any files ending .jar are treated
+     * as bundle jars. Any directories will be descended into to 
+     * process any .jar files contained in the directory tree beneath. 
+     */
+    private static void initBundleJarList(String[] args)
+            throws Exception
+    {
+        // params are an array of jar files which may have partially compelete manifests
+        for (int ix = 0; ix < args.length; ix++)
+        {
+            if (args[ix].endsWith(".jar"))
+            {
+                initJar(args[ix]);
+            }
+            else
+            {
+                File f = new File(args[ix]);
+                if (f.isDirectory())
+                {
+                    processDir(f);
+                }
+            }
+        }
+    }
+    
+    /**
+     * Process a directory argument. Process all files and subdirectories.
+     */
+    private static void processDir(File dir)
+            throws Exception
+    {
+        //TODO: exception handling cases?
+        String[] files = dir.list();
+        for (int ix = 0; ix < files.length; ix++)
+        {
+            files[ix] = dir.getPath() + File.separator + files[ix];
+        }
+        initBundleJarList(files);
+    }
+    
+    /**
+    * Create a new {@see BundleJar} and add it to the list of bundle JARs. 
+     */
+    private static void initJar(String jarName)
+            throws Exception
+    {
+        try
+        {   
+            // normalize path separators for checking
+            String absName = new File(jarName).getAbsolutePath();
+            if (!fileMap.containsKey(absName))
+            {
+                BundleJar bund = new BundleJar(absName);
+                fileMap.put(absName, bund);
+                jarList.add(bund);
+            }
+            else
+            {
+                trace("skipping repeated filename: " + absName);
+            }
+        }
+        catch (RuntimeException re)
+        {
+            System.err.println("Exception: " + re + ", skipping bundle jar: " + jarName);
+            re.printStackTrace(System.err);
+        }
+        catch (Exception e)
+        {
+            System.err.println("Exception: " + e + ", skipping bundle jar: " + jarName);
+        }
+    }
+    
+    /**
+     * Process runtime options and defaults.
+     */
+    private static void processOptions()
+    {
+        String opt = PropertyManager.getProperty("mangen.trace", "off");
+        if (opt.compareToIgnoreCase("on") == 0)
+        {
+            optTrace = true;
+        }
+        else
+        {
+            optTrace = false;
+        }
+        
+        opt = PropertyManager.getProperty("mangen.failonerror", "on");
+        if (opt.compareToIgnoreCase("on") == 0)
+        {
+            optFailOnError = true;
+        }
+        else
+        {
+            optFailOnError = false;
+        }
+        
+        opt = PropertyManager.getProperty("mangen.failonwarning", "off");
+        if (opt.compareToIgnoreCase("on") == 0)
+        {
+            optFailOnWarning = true;
+        }
+        else
+        {
+            optFailOnWarning = false;
+        }
+    }    
+    
+    //////////////////////////////////////////////////
+    // INSTANCE VARIABLES
+    //////////////////////////////////////////////////
+    
+    //////////////////////////////////////////////////
+    // CONSTRUCTORS
+    //////////////////////////////////////////////////
+    
+    //////////////////////////////////////////////////
+    // ACCESSOR METHODS
+    //////////////////////////////////////////////////
+
+    //////////////////////////////////////////////////
+    // PUBLIC INSTANCE METHODS
+    //////////////////////////////////////////////////
+    
+    //////////////////////////////////////////////////
+    // INTERFACE METHODS
+    //////////////////////////////////////////////////
+
+    //////////////////////////////////////////////////
+    // PROTECTED INSTANCE METHODS
+    //////////////////////////////////////////////////    
+    
+    //////////////////////////////////////////////////
+    // PRIVATE INSTANCE METHODS
+    //////////////////////////////////////////////////
+    
+    //////////////////////////////////////////////////
+    // STATIC INNER CLASSES
+    //////////////////////////////////////////////////
+
+    //////////////////////////////////////////////////
+    // NON-STATIC INNER CLASSES
+    //////////////////////////////////////////////////
+    
+}