[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.
git-svn-id: https://svn.apache.org/repos/asf/incubator/felix/trunk@434371 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/pom.xml b/pom.xml
index b8f338e..f5c9f14 100644
--- a/pom.xml
+++ b/pom.xml
@@ -80,6 +80,8 @@
<module>ipojo.metadata</module>
<module>ipojo.plugin</module>
<module>ipojo.arch</module>
+
+ <module>tools/mangen</module>
</modules>
<repositories>
diff --git a/tools/mangen/pom.xml b/tools/mangen/pom.xml
new file mode 100644
index 0000000..6fce2b4
--- /dev/null
+++ b/tools/mangen/pom.xml
@@ -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>
diff --git a/tools/mangen/src/main/java/org/apache/felix/tool/mangen/ASMClassScanner.java b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/ASMClassScanner.java
new file mode 100644
index 0000000..7a92d2b
--- /dev/null
+++ b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/ASMClassScanner.java
@@ -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;
+ }
+}
diff --git a/tools/mangen/src/main/java/org/apache/felix/tool/mangen/BCELScanner.java b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/BCELScanner.java
new file mode 100644
index 0000000..1adbfc7
--- /dev/null
+++ b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/BCELScanner.java
@@ -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
+ //////////////////////////////////////////////////
+
+}
diff --git a/tools/mangen/src/main/java/org/apache/felix/tool/mangen/BundleJar.java b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/BundleJar.java
new file mode 100644
index 0000000..cbe878e
--- /dev/null
+++ b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/BundleJar.java
@@ -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
+ //////////////////////////////////////////////////
+
+}
diff --git a/tools/mangen/src/main/java/org/apache/felix/tool/mangen/ClassScanner.java b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/ClassScanner.java
new file mode 100644
index 0000000..15e93e4
--- /dev/null
+++ b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/ClassScanner.java
@@ -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<classname>;</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);
+
+}
diff --git a/tools/mangen/src/main/java/org/apache/felix/tool/mangen/GenericHandler.java b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/GenericHandler.java
new file mode 100644
index 0000000..dcc7755
--- /dev/null
+++ b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/GenericHandler.java
@@ -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
+ //////////////////////////////////////////////////
+
+}
diff --git a/tools/mangen/src/main/java/org/apache/felix/tool/mangen/GenericHandlerItem.java b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/GenericHandlerItem.java
new file mode 100644
index 0000000..809dc54
--- /dev/null
+++ b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/GenericHandlerItem.java
@@ -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);
+
+}
diff --git a/tools/mangen/src/main/java/org/apache/felix/tool/mangen/GenericHandlerItemImpl.java b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/GenericHandlerItemImpl.java
new file mode 100644
index 0000000..d72fb40
--- /dev/null
+++ b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/GenericHandlerItemImpl.java
@@ -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
+ //////////////////////////////////////////////////
+
+}
diff --git a/tools/mangen/src/main/java/org/apache/felix/tool/mangen/MangenMain.java b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/MangenMain.java
new file mode 100644
index 0000000..881d07c
--- /dev/null
+++ b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/MangenMain.java
@@ -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
+ //////////////////////////////////////////////////
+
+}
diff --git a/tools/mangen/src/main/java/org/apache/felix/tool/mangen/OsgiPackage.java b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/OsgiPackage.java
new file mode 100644
index 0000000..49dcba6
--- /dev/null
+++ b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/OsgiPackage.java
@@ -0,0 +1,187 @@
+/*
+ * 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.Comparator;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+
+import org.apache.felix.framework.searchpolicy.*;
+import org.osgi.framework.Version;
+
+/**
+ * The OsgiPackage class is a wrapper for either simple string based package
+ * names or full Osgi package objects containing attributes and versions etc.
+ *
+ * @version $Revision: 31 $
+ * @author <A HREF="mailto:robw@ascert.com">Rob Walker</A>
+ */
+public abstract class OsgiPackage
+ implements Comparable
+{
+ //////////////////////////////////////////////////
+ // STATIC VARIABLES
+ //////////////////////////////////////////////////
+
+ public static Comparator pkgComparator = new PackageComparator();
+
+ //////////////////////////////////////////////////
+ // STATIC PUBLIC METHODS
+ //////////////////////////////////////////////////
+
+ /**
+ * Create a package wrapper for a simple package name string.
+ */
+ public static OsgiPackage createStringPackage(String pkgName)
+ {
+ return new OsgiStringPackage(pkgName);
+ }
+
+ /**
+ * Create a set of OSGi package wrappers by parsing the supplied import
+ * or export headers. At present, we only create true R4 packages for header
+ * strings which contain ";" (the header attribute separator char). All
+ * others we create as simple string packages
+ */
+ public static Set createFromHeaders(String headers)
+ {
+ Set set = getNewSet();
+
+ if (headers != null)
+ {
+ for (StringTokenizer tok = new StringTokenizer(headers, ","); tok.hasMoreTokens(); )
+ {
+ String pkgString = tok.nextToken().trim();
+
+ // look for presence of package attribute separator ';'
+ if (pkgString.indexOf(';') != -1)
+ {
+ // parse and add all R4 packages contained (can be multiple)
+ R4Package[] pkgs = R4Package.parseImportOrExportHeader(pkgString);
+ for (int ix = 0; ix < pkgs.length; ix++)
+ {
+ set.add(new OsgiR4Package(pkgs[ix]));
+ }
+ }
+ else
+ {
+ // treat as a simple string package name
+ set.add(new OsgiStringPackage(pkgString));
+ }
+ }
+ }
+
+ return set;
+ }
+
+ /**
+ * Creates and returns a new Set for holding package entries.
+ */
+ public static Set getNewSet()
+ {
+ return new TreeSet(pkgComparator);
+ }
+
+ //////////////////////////////////////////////////
+ // INSTANCE VARIABLES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // CONSTRUCTORS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // ACCESSOR METHODS
+ //////////////////////////////////////////////////
+
+ /**
+ * Return simple package name form without attributes
+ */
+ public abstract String getName();
+
+ /**
+ * Return specified package version, or <code>null</code> if none defined.
+ */
+ public abstract Version getVersion();
+
+ //////////////////////////////////////////////////
+ // PUBLIC INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ /**
+ * Very important to override standard equals method, since set based operations
+ * work using this method for comparisons
+ */
+ public boolean equals(Object o)
+ {
+ return compareTo(o) == 0 ? true : false;
+ }
+
+ //////////////////////////////////////////////////
+ // INTERFACE METHODS - Comparable
+ //////////////////////////////////////////////////
+
+ /**
+ * Just used for sorting in reports
+ */
+ public int compareTo(Object o)
+ {
+ return -1;
+ }
+
+ //////////////////////////////////////////////////
+ // PROTECTED INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // PRIVATE INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+ public static class PackageComparator
+ implements Comparator
+ {
+ public int compare(Object o1, Object o2)
+ {
+ int retval = -1;
+
+ if (o1 instanceof OsgiPackage)
+ {
+ retval = ((Comparable) o1).compareTo(o2);
+ }
+ else if (o2 instanceof OsgiPackage)
+ {
+ retval = ((Comparable) o2).compareTo(o1);
+ }
+ else
+ {
+ throw new ClassCastException("can't compare, neither o1 or o2 is an OsgiPackage");
+ }
+
+ return retval;
+ }
+ }
+
+ //////////////////////////////////////////////////
+ // NON-STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+}
diff --git a/tools/mangen/src/main/java/org/apache/felix/tool/mangen/OsgiR4Package.java b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/OsgiR4Package.java
new file mode 100644
index 0000000..e66db59
--- /dev/null
+++ b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/OsgiR4Package.java
@@ -0,0 +1,267 @@
+/*
+ * 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.Iterator;
+import java.util.Properties;
+
+import org.apache.felix.framework.searchpolicy.*;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+
+//TODO: need to look at VersionRange handling to see whether version checking
+// should be modified to cater for version ranges.
+/**
+ * Wrapper for a full R4 package name
+ *
+ * @version $Revision: 31 $
+ * @author <A HREF="mailto:robw@ascert.com">Rob Walker</A>
+ */
+public class OsgiR4Package
+ extends OsgiPackage
+{
+ //////////////////////////////////////////////////
+ // STATIC VARIABLES
+ //////////////////////////////////////////////////
+
+ public static String verString;
+
+ //////////////////////////////////////////////////
+ // STATIC PUBLIC METHODS
+ //////////////////////////////////////////////////
+
+ /**
+ * Compare two sets of packages versions.
+ */
+ public static int compareVersions(Version v1, Version v2)
+ {
+ int retval = -1;
+
+ if (v1 != null)
+ {
+ v1.compareTo(v2);
+ }
+ else if (v2 != null)
+ {
+ v2.compareTo(v1);
+ }
+ else
+ {
+ retval = 0;
+ }
+
+ return retval;
+ }
+
+ //////////////////////////////////////////////////
+ // INSTANCE VARIABLES
+ //////////////////////////////////////////////////
+
+ public R4Package pkg;
+
+ //////////////////////////////////////////////////
+ // CONSTRUCTORS
+ //////////////////////////////////////////////////
+
+ public OsgiR4Package(R4Package pkg)
+ {
+ this.pkg = pkg;
+
+ if (verString == null)
+ {
+ String val = PropertyManager.getProperty("mangen.osgi.level", "3");
+ if (val.equals("4"))
+ {
+ verString = Constants.VERSION_ATTRIBUTE;
+ }
+ else
+ {
+ verString = Constants.PACKAGE_SPECIFICATION_VERSION;
+ }
+ }
+ }
+
+ //////////////////////////////////////////////////
+ // ACCESSOR METHODS
+ //////////////////////////////////////////////////
+
+ /**
+ * Return the wrapped Oscar R4 package.
+ */
+ public R4Package getPackage()
+ {
+ return pkg;
+ }
+
+
+ public String getName()
+ {
+ return pkg.getName();
+ }
+
+
+ public Version getVersion()
+ {
+ Version ver = null;
+
+ // use 'version' attribute if present, fallback to getVersion if not
+ R4Attribute verAttr = getAttribute(verString);
+
+ if (verAttr != null)
+ {
+ String sVer = verAttr.getValue().toString();
+ ver = new Version(sVer);
+ }
+ else
+ {
+ Version v = pkg.getVersion();
+ if (!v.equals(Version.emptyVersion))
+ {
+ ver = v;
+ }
+ }
+
+ return ver;
+ }
+
+
+ //////////////////////////////////////////////////
+ // PUBLIC INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ /**
+ * Important to override standard toString method for pattern matching and
+ * manifest header value generation.
+ */
+ public String toString()
+ {
+ StringBuffer str = new StringBuffer(pkg.getName());
+
+ // bit over the top this - but ensures we preserve a version attribute
+ // specified as 1.2 rather than converting it to 1.2.0
+ R4Attribute verAttr = getAttribute(verString);
+ String sVer = null;
+
+ if (verAttr != null)
+ {
+ sVer = verAttr.getValue().toString();
+ }
+ else
+ {
+ Version v = getVersion();
+ if (v != null)
+ {
+ sVer = v.toString();
+ }
+ }
+
+ if (sVer != null)
+ {
+ str.append(";" + verString + "=");
+ str.append("\"" + sVer + "\"");
+ }
+
+ return str.toString();
+ }
+
+ //////////////////////////////////////////////////
+ // INTERFACE METHODS - Comparable
+ //////////////////////////////////////////////////
+
+ /**
+ * The Comparable interface is used as the basis for the OsgiPackage default
+ * implementation of equals() and also for the Comparator interface used to
+ * determine whether set members are present.
+ */
+ public int compareTo(Object o)
+ {
+ int retval = -1;
+
+ if (o instanceof OsgiR4Package)
+ {
+ OsgiR4Package otherPkg = (OsgiR4Package) o;
+ // first check for name match
+ retval = getName().compareTo(otherPkg.getName());
+
+ // check low versions match
+ if (retval == 0)
+ {
+ retval = compareVersions(getVersion(),
+ otherPkg.getVersion());
+ }
+ //TODO: Original Oscar2 mangen handled version ranges, may need to
+ // revisit this
+ // check high versions match
+ //if (retval == 0)
+ //{
+ // retval = compareVersions(pkg.getVersionHigh(),
+ // otherPkg.getVersionHigh());
+ //}
+ }
+ else if (o instanceof OsgiStringPackage || o instanceof String)
+ {
+ // simple package name comparison
+ retval = getName().compareTo(o.toString());
+ }
+ else
+ {
+ // prob won't do any better a job than we do, but fallback just in case
+ retval = super.compareTo(o);
+ }
+
+ return retval;
+ }
+
+ //////////////////////////////////////////////////
+ // PROTECTED INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ /**
+ * Get a specific attribute of the package.
+ */
+ public R4Attribute getAttribute(String name)
+ {
+ R4Attribute attrib = null;
+ R4Attribute[] attrs = pkg.getAttributes();
+
+ if (attrs != null)
+ {
+ for (int i = 0; (attrib == null) && (i < attrs.length); i++)
+ {
+ if (attrs[i].getName().equals(name))
+ {
+ attrib = attrs[i];
+ }
+ }
+ }
+
+ return attrib;
+ }
+
+ //////////////////////////////////////////////////
+ // PRIVATE INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // NON-STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+}
diff --git a/tools/mangen/src/main/java/org/apache/felix/tool/mangen/OsgiStringPackage.java b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/OsgiStringPackage.java
new file mode 100644
index 0000000..ad76a55
--- /dev/null
+++ b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/OsgiStringPackage.java
@@ -0,0 +1,144 @@
+/*
+ * 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.Iterator;
+import java.util.Properties;
+
+import org.apache.felix.framework.searchpolicy.*;
+import org.osgi.framework.Version;
+
+/**
+ * Wrapper for a simple string based package name.
+ *
+ * @version $Revision: 31 $
+ * @author <A HREF="mailto:robw@ascert.com">Rob Walker</A>
+ */
+public class OsgiStringPackage
+ extends OsgiPackage
+{
+ //////////////////////////////////////////////////
+ // STATIC VARIABLES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // STATIC PUBLIC METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // INSTANCE VARIABLES
+ //////////////////////////////////////////////////
+
+ public String pkgname;
+
+ //////////////////////////////////////////////////
+ // CONSTRUCTORS
+ //////////////////////////////////////////////////
+
+ public OsgiStringPackage(String pkgname)
+ {
+ this.pkgname = pkgname;
+ }
+
+ //////////////////////////////////////////////////
+ // ACCESSOR METHODS
+ //////////////////////////////////////////////////
+
+ public String getName()
+ {
+ return pkgname;
+ }
+
+ public Version getVersion()
+ {
+ return null;
+ }
+
+
+
+ //////////////////////////////////////////////////
+ // PUBLIC INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ /**
+ * Important to override standard toString method for pattern matching and
+ * manifest header value generation.
+ */
+ public String toString()
+ {
+ return pkgname;
+ }
+
+ /**
+ * Important to override for hash table/set key lookups. Basically just need
+ * to return hashcode of our underlying object.
+ */
+ public int hashCode()
+ {
+ return pkgname.hashCode();
+ }
+
+ //////////////////////////////////////////////////
+ // INTERFACE METHODS - Comparable
+ //////////////////////////////////////////////////
+
+ /**
+ * The Comparable interface is used as the basis for the OsgiPackage default
+ * implementation of equals() and also for the Comparator interface used to
+ * determine whether set members are present.
+ */
+ public int compareTo(Object o)
+ {
+ int retval = -1;
+
+ if (o instanceof OsgiR4Package)
+ {
+ // R4 package comparison is complicated, so invert the compare and
+ // let the R4 package do the work
+ retval = ((OsgiR4Package) o).compareTo(this);
+ }
+ else if (o instanceof OsgiStringPackage || o instanceof String)
+ {
+ // simple string compare will suffice
+ retval = pkgname.compareTo(o.toString());
+ }
+ else
+ {
+ // prob won't do any better a job than we do, but fallback just in case
+ retval = super.compareTo(o);
+ }
+
+ return retval;
+ }
+
+ //////////////////////////////////////////////////
+ // PROTECTED INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // PRIVATE INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // NON-STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+}
diff --git a/tools/mangen/src/main/java/org/apache/felix/tool/mangen/PropertyManager.java b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/PropertyManager.java
new file mode 100644
index 0000000..9823fa7
--- /dev/null
+++ b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/PropertyManager.java
@@ -0,0 +1,243 @@
+/*
+ * 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.FileInputStream;
+
+import java.util.Properties;
+
+/**
+ *
+ * @version $Revision: 14 $
+ * @author <A HREF="mailto:robw@ascert.com">Rob Walker</A>
+ */
+public class PropertyManager
+{
+ //////////////////////////////////////////////////
+ // STATIC VARIABLES
+ //////////////////////////////////////////////////
+
+ private static final String DELIM_START = "${";
+ private static final char DELIM_STOP = '}';
+ private static final int DELIM_START_LEN = 2;
+ private static final int DELIM_STOP_LEN = 1;
+
+ public static Properties props = new Properties();
+
+ //////////////////////////////////////////////////
+ // STATIC PUBLIC METHODS
+ //////////////////////////////////////////////////
+
+ /**
+ * Initialise the properties either from a file specified by -Dmangen.properties
+ * or if this is not specified, from the default lib\mangen.properties file.
+ */
+ public static void initProperties(String propFileKey)
+ throws Exception
+ {
+ String propsFile = System.getProperty(propFileKey);
+
+ if (propsFile != null)
+ {
+ props.load(new FileInputStream(propsFile));
+ }
+ else
+ {
+ getDefaultProperties(propFileKey);
+ }
+
+ if (props.size() == 0)
+ {
+ System.err.println("Warning: no mangen properties specified.");
+ }
+ }
+
+ /**
+ * Get the default mangen properties from lib\mangen.properties
+ */
+ public static void getDefaultProperties(String propFileKey)
+ {
+ // Determine where mangen.jar is located by looking at the system class path.
+ String jarLoc = null;
+ String classpath = System.getProperty("java.class.path");
+ int index = classpath.toLowerCase().indexOf("mangen.jar");
+ int start = classpath.lastIndexOf(File.pathSeparator, index) + 1;
+ if (index > start)
+ {
+ jarLoc = classpath.substring(start, index);
+ if (jarLoc.length() == 0)
+ {
+ jarLoc = ".";
+ }
+ }
+
+ // see if we can load from a default mangen.properties file
+ try
+ {
+ props.load(new FileInputStream(jarLoc + propFileKey));
+ }
+ catch (Exception e)
+ {
+ // ok to ignore, we'll report error in caller
+ }
+ }
+
+ /**
+ * Provides a wrapper into either System properties, if the specified
+ * property is present there, or our mangen.properties if not. As with the
+ * standartd getProperty call the default value is returned if the key is not
+ * present in either of these property sets. Variable substitution using
+ * ${var} style markers is also supported.
+ */
+ public static String getProperty(String key, String def)
+ {
+ String retval = null;
+
+ retval = System.getProperty(key);
+ if (retval == null)
+ {
+ retval = props.getProperty(key, def);
+ }
+
+ if (retval != null)
+ {
+ retval = substVars(retval, key);
+ }
+
+ return retval;
+ }
+
+ /**
+ * No default wrapper for getProperty.
+ */
+ public static String getProperty(String key)
+ {
+ return getProperty(key, null);
+ }
+
+
+ /**
+ * <p>
+ * This method performs property variable substitution on the
+ * specified string value. If the specified string contains the
+ * syntax <code>${<system-prop-name>}</code>, then the corresponding
+ * system property value is substituted for the marker.
+ * </p>
+ * @param val The string on which to perform system property substitution.
+ * @param currentKey The current key name, used to detect recursion
+ * @return The value of the specified string after system property substitution.
+ * @throws IllegalArgumentException If there was a syntax error in the
+ * system property variable marker syntax or recursion will occur.
+ **/
+ public static String substVars(String val, String currentKey)
+ throws IllegalArgumentException
+ {
+ StringBuffer sbuf = new StringBuffer();
+
+ if (val == null)
+ {
+ return val;
+ }
+
+ int i = 0;
+ int j, k;
+
+ while (true)
+ {
+ j = val.indexOf(DELIM_START, i);
+ if (j == -1)
+ {
+ if (i == 0)
+ {
+ return val;
+ }
+ else
+ {
+ sbuf.append(val.substring(i, val.length()));
+ return sbuf.toString();
+ }
+ }
+ else
+ {
+ sbuf.append(val.substring(i, j));
+ k = val.indexOf(DELIM_STOP, j);
+ if (k == -1)
+ {
+ throw new IllegalArgumentException(
+ '"' + val +
+ "\" has no closing brace. Opening brace at position "
+ + j + '.');
+ }
+ else
+ {
+ j += DELIM_START_LEN;
+ String key = val.substring(j, k);
+
+ if (key.equals(currentKey))
+ {
+ throw new IllegalArgumentException("recursive property substitution in: " + currentKey);
+ }
+ // Try system properties.
+ String replacement = getProperty(key, null);
+ if (replacement != null)
+ {
+ sbuf.append(replacement);
+ }
+ i = k + DELIM_STOP_LEN;
+ }
+ }
+ }
+ }
+
+ //////////////////////////////////////////////////
+ // INSTANCE VARIABLES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // CONSTRUCTORS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // ACCESSOR METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // PUBLIC INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // INTERFACE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // PROTECTED INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // PRIVATE INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // NON-STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+}
diff --git a/tools/mangen/src/main/java/org/apache/felix/tool/mangen/Report.java b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/Report.java
new file mode 100644
index 0000000..8c9337d
--- /dev/null
+++ b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/Report.java
@@ -0,0 +1,46 @@
+/*
+ * 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 java.io.PrintStream;
+
+import java.util.List;
+
+/**
+ *
+ * @version $Revision: 29 $
+ * @author <A HREF="mailto:robw@ascert.com">Rob Walker</A>
+ */
+public interface Report
+ extends GenericHandlerItem
+{
+ //////////////////////////////////////////////////
+ // STATIC VARIABLES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // SIGNATURES
+ //////////////////////////////////////////////////
+
+ /**
+ * Run Report for the specified list of BundleJars, generating output to
+ * specified PrintStream.
+ */
+ public void run(PrintStream rpt, List jarList)
+ throws IOException;
+}
diff --git a/tools/mangen/src/main/java/org/apache/felix/tool/mangen/ReportHandler.java b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/ReportHandler.java
new file mode 100644
index 0000000..97f4d92
--- /dev/null
+++ b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/ReportHandler.java
@@ -0,0 +1,108 @@
+/*
+ * 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 java.io.PrintStream;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.Properties;
+
+/**
+ *
+ * @version $Revision: 29 $
+ * @author <A HREF="mailto:robw@ascert.com">Rob Walker</A>
+ */
+public class ReportHandler
+ extends GenericHandler
+{
+ //////////////////////////////////////////////////
+ // STATIC VARIABLES
+ //////////////////////////////////////////////////
+
+ /** Classname prefix for basic report classes. */
+ public static final String REPORT_PACKAGE = "org.apache.felix.tool.mangen.report";
+ /** Report property prefix. **/
+ public static final String REPORT_KEY = "mangen-report-";
+
+ //////////////////////////////////////////////////
+ // STATIC PUBLIC METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // INSTANCE VARIABLES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // CONSTRUCTORS
+ //////////////////////////////////////////////////
+
+ /**
+ * Initialise the set of reports based on the REPORT_KEY.<n> properties defined.
+ * Each reports is either a full classname implementing the Report interface, or a
+ * classname in REPORT_PACKAGE. The actual work to process the properties and
+ * create the report objects is done by our parent class, GenericHandler.
+ */
+ public ReportHandler()
+ {
+ super(REPORT_KEY, Report.class, REPORT_PACKAGE);
+ }
+
+
+ //////////////////////////////////////////////////
+ // ACCESSOR METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // PUBLIC INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ /**
+ * Retrieve each defined report and run it.
+ */
+ public void runReports(PrintStream rpt, List jarList)
+ throws IOException
+ {
+ for(Iterator i = handlerList.iterator(); i.hasNext(); )
+ {
+ Report report = (Report) i.next();
+ report.run(rpt, jarList);
+ }
+ }
+
+ //////////////////////////////////////////////////
+ // INTERFACE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // PROTECTED INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // PRIVATE INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // NON-STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+}
diff --git a/tools/mangen/src/main/java/org/apache/felix/tool/mangen/Rule.java b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/Rule.java
new file mode 100644
index 0000000..11e4f25
--- /dev/null
+++ b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/Rule.java
@@ -0,0 +1,61 @@
+/*
+ * 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 java.io.PrintStream;
+
+import java.util.List;
+
+/**
+ *
+ * @version $Revision: 29 $
+ * @author <A HREF="mailto:robw@ascert.com">Rob Walker</A>
+ */
+public interface Rule
+ extends GenericHandlerItem
+{
+ //////////////////////////////////////////////////
+ // STATIC VARIABLES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // SIGNATURES
+ //////////////////////////////////////////////////
+
+ /**
+ * Execute the Rule on the supplied list of BundleJars.
+ */
+ public void execute(List jarList);
+
+ /**
+ * Send any output for the Rule to the specified PrintStream.
+ */
+ public void report(PrintStream rpt)
+ throws IOException;
+
+ /**
+ * Should return true if Rule can be used globally (i.e. in mangen.properties).
+ */
+ public boolean isUsableGlobally();
+
+ /**
+ * Should return true if Rule can be used locally (i.e. in a bundle's manifest).
+ */
+ public boolean isUsableLocally();
+
+}
diff --git a/tools/mangen/src/main/java/org/apache/felix/tool/mangen/RuleHandler.java b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/RuleHandler.java
new file mode 100644
index 0000000..c70b03c
--- /dev/null
+++ b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/RuleHandler.java
@@ -0,0 +1,220 @@
+/*
+ * 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 java.io.PrintStream;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+import java.util.jar.Attributes;
+
+/**
+ *
+ * @version $Revision: 31 $
+ * @author <A HREF="mailto:robw@ascert.com">Rob Walker</A>
+ */
+public class RuleHandler
+ extends GenericHandler
+{
+ //////////////////////////////////////////////////
+ // STATIC VARIABLES
+ //////////////////////////////////////////////////
+
+ /** Classname prefix for basic rule classes */
+ public static final String RULE_PACKAGE = "org.apache.felix.tool.mangen.rule";
+ /** Rule set property **/
+ public static final String RULESET_KEY = "mangen.rulesets";
+ /** Default rule property prefix **/
+ public static final String RULE_KEY = "mangen-rule-";
+
+ //////////////////////////////////////////////////
+ // STATIC PUBLIC METHODS
+ //////////////////////////////////////////////////
+
+ /**
+ * Initialise a set of rules to be run, based on the value of the
+ * mangen.rule property if present, or the default rule key if not.
+ */
+ public static RuleHandler[] initRuleSets()
+ {
+ String rulesetStr = PropertyManager.getProperty(RULESET_KEY, RULE_KEY);
+
+ StringTokenizer tok = new StringTokenizer(rulesetStr, ",");
+ RuleHandler[] handlers = new RuleHandler[tok.countTokens()];
+ for (int ix = 0; ix < handlers.length; ix++)
+ {
+ handlers[ix] = new RuleHandler(tok.nextToken().trim());
+ }
+
+ return handlers;
+ }
+
+ /**
+ * Run each specified ruleset.
+ */
+ public static void runRuleSets(RuleHandler[] handlers, List jarList)
+ {
+ for (int ix = 0; ix < handlers.length; ix++)
+ {
+ handlers[ix].executeRules(jarList);
+ }
+ }
+
+ /**
+ *
+ * Run reports for each specified ruleset.
+ */
+ public static void runRuleSetReports(RuleHandler[] handlers, PrintStream rpt, boolean header)
+ throws IOException
+ {
+ for (int ix = 0; ix < handlers.length; ix++)
+ {
+ handlers[ix].runReports(rpt, header);
+ }
+ }
+
+ //////////////////////////////////////////////////
+ // INSTANCE VARIABLES
+ //////////////////////////////////////////////////
+
+ public boolean isGlobalRule;
+
+ //////////////////////////////////////////////////
+ // CONSTRUCTORS
+ //////////////////////////////////////////////////
+
+ /**
+ * Initialise a rule handler usng the default RULE_KEY.
+ */
+ public RuleHandler()
+ {
+ this(RULE_KEY);
+ }
+
+ /**
+ * Initialise the set of rules based on the RULE_KEY.<n> properties defined.
+ * Each rule is either a full classname implementing the Rule interface, or a
+ * classname in RULE_PACKAGE. The actual work to process the properties and
+ * create the rule objects is done by our parent class, GenericHandler.
+ */
+ public RuleHandler(String key)
+ {
+ super(key, Rule.class, RULE_PACKAGE);
+ // for now, safe to assume Property set based rules are global
+ this.isGlobalRule = true;
+ }
+
+ /**
+ * Initialise a rule handler based on a set of Manifest attributes.
+ */
+ public RuleHandler(Attributes atts)
+ {
+ super(atts, RULE_KEY, Rule.class, RULE_PACKAGE);
+ // for now, safe to assume Attribute set based rules are local
+ this.isGlobalRule = false;
+ }
+
+ //////////////////////////////////////////////////
+ // ACCESSOR METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // PUBLIC INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ /**
+ * Retrieve each defined rule and apply it. At present we take the approach that
+ * each rule will be applied in it's entirety before proceeding to the next rule.
+ * For simple rules, this will result in multiple passes over
+ * the jarList set for simowhich obviously incurs a processing overhead, but it is felt
+ * that this simpler approach will have fewer nasty side-effects for more complex
+ * rules that span multiple bundle jars.
+ */
+ public void executeRules(List jarList)
+ {
+ for(Iterator i = handlerList.iterator(); i.hasNext(); )
+ {
+ Rule rule = (Rule) i.next();
+
+ if (isGlobalRule && !rule.isUsableGlobally())
+ {
+ throw new IllegalArgumentException("rule cannot be used globally: "
+ + rule.getClass().getName());
+ }
+
+ if (!isGlobalRule && !rule.isUsableLocally())
+ {
+ throw new IllegalArgumentException("rule cannot be used locally: "
+ + rule.getClass().getName());
+ }
+
+ rule.execute(jarList);
+ }
+ }
+
+ /**
+ * Run report for each rule. Bit of a hack for now!
+ */
+ public void runReports(PrintStream rpt, boolean header)
+ throws IOException
+ {
+ for(Iterator i = handlerList.iterator(); i.hasNext(); )
+ {
+ Rule rule = (Rule) i.next();
+
+ if (header)
+ {
+ rpt.println("");
+ rpt.println("============================================================");
+ rpt.println("Rule: " + rule.getClass().getName());
+ rpt.println("============================================================");
+ rpt.println("");
+ }
+ else
+ {
+ rpt.println("Rule: " + rule.getClass().getName());
+ }
+
+ rule.report(rpt);
+ }
+ }
+
+ //////////////////////////////////////////////////
+ // INTERFACE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // PROTECTED INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // PRIVATE INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // NON-STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+}
diff --git a/tools/mangen/src/main/java/org/apache/felix/tool/mangen/report/BundleReport.java b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/report/BundleReport.java
new file mode 100644
index 0000000..ffe3efb
--- /dev/null
+++ b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/report/BundleReport.java
@@ -0,0 +1,206 @@
+/*
+ * 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.report;
+
+import java.io.IOException;
+import java.io.PrintStream;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import java.util.regex.Pattern;
+
+import org.apache.felix.tool.mangen.BundleJar;
+import org.apache.felix.tool.mangen.OsgiPackage;
+import org.apache.felix.tool.mangen.Report;
+
+/**
+ *
+ * @version $Revision: 29 $
+ * @author <A HREF="mailto:robw@ascert.com">Rob Walker</A>
+ */
+public class BundleReport
+ implements Report
+{
+ //////////////////////////////////////////////////
+ // STATIC VARIABLES
+ //////////////////////////////////////////////////
+
+ public static Pattern[] pkgPatterns;
+
+ //////////////////////////////////////////////////
+ // STATIC PUBLIC METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // INSTANCE VARIABLES
+ //////////////////////////////////////////////////
+
+ public boolean optShowDiffs = false;
+ public boolean optShowLocalRules = false;
+
+ //////////////////////////////////////////////////
+ // CONSTRUCTORS
+ //////////////////////////////////////////////////
+
+ public BundleReport()
+ {
+ }
+
+ //////////////////////////////////////////////////
+ // ACCESSOR METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // PUBLIC INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // INTERFACE METHODS - Report
+ //////////////////////////////////////////////////
+
+ /**
+ */
+ public void run(PrintStream rpt, List jarList)
+ throws IOException
+ {
+ for(Iterator i = jarList.iterator(); i.hasNext(); )
+ {
+ BundleJar bund = (BundleJar) i.next();
+ doReport(rpt, bund);
+ }
+ }
+
+ //////////////////////////////////////////////////
+ // INTERFACE METHODS - GenericHandlerItem
+ //////////////////////////////////////////////////
+
+ /**
+ * Process the option set as a a space separated list of options.
+ *
+ * opt1 opt2
+ */
+ public void setOptions(String options)
+ {
+ for (StringTokenizer tok = new StringTokenizer(options, " "); tok.hasMoreTokens(); )
+ {
+ String opt = tok.nextToken().trim();
+ if (opt.compareToIgnoreCase("show-differences") == 0)
+ {
+ optShowDiffs = true;
+ }
+ else if (opt.compareToIgnoreCase("show-local-rules") == 0)
+ {
+ optShowLocalRules = true;
+ }
+ }
+ }
+
+ //////////////////////////////////////////////////
+ // PROTECTED INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ /**
+ * Produce a simple report on the current state of the imports and exports
+ * for the specified bundle's jar.
+ */
+ protected void doReport(PrintStream rpt, BundleJar bund)
+ throws IOException
+ {
+ rpt.println("");
+ rpt.println("============================================================");
+ rpt.println(bund.getName());
+ rpt.println("============================================================");
+ rpt.println("");
+
+ if (optShowLocalRules && bund.bundleRuleHandler != null)
+ {
+ rpt.println("Local bundle rules:");
+ bund.bundleRuleHandler.runReports(rpt, false);
+ }
+
+ if (optShowDiffs)
+ {
+ rpt.println("");
+ rpt.println("mangen import differences:");
+ showDiffs(bund.getPossibleImports(), bund.getCurrentImports(), rpt);
+
+ rpt.println("");
+ rpt.println("mangen export differences:");
+ showDiffs(bund.getPossibleExports(), bund.getCurrentExports(), rpt);
+ }
+ else
+ {
+ rpt.println("");
+ rpt.println("mangen proposed imports:");
+ Set imports = bund.getPossibleImports();
+ for(Iterator i = imports.iterator(); i.hasNext(); )
+ {
+ rpt.println(" " + (OsgiPackage) i.next());
+ }
+
+ rpt.println("");
+ rpt.println("mangen proposed exports:");
+ Set exports = bund.getPossibleExports();
+ for(Iterator i = exports.iterator(); i.hasNext(); )
+ {
+ rpt.println(" " + (OsgiPackage) i.next());
+ }
+
+ rpt.println("");
+ }
+ }
+
+ /**
+ * Show the differences in the generated Set of packages and the current Set.
+ */
+ protected void showDiffs(Set genSet, Set currSet, PrintStream rpt)
+ {
+ for(Iterator i = genSet.iterator(); i.hasNext(); )
+ {
+ OsgiPackage newPkg = (OsgiPackage) i.next();
+ if (!currSet.contains(newPkg))
+ {
+ rpt.println(" +++ ADDED :" + newPkg);
+ }
+ }
+
+ for(Iterator i = currSet.iterator(); i.hasNext(); )
+ {
+ OsgiPackage oldPkg = (OsgiPackage) i.next();
+ if (!genSet.contains(oldPkg))
+ {
+ rpt.println(" --- REMOVED :" + oldPkg);
+ }
+ }
+ }
+
+ //////////////////////////////////////////////////
+ // PRIVATE INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // NON-STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+}
diff --git a/tools/mangen/src/main/java/org/apache/felix/tool/mangen/report/ObrReport.java b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/report/ObrReport.java
new file mode 100644
index 0000000..e749be1
--- /dev/null
+++ b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/report/ObrReport.java
@@ -0,0 +1,234 @@
+/*
+ * 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.report;
+
+import java.io.IOException;
+import java.io.PrintStream;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.apache.felix.tool.mangen.BundleJar;
+import org.apache.felix.tool.mangen.GenericHandlerItemImpl;
+import org.apache.felix.tool.mangen.OsgiPackage;
+import org.apache.felix.tool.mangen.PropertyManager;
+import org.apache.felix.tool.mangen.Report;
+
+/**
+ *
+ * Produce a report for each bundle that can be used as an OBR descriptor
+ *
+ * @version $Revision: 14 $
+ * @author <A HREF="mailto:robw@ascert.com">Rob Walker</A>
+ */
+public class ObrReport
+ extends GenericHandlerItemImpl
+ implements Report
+{
+ //////////////////////////////////////////////////
+ // STATIC VARIABLES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // STATIC PUBLIC METHODS
+ //////////////////////////////////////////////////
+
+ public static String getObrProperty(String key)
+ {
+ String descrKey = key + "." + PropertyManager.getProperty("mangen.obr.ver", "1");
+ return PropertyManager.getProperty(descrKey, "");
+ }
+
+ //////////////////////////////////////////////////
+ // INSTANCE VARIABLES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // CONSTRUCTORS
+ //////////////////////////////////////////////////
+
+ public ObrReport()
+ {
+ }
+
+ //////////////////////////////////////////////////
+ // ACCESSOR METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // PUBLIC INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // INTERFACE METHODS - Report
+ //////////////////////////////////////////////////
+
+ /**
+ */
+ public void run(PrintStream rpt, List jarList)
+ throws IOException
+ {
+ for(Iterator i = jarList.iterator(); i.hasNext(); )
+ {
+ BundleJar bund = (BundleJar) i.next();
+ // only process JARs that don't match exclusion names
+ if (!isJarNameMatch(bund.getName(), "skip-jars"))
+ {
+ doReport(rpt, bund);
+ }
+ }
+ }
+
+ //////////////////////////////////////////////////
+ // PROTECTED INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ /**
+ * Produce a simple report on the current state of the imports and exports
+ * for the specified bundle's jar.
+ */
+ protected void doReport(PrintStream rpt, BundleJar bund)
+ throws IOException
+ {
+ String descrText = getObrProperty("mangen.obr.descr");
+ rpt.println(expandTags(descrText, bund, null));
+ }
+
+ /**
+ * Expands the tags in the OBR descriptor text. General method for
+ * tag expansion. Will be called either from main bundle iteration
+ * loop to expand the complete OBR descriptor template, and also recursively from
+ * within individual tags to expand package specific template details.
+ */
+ protected String expandTags(String descr, BundleJar bund, OsgiPackage pkg)
+ {
+ StringBuffer expanded = new StringBuffer();
+
+ int off = 0;
+ int tagPos = descr.indexOf("@@", off);
+
+ while (tagPos != -1)
+ {
+ expanded.append(descr.substring(off, tagPos));
+ off = descr.indexOf("@@", tagPos+2);
+ processTag(descr.substring(tagPos+2, off), expanded, bund, pkg);
+ off += 2;
+ tagPos = descr.indexOf("@@", off);
+ }
+ // final segment
+ expanded.append(descr.substring(off));
+ return expanded.toString();
+ }
+
+
+ /**
+ * Expand single tag in the OBR descriptor text
+ */
+ protected void processTag(String tag, StringBuffer buf, BundleJar bund, OsgiPackage pkg)
+ {
+ String tagVal = "";
+
+ if (tag.startsWith("hdr:"))
+ {
+ tagVal = bund.getManifestHeader(tag.substring(4), true);
+ }
+ else if (tag.equals("imports"))
+ {
+ Set imports = bund.getPossibleImports();
+ String template = getObrProperty("mangen.obr.import");
+ tagVal = expandPackageRefs(imports, template);
+ }
+ else if (tag.equals("exports"))
+ {
+ Set exports = bund.getPossibleExports();
+ String template = getObrProperty("mangen.obr.export");
+ tagVal = expandPackageRefs(exports, template);
+ }
+ else if (tag.equals("import-ver"))
+ {
+ // only include this if the version is non-null
+ if (pkg != null && pkg.getVersion() != null)
+ {
+ // recurse for version template
+ String template = getObrProperty("mangen.obr.import.ver");
+ tagVal = expandTags(template, null, pkg);
+ }
+ }
+ else if (tag.equals("export-ver"))
+ {
+ if (pkg != null && pkg.getVersion() != null)
+ {
+ // recurse for version template
+ String template = getObrProperty("mangen.obr.export.ver");
+ tagVal = expandTags(template, null, pkg);
+ }
+ }
+ else if (tag.equals("pkg:name"))
+ {
+ // this tag should only appear in templates where we've a package
+ if (pkg != null)
+ {
+ tagVal = pkg.getName();
+ }
+ }
+ else if (tag.equals("pkg:ver"))
+ {
+ // this tag should only appear in templates where we've a package
+ if (pkg != null && pkg.getVersion() != null)
+ {
+ tagVal = pkg.getVersion().toString();
+ }
+ }
+
+ if (tagVal != null)
+ {
+ buf.append(tagVal);
+ }
+ }
+
+ /**
+ * Iterate over a set of package refs and expand each one using the
+ * supplied template.
+ */
+ protected String expandPackageRefs(Set pkgSet, String template)
+ {
+ StringBuffer expanded = new StringBuffer();
+
+ for(Iterator i = pkgSet.iterator(); i.hasNext(); )
+ {
+ OsgiPackage pkg = (OsgiPackage) i.next();
+ // recurse to expand tags, but this time only need to supply package
+ expanded.append(expandTags(template, null, pkg));
+ }
+
+ return expanded.toString();
+ }
+
+ //////////////////////////////////////////////////
+ // PRIVATE INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // NON-STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+}
diff --git a/tools/mangen/src/main/java/org/apache/felix/tool/mangen/report/RuleReport.java b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/report/RuleReport.java
new file mode 100644
index 0000000..a1d2475
--- /dev/null
+++ b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/report/RuleReport.java
@@ -0,0 +1,108 @@
+/*
+ * 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.report;
+
+import java.io.IOException;
+import java.io.PrintStream;
+
+import java.util.List;
+
+import java.util.regex.Pattern;
+
+import org.apache.felix.tool.mangen.MangenMain;
+import org.apache.felix.tool.mangen.Report;
+import org.apache.felix.tool.mangen.RuleHandler;
+
+/**
+ *
+ * @version $Revision: 29 $
+ * @author <A HREF="mailto:robw@ascert.com">Rob Walker</A>
+ */
+public class RuleReport
+ implements Report
+{
+ //////////////////////////////////////////////////
+ // STATIC VARIABLES
+ //////////////////////////////////////////////////
+
+ public static Pattern[] pkgPatterns;
+
+ //////////////////////////////////////////////////
+ // STATIC PUBLIC METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // INSTANCE VARIABLES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // CONSTRUCTORS
+ //////////////////////////////////////////////////
+
+ public RuleReport()
+ {
+ }
+
+ //////////////////////////////////////////////////
+ // ACCESSOR METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // PUBLIC INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // INTERFACE METHODS - Report
+ //////////////////////////////////////////////////
+
+ /**
+ */
+ public void run(PrintStream rpt, List jarList)
+ throws IOException
+ {
+ //TODO: bit of a hack for now!
+ RuleHandler.runRuleSetReports(MangenMain.ruleSets, rpt, true);
+ }
+
+ //////////////////////////////////////////////////
+ // INTERFACE METHODS - GenericHandlerItem
+ //////////////////////////////////////////////////
+
+ /**
+ */
+ public void setOptions(String options)
+ {
+ //TODO: including checking of wildcard matching options
+ }
+
+ //////////////////////////////////////////////////
+ // PROTECTED INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // PRIVATE INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // NON-STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+}
diff --git a/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/AttributeStamp.java b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/AttributeStamp.java
new file mode 100644
index 0000000..3847d7e
--- /dev/null
+++ b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/AttributeStamp.java
@@ -0,0 +1,187 @@
+/*
+ * 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.rule;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.apache.felix.tool.mangen.MangenMain;
+import org.apache.felix.tool.mangen.OsgiPackage;
+import org.apache.felix.tool.mangen.OsgiStringPackage;
+import org.apache.felix.tool.mangen.OsgiR4Package;
+import org.apache.felix.tool.mangen.BundleJar;
+import org.apache.felix.tool.mangen.Rule;
+
+/**
+ * Rule to match package name strings against a wildcard pattern and them stamp the
+ * package with additional OSGi attributes e.g. version information.
+ *
+ * @version $Revision: 29 $
+ * @author <A HREF="mailto:robw@ascert.com">Rob Walker</A>
+ */
+public class AttributeStamp
+ extends GenericRule
+{
+ //////////////////////////////////////////////////
+ // STATIC VARIABLES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // STATIC PUBLIC METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // INSTANCE VARIABLES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // CONSTRUCTORS
+ //////////////////////////////////////////////////
+
+ public AttributeStamp()
+ {
+ }
+
+ //////////////////////////////////////////////////
+ // ACCESSOR METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // PUBLIC INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // INTERFACE METHODS - Rule
+ //////////////////////////////////////////////////
+
+ /**
+ * Iterate over the list of bundles and for each bundle remove any import
+ * which matches an export in the same bundle.
+ */
+ public void execute(List jarList)
+ {
+ for(Iterator i = jarList.iterator(); i.hasNext(); )
+ {
+ BundleJar bund = (BundleJar) i.next();
+ rptOut.println("");
+ rptOut.println("> " + bund.getName() + " :");
+
+ stampPackages(bund.getPossibleImports(), "imports");
+ stampPackages(bund.getPossibleExports(), "exports");
+ }
+ }
+
+ /**
+ * This rule can be used globally.
+ */
+ public boolean isUsableGlobally()
+ {
+ return true;
+ }
+
+ /**
+ * This rule can be used locally.
+ */
+ public boolean isUsableLocally()
+ {
+ return true;
+ }
+
+ //////////////////////////////////////////////////
+ // PROTECTED INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ /**
+ * Any string packages from the specified set which match package patterns in the
+ * specified qualifier set will have the additional OSGi attributes "stamped"
+ * onto their package specification. Packages that already have attributes which
+ * match will generate warnings and be skipped.
+ *
+ */
+ protected void stampPackages(Set set, String qualName)
+ {
+ rptOut.println("");
+ rptOut.println("... stamping packages in " + qualName);
+ for(Iterator i = set.iterator(); i.hasNext(); )
+ {
+ OsgiPackage pkg = (OsgiPackage) i.next();
+ String stamp = getMatchingPatternString(pkg, qualName, true);
+ if (stamp != null)
+ {
+ stamp(pkg, stamp, set);
+ }
+ }
+ }
+
+ /**
+ * Stamp the supplied package with the specified attributes. This will be
+ * an error if the package is already an R4 pakage with conflicting attributes.
+ */
+ protected void stamp(OsgiPackage pkg, String stamp, Set set)
+ {
+ int delim = stamp.indexOf(";");
+
+ if (delim == -1)
+ {
+ MangenMain.warning(rptOut, "*** WARNING *** stamp has no attributes: " + stamp);
+ return;
+ }
+
+ // simple thing is to rebuild an OSGi header with attributes and use OsgiPackage
+ // methods to parse this into an R4 package.
+ String hdr = pkg.getName() + stamp.substring(delim);
+ OsgiPackage[] newPkgs = (OsgiPackage[]) OsgiPackage.createFromHeaders(hdr).toArray(new OsgiPackage[0]);
+ if (newPkgs.length != 1)
+ {
+ MangenMain.error(rptOut, "*** ERROR *** stamp doesn't create a single package : " + stamp);
+ return;
+ }
+ OsgiPackage stampedPkg = newPkgs[0];
+
+ // replace a simple string package with the stamped package
+ if (pkg instanceof OsgiStringPackage)
+ {
+ set.remove(pkg);
+ set.add(stampedPkg);
+ rptOut.println(" > replacing : " + pkg + " with : " + stampedPkg);
+ return;
+
+ }
+
+ // it's an error to try and stamp an existing package with different details
+ if (pkg instanceof OsgiR4Package && pkg.compareTo(stampedPkg) != 0)
+ {
+ MangenMain.error(rptOut, "*** ERROR *** stamp has conflicting package details: " + pkg
+ + " != " + stampedPkg);
+ return;
+ }
+ }
+
+ //////////////////////////////////////////////////
+ // PRIVATE INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // NON-STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+}
diff --git a/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/DontImportOwnExports.java b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/DontImportOwnExports.java
new file mode 100644
index 0000000..58054f7
--- /dev/null
+++ b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/DontImportOwnExports.java
@@ -0,0 +1,151 @@
+/*
+ * 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.rule;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.apache.felix.tool.mangen.OsgiPackage;
+import org.apache.felix.tool.mangen.BundleJar;
+import org.apache.felix.tool.mangen.Rule;
+
+/**
+ * Rule to exclude each bundle's own exports from it's list of possible imports.
+ * <p>
+ * Any packages which are exported by a bundle in general do not need to also
+ * be imported and hence can be excluded from the list of possible imports.
+ * <p>
+ * Note that with OSGi R4 comes support for a bundle to import versions of it's
+ * own packages from an alternate bundle. This may require enhancements to this
+ * rule, or possibly extra rules.
+ *
+ * @version $Revision: 29 $
+ * @author <A HREF="mailto:robw@ascert.com">Rob Walker</A>
+ */
+public class DontImportOwnExports
+ extends GenericRule
+{
+ //////////////////////////////////////////////////
+ // STATIC VARIABLES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // STATIC PUBLIC METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // INSTANCE VARIABLES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // CONSTRUCTORS
+ //////////////////////////////////////////////////
+
+ public DontImportOwnExports()
+ {
+ }
+
+ //////////////////////////////////////////////////
+ // ACCESSOR METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // PUBLIC INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // INTERFACE METHODS - Rule
+ //////////////////////////////////////////////////
+
+ /**
+ * Iterate over the list of bundles and for each bundle remove any import
+ * which matches an export in the same bundle.
+ */
+ public void execute(List jarList)
+ {
+ for(Iterator i = jarList.iterator(); i.hasNext(); )
+ {
+ BundleJar bund = (BundleJar) i.next();
+ rptOut.println("");
+ rptOut.println("> " + bund.getName() + " :");
+
+ Set exports = bund.getPossibleExports();
+ Set imports = bund.getPossibleImports();
+ for(Iterator j = exports.iterator(); j.hasNext(); )
+ {
+ remove(imports, (OsgiPackage) j.next());
+ }
+ }
+ }
+
+ /**
+ * This rule can be used globally.
+ */
+ public boolean isUsableGlobally()
+ {
+ return true;
+ }
+
+ /**
+ * This rule can be used locally.
+ */
+ public boolean isUsableLocally()
+ {
+ return true;
+ }
+
+ //////////////////////////////////////////////////
+ // INTERFACE METHODS - GenericHandlerItem
+ //////////////////////////////////////////////////
+
+ /**
+ * No required options for this rule.
+ */
+ public void setOptions(String options)
+ {
+ }
+
+ //////////////////////////////////////////////////
+ // PROTECTED INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ /**
+ * Removes any import which is an exact match for the supplied package.
+ */
+ protected void remove(Set set, OsgiPackage pkg)
+ {
+ if (set.contains(pkg))
+ {
+ rptOut.println("... removing import of own export: " + pkg);
+ set.remove(pkg);
+ }
+ }
+
+ //////////////////////////////////////////////////
+ // PRIVATE INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // NON-STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+}
diff --git a/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/GenericRule.java b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/GenericRule.java
new file mode 100644
index 0000000..c0ed6ef
--- /dev/null
+++ b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/GenericRule.java
@@ -0,0 +1,94 @@
+/*
+ * 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.rule;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+
+import org.apache.felix.tool.mangen.GenericHandlerItemImpl;
+import org.apache.felix.tool.mangen.Rule;
+
+/**
+ * Holder for common Rule handling methods, such as reporting
+ *
+ * @version $Revision: 27 $
+ * @author <A HREF="mailto:robw@ascert.com">Rob Walker</A>
+ */
+public abstract class GenericRule
+ extends GenericHandlerItemImpl
+ implements Rule
+{
+ //////////////////////////////////////////////////
+ // STATIC VARIABLES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // STATIC PUBLIC METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // INSTANCE VARIABLES
+ //////////////////////////////////////////////////
+
+ /** Holds buffered rule output in case a rule report is requested */
+ public ByteArrayOutputStream rptBuf = new ByteArrayOutputStream();
+ public PrintStream rptOut = new PrintStream(rptBuf);
+
+ //////////////////////////////////////////////////
+ // CONSTRUCTORS
+ //////////////////////////////////////////////////
+
+ public GenericRule()
+ {
+ }
+
+ //////////////////////////////////////////////////
+ // ACCESSOR METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // PUBLIC INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // INTERFACE METHODS - Rule
+ //////////////////////////////////////////////////
+
+ public void report(PrintStream rpt)
+ throws IOException
+ {
+ rptBuf.writeTo(rpt);
+ }
+
+ //////////////////////////////////////////////////
+ // PROTECTED INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // PRIVATE INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // NON-STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+}
diff --git a/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/Ignore.java b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/Ignore.java
new file mode 100644
index 0000000..07f6900
--- /dev/null
+++ b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/Ignore.java
@@ -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.rule;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.apache.felix.tool.mangen.BundleJar;
+import org.apache.felix.tool.mangen.OsgiPackage;
+
+/**
+ * Rule to exclude specific packages from each bundle's possible imports.
+ * <p>
+ * Package names that match any of a list of regex based package name patterns
+ * will be excluded from the list of possible imports.
+ *
+ * @version $Revision: 29 $
+ * @author <A HREF="mailto:robw@ascert.com">Rob Walker</A>
+ */
+public class Ignore
+ extends GenericRule
+{
+ //////////////////////////////////////////////////
+ // STATIC VARIABLES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // STATIC PUBLIC METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // INSTANCE VARIABLES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // CONSTRUCTORS
+ //////////////////////////////////////////////////
+
+ public Ignore()
+ {
+ }
+
+ //////////////////////////////////////////////////
+ // ACCESSOR METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // PUBLIC INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // INTERFACE METHODS - Rule
+ //////////////////////////////////////////////////
+
+ /**
+ * Iterate over the list of bundle jars, removing any imports
+ * which match any of the specific package name Patterns
+ */
+ public void execute(List jarList)
+ {
+ for(Iterator i = jarList.iterator(); i.hasNext(); )
+ {
+ BundleJar bund = (BundleJar) i.next();
+ rptOut.println("");
+ rptOut.println("> " + bund.getName() + " :");
+
+ removeMatchingPackages(bund.getPossibleImports(), "imports");
+ removeMatchingPackages(bund.getPossibleExports(), "exports");
+ }
+ }
+
+ /**
+ * This rule can be used globally.
+ */
+ public boolean isUsableGlobally()
+ {
+ return true;
+ }
+
+ /**
+ * This rule can be used locally.
+ */
+ public boolean isUsableLocally()
+ {
+ return true;
+ }
+
+ //////////////////////////////////////////////////
+ // PROTECTED INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ /**
+ * Removes all packages from the set which match package patterns in the
+ * specified qualifier set.
+ */
+ public void removeMatchingPackages(Set set, String qualName)
+ {
+ for(Iterator i = set.iterator(); i.hasNext(); )
+ {
+ OsgiPackage pkg = (OsgiPackage) i.next();
+ if (isPackageMatch(pkg, qualName))
+ {
+ rptOut.println("... removing ignored package from " + qualName + " : " + pkg);
+ i.remove();
+ }
+ }
+ }
+
+
+ //////////////////////////////////////////////////
+ // PRIVATE INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // NON-STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+}
diff --git a/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/Merge.java b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/Merge.java
new file mode 100644
index 0000000..60e8cae
--- /dev/null
+++ b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/Merge.java
@@ -0,0 +1,230 @@
+/*
+ * 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.rule;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.apache.felix.tool.mangen.BundleJar;
+import org.apache.felix.tool.mangen.OsgiPackage;
+import org.apache.felix.tool.mangen.OsgiStringPackage;
+import org.apache.felix.tool.mangen.OsgiR4Package;
+
+/**
+ * Rule to merge the current mangenn mangen created import and/or export package sets
+ * with some other package set.
+ * <p>
+ * At present the possible other package sets are:
+ * <ul>
+ * <li> existing - existing <code>Export-Package</code> or <code>Import-Package</code> statements in the
+ * current manifest
+ * <li> fixed - <code>Export-Package</code> or <code>Import-Package</code> in the mangen section of
+ * the current manifest
+ * </ul>
+ * Package names that match any of a list of regex based package name patterns
+ * will be merged into the mangen generated list of possible imports or exports
+ * (as appropriate).
+ *
+ * @version $Revision: 29 $
+ * @author <A HREF="mailto:robw@ascert.com">Rob Walker</A>
+ */
+public class Merge
+ extends GenericRule
+{
+ //////////////////////////////////////////////////
+ // STATIC VARIABLES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // STATIC PUBLIC METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // INSTANCE VARIABLES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // CONSTRUCTORS
+ //////////////////////////////////////////////////
+
+ public Merge()
+ {
+ }
+
+ //////////////////////////////////////////////////
+ // ACCESSOR METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // PUBLIC INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ public boolean existing = false;
+
+ public boolean fixed = false;
+
+ //////////////////////////////////////////////////
+ // INTERFACE METHODS - Rule
+ //////////////////////////////////////////////////
+
+ /**
+ * Iterate over the list of bundle jars, removing any imports
+ * which match any of the specific package name Patterns
+ */
+ public void execute(List jarList)
+ {
+ for(Iterator i = jarList.iterator(); i.hasNext(); )
+ {
+ BundleJar bund = (BundleJar) i.next();
+
+ if (existing)
+ {
+ rptOut.println("");
+ rptOut.println("> " + bund.getName() + " existing package set:");
+ mergePackages(bund.getCurrentImports(), "imports", bund.getPossibleImports());
+ mergePackages(bund.getCurrentExports(), "exports", bund.getPossibleExports());
+ }
+
+ if (fixed)
+ {
+ rptOut.println("");
+ rptOut.println("> " + bund.getName() + " fixed package set:");
+ mergePackages(bund.getFixedImports(), "imports", bund.getPossibleImports());
+ mergePackages(bund.getFixedExports(), "exports", bund.getPossibleExports());
+ }
+
+ }
+ }
+
+ /**
+ * This rule can be used globally.
+ */
+ public boolean isUsableGlobally()
+ {
+ return true;
+ }
+
+ /**
+ * This rule can be used locally.
+ */
+ public boolean isUsableLocally()
+ {
+ return true;
+ }
+
+ //////////////////////////////////////////////////
+ // PROTECTED INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ /**
+ * Overrides superclass version to look for 'existing' and 'fixed' option
+ * qualifiers
+ */
+ protected void processNonStandardQualifier(String qual)
+ {
+ if (qual.compareToIgnoreCase("existing") == 0 )
+ {
+ existing = true;
+ }
+ else if (qual.compareToIgnoreCase("fixed") == 0)
+ {
+ fixed = true;
+ }
+ }
+
+ /**
+ * Any packages from the specified current set which match package patterns in the
+ * specified qualifier set will be merged into the specified mangen set.
+ *
+ * In the case where the current package does not already exist in the mangen set,
+ * merging is a simple matter of adding the package.
+ *
+ * In the case where the current package is already present in the mangen set, then
+ * a decision is needed on how to merge any version information present on the
+ * package entry in either set.
+ */
+ protected void mergePackages(Set currentSet, String qualName, Set mangenSet)
+ {
+ rptOut.println("");
+ rptOut.println("... merge to " + qualName);
+ for(Iterator i = currentSet.iterator(); i.hasNext(); )
+ {
+ OsgiPackage pkg = (OsgiPackage) i.next();
+ if (isPackageMatch(pkg, qualName))
+ {
+ merge(pkg, mangenSet);
+ }
+ }
+ }
+
+ /**
+ * Merge the supplied package into the specified set.
+ */
+ protected void merge(OsgiPackage pkgToMerge, Set set)
+ {
+ // oops, set doesn't have a "get". Guess we could move to using Map
+ // but Sets are working for most other cases, so find by hand for now.
+ OsgiPackage existingPkg = getPackageFromSet(pkgToMerge, set);
+
+ // if not present in any form, then we can add
+ if (existingPkg == null)
+ {
+ set.add(pkgToMerge);
+ rptOut.println(" > adding : " + pkgToMerge);
+ return;
+ }
+
+ // There's no added value in merging string packages to anything already present
+ // since they carry no extra package attributes
+ if (pkgToMerge instanceof OsgiStringPackage)
+ {
+ return;
+ }
+
+ // string packages can be replaced with R4 package, which will have
+ // extended package attributes.
+ if (existingPkg instanceof OsgiStringPackage)
+ {
+ set.remove(existingPkg);
+ set.add(pkgToMerge);
+ rptOut.println(" > replacing : " + existingPkg + " with : " + pkgToMerge);
+ return;
+ }
+
+ // if present and don't match then throw error
+ if (existingPkg instanceof OsgiR4Package && existingPkg.compareTo(pkgToMerge) != 0)
+ {
+ rptOut.println("*** ERROR *** can't merge conflicting package details: " + pkgToMerge
+ + " != " + existingPkg);
+ return;
+ }
+ }
+
+ //////////////////////////////////////////////////
+ // PRIVATE INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // NON-STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+}
diff --git a/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/ProcessBundles.java b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/ProcessBundles.java
new file mode 100644
index 0000000..e957a95
--- /dev/null
+++ b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/ProcessBundles.java
@@ -0,0 +1,127 @@
+/*
+ * 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.rule;
+
+import java.io.IOException;
+
+import java.util.List;
+import java.util.Iterator;
+
+import org.apache.felix.tool.mangen.MangenMain;
+import org.apache.felix.tool.mangen.BundleJar;
+
+/**
+ * Rule to caused each Bundle JAR to be processed. This will involved scanning
+ * the classes and inner jars within each bundle JAR to determine the range of
+ * possible imports and exports.
+ *
+ * @version $Revision: 18 $
+ * @author <A HREF="mailto:robw@ascert.com">Rob Walker</A>
+ */
+public class ProcessBundles
+ extends GenericRule
+{
+ //////////////////////////////////////////////////
+ // STATIC VARIABLES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // STATIC PUBLIC METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // INSTANCE VARIABLES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // CONSTRUCTORS
+ //////////////////////////////////////////////////
+
+ public ProcessBundles()
+ {
+ }
+
+ //////////////////////////////////////////////////
+ // ACCESSOR METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // PUBLIC INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // INTERFACE METHODS - Rule
+ //////////////////////////////////////////////////
+
+ /**
+ * Iterate over the list of bundles and ask each bundle to process it's
+ * contained classes and inner JARs.
+ */
+ public void execute(List jarList)
+ {
+ for(Iterator i = jarList.iterator(); i.hasNext(); )
+ {
+ BundleJar bund = (BundleJar) i.next();
+ try
+ {
+ bund.process();
+ }
+ catch (RuntimeException re)
+ {
+ MangenMain.error(rptOut, "Exception: " + re + ", skipping bundle jar: " + bund.getName());
+ re.printStackTrace(rptOut);
+ }
+ catch (Exception e)
+ {
+ MangenMain.error(rptOut, "Exception: " + e + ", skipping bundle jar: " + bund.getName());
+ }
+ }
+ }
+
+ /**
+ * This rule can be used globally.
+ */
+ public boolean isUsableGlobally()
+ {
+ return true;
+ }
+
+ /**
+ * This rule cannot be used locally.
+ */
+ public boolean isUsableLocally()
+ {
+ return false;
+ }
+
+ //////////////////////////////////////////////////
+ // PROTECTED INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // PRIVATE INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // NON-STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+}
diff --git a/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/ResolveImportsToExports.java b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/ResolveImportsToExports.java
new file mode 100644
index 0000000..7213254
--- /dev/null
+++ b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/ResolveImportsToExports.java
@@ -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.rule;
+
+import java.util.List;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.apache.felix.tool.mangen.MangenMain;
+import org.apache.felix.tool.mangen.BundleJar;
+import org.apache.felix.tool.mangen.OsgiPackage;
+
+/**
+ * Rule to resolve the set of imports and exports across all bundles.
+ * <p>
+ * Exports that are not required to satisfy any bundle import will be removed. Imports
+ * which have no matching exports will be reported, as will duplicate exports.
+ * <p>
+ * At present duplicate package names are detected solely on name. In future this
+ * needs to be extended to include a version, or version range match.
+ *
+ * @version $Revision: 29 $
+ * @author <A HREF="mailto:robw@ascert.com">Rob Walker</A>
+ */
+public class ResolveImportsToExports
+ extends GenericRule
+{
+ //////////////////////////////////////////////////
+ // STATIC VARIABLES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // STATIC PUBLIC METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // INSTANCE VARIABLES
+ //////////////////////////////////////////////////
+
+ public Set allImports = OsgiPackage.getNewSet();
+ public Set resolvedImports = OsgiPackage.getNewSet();
+
+ //////////////////////////////////////////////////
+ // CONSTRUCTORS
+ //////////////////////////////////////////////////
+
+ public ResolveImportsToExports()
+ {
+ }
+
+ //////////////////////////////////////////////////
+ // ACCESSOR METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // PUBLIC INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // INTERFACE METHODS - Rule
+ //////////////////////////////////////////////////
+
+
+ public void execute(List jarList)
+ {
+ // build up complete set of required imports
+ for(Iterator i = jarList.iterator(); i.hasNext(); )
+ {
+ BundleJar bund = (BundleJar) i.next();
+ allImports.addAll(bund.getPossibleImports());
+ }
+
+ // for each bundle, resolve exports to imports and remove exports
+ // that don't match a required import
+ for(Iterator i = jarList.iterator(); i.hasNext(); )
+ {
+ BundleJar bund = (BundleJar) i.next();
+ rptOut.println("");
+ rptOut.println("> " + bund.getName() + " :");
+
+ Set exports = bund.getPossibleExports();
+ for(Iterator j = exports.iterator(); j.hasNext(); )
+ {
+ OsgiPackage pkg = (OsgiPackage) j.next();
+ if (allImports.contains(pkg))
+ {
+ // exports matches imports, move it to resolved list or report
+ // duplicate if already there
+ allImports.remove(pkg);
+ resolvedImports.add(pkg);
+ rptOut.println(" ... resolved export: " + pkg);
+ }
+ else if (resolvedImports.contains(pkg))
+ {
+ MangenMain.warning(rptOut, "*** WARNING *** duplicate export, removing: " + pkg);
+ //TODO: for now we'll suppress the duplicate, which means first
+ // seen becomes the exporter. Probably need to handle
+ // better e.g. using versions or wilcard rules in manifest
+ // to decide who exports
+ j.remove();
+ }
+ else
+ {
+ // export doesn't match any imports, so remove it
+ rptOut.println(" ... removing un-needed export: " + pkg);
+ j.remove();
+ }
+ }
+ }
+
+ rptOut.println("");
+
+ // report any unresolved imports
+ for(Iterator i = allImports.iterator(); i.hasNext(); )
+ {
+ OsgiPackage pkg = (OsgiPackage) i.next();
+ if (!isPackageMatch(pkg, "sys-packages"))
+ {
+ MangenMain.warning(rptOut, "*** WARNING *** unresolved import: " + pkg);
+ }
+ }
+ }
+
+ /**
+ * This rule can be used globally.
+ */
+ public boolean isUsableGlobally()
+ {
+ return true;
+ }
+
+ /**
+ * This rule cannot be used locally.
+ */
+ public boolean isUsableLocally()
+ {
+ return false;
+ }
+
+ //////////////////////////////////////////////////
+ // INTERFACE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // PROTECTED INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // PRIVATE INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // NON-STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+}
diff --git a/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/UpdateBundles.java b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/UpdateBundles.java
new file mode 100644
index 0000000..59bc7ef
--- /dev/null
+++ b/tools/mangen/src/main/java/org/apache/felix/tool/mangen/rule/UpdateBundles.java
@@ -0,0 +1,134 @@
+/*
+ * 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.rule;
+
+import java.io.IOException;
+
+import java.util.List;
+import java.util.Iterator;
+
+import org.apache.felix.tool.mangen.MangenMain;
+import org.apache.felix.tool.mangen.BundleJar;
+
+/**
+ * Rule to request each bundle to update it's manifest, based on the mangen
+ * rule-based set of imports and exports.
+ *
+ * @version $Revision: 29 $
+ * @author <A HREF="mailto:robw@ascert.com">Rob Walker</A>
+ */
+public class UpdateBundles
+ extends GenericRule
+{
+ //////////////////////////////////////////////////
+ // STATIC VARIABLES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // STATIC PUBLIC METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // INSTANCE VARIABLES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // CONSTRUCTORS
+ //////////////////////////////////////////////////
+
+ public UpdateBundles()
+ {
+ }
+
+ //////////////////////////////////////////////////
+ // ACCESSOR METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // PUBLIC INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ public boolean overwrite = false;
+
+ //////////////////////////////////////////////////
+ // INTERFACE METHODS - Rule
+ //////////////////////////////////////////////////
+
+ /**
+ * Iterate over the list of bundles and ask each bundle to update itself.
+ */
+ public void execute(List jarList)
+ {
+ for(Iterator i = jarList.iterator(); i.hasNext(); )
+ {
+ BundleJar bund = (BundleJar) i.next();
+ try
+ {
+ rptOut.println("Updating bundle: " + bund.getName() + " :");
+ bund.update(overwrite);
+ }
+ catch (IOException ioe)
+ {
+ MangenMain.error(rptOut, "IO Exception occured during update: " + ioe);
+ }
+ }
+ }
+
+ /**
+ * This rule can be used globally.
+ */
+ public boolean isUsableGlobally()
+ {
+ return true;
+ }
+
+ /**
+ * This rule cannot be used locally.
+ */
+ public boolean isUsableLocally()
+ {
+ return false;
+ }
+
+ //////////////////////////////////////////////////
+ // PROTECTED INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ /**
+ * Overrides superclass version to look for 'overwrite' option qualifiers
+ */
+ protected void processNonStandardQualifier(String qual)
+ {
+ if (qual.compareToIgnoreCase("overwrite") == 0 )
+ {
+ overwrite = true;
+ }
+ }
+
+ //////////////////////////////////////////////////
+ // PRIVATE INSTANCE METHODS
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+ //////////////////////////////////////////////////
+ // NON-STATIC INNER CLASSES
+ //////////////////////////////////////////////////
+
+}
diff --git a/tools/mangen/src/main/manifest/Manifest.mf b/tools/mangen/src/main/manifest/Manifest.mf
new file mode 100644
index 0000000..e43931d
--- /dev/null
+++ b/tools/mangen/src/main/manifest/Manifest.mf
@@ -0,0 +1,2 @@
+Main-Class: org.apache.felix.tool.mangen.MangenMain
+Class-Path: bcel.jar asm.jar felix.main.jar felix.framework.jar org.osgi.core.jar
diff --git a/tools/mangen/src/main/resources/mangen.properties b/tools/mangen/src/main/resources/mangen.properties
new file mode 100644
index 0000000..ea7c354
--- /dev/null
+++ b/tools/mangen/src/main/resources/mangen.properties
@@ -0,0 +1,102 @@
+# ------------------------------------------------------------------
+# General mangen properties example
+# ------------------------------------------------------------------
+
+# Determines which OSGi release level to create the bundle jars against. Current
+# valid values are '3' and '4' (default is '3')
+mangen.osgi.level=3
+
+
+# Determines whether to automatically process any inner jars found, including
+# generation of a suitable "Bundle-Classpath" header. Valid values are 'true' or
+# 'false' (default is 'false').
+#TODO: not implemented yet
+#mangen.innerjar.auto=true
+
+# Default is to fail (exit code = 3) on errors, can be disabled
+#mangen.failonerror=off
+# Optionally can be made to fail (exit code = 5) if there are warnings
+#mangen.failonwarning=on
+
+#
+# The concept of rulesets is to make it easier to group whole sets of rules for
+# use e.g. here it's used to separate general rules, OSGi R3 vs. OSGi R4
+# specific rules, and an app. specific rule to ignore all Ant packages found
+# in packages. Without rulesets, the sequential numbering means that mixing
+# and matching various rules requires a lot of manual renumbering.
+#
+# Note also the use of ${var} expansion, which follows the same model as used
+# for Oscar properties (http://oscar.objectweb.org/)
+#
+mangen.rulesets=mangen-rule-initial- , mangen-rule-R${mangen.osgi.level}- , mangen-rule- , mangen-rule-final-
+
+# Standard JDK javax.* packages
+mangen.jdk.javax.packages=javax\\.accessibility,javax\\.crypto,javax\\.crypto\\.interfaces,javax\\.crypto\\.spec,javax\\.imageio,javax\\.imageio\\.event,javax\\.imageio\\.metadata,javax\\.imageio\\.plugins\\.jpeg,javax\\.imageio\\.spi,javax\\.imageio\\.stream,javax\\.naming,javax\\.naming\\.directory,javax\\.naming\\.event,javax\\.naming\\.ldap,javax\\.naming\\.spi,javax\\.net,javax\\.net\\.ssl,javax\\.print,javax\\.print\\.attribute,javax\\.print\\.attribute\\.standard,javax\\.print\\.event,javax\\.rmi,javax\\.rmi\\.CORBA,javax\\.security\\.auth,javax\\.security\\.auth\\.callback,javax\\.security\\.auth\\.kerberos,javax\\.security\\.auth\\.login,javax\\.security\\.auth\\.spi,javax\\.security\\.auth\\.x500,javax\\.security\\.cert,javax\\.sound\\.midi,javax\\.sound\\.midi\\.spi,javax\\.sound\\.sampled,javax\\.sound\\.sampled\\.spi,javax\\.sql,javax\\.swing,javax\\.swing\\.border,javax\\.swing\\.colorchooser,javax\\.swing\\.event,javax\\.swing\\.filechooser,javax\\.swing\\.plaf,javax\\.swing\\.plaf\\.basic,javax\\.swing\\.plaf\\.metal,javax\\.swing\\.plaf\\.multi,javax\\.swing\\.table,javax\\.swing\\.text,javax\\.swing\\.text\\.html,javax\\.swing\\.text\\.html\\.parser,javax\\.swing\\.text\\.rtf,javax\\.swing\\.tree,javax\\.swing\\.undo,javax\\.transaction,javax\\.transaction\\.xa,javax\\.xml\\.parsers,javax\\.xml\\.transform,javax\\.xml\\.transform\\.dom,javax\\.xml\\.transform\\.sax,javax\\.xml\\.transform\\.stream,org\\.ietf\\.jgss,org\\.omg\\.CORBA,org\\.omg\\.CORBA_2_3,org\\.omg\\.CORBA_2_3\\.portable,org\\.omg\\.CORBA\\.DynAnyPackage,org\\.omg\\.CORBA\\.ORBPackage,org\\.omg\\.CORBA\\.portable,org\\.omg\\.CORBA\\.TypeCodePackage,org\\.omg\\.CosNaming,org\\.omg\\.CosNaming\\.NamingContextExtPackage,org\\.omg\\.CosNaming\\.NamingContextPackage,org\\.omg\\.Dynamic,org\\.omg\\.DynamicAny,org\\.omg\\.DynamicAny\\.DynAnyFactoryPackage,org\\.omg\\.DynamicAny\\.DynAnyPackage,org\\.omg\\.IOP,org\\.omg\\.IOP\\.CodecFactoryPackage,org\\.omg\\.IOP\\.CodecPackage,org\\.omg\\.Messaging,org\\.omg\\.PortableInterceptor,org\\.omg\\.PortableInterceptor\\.ORBInitInfoPackage,org\\.omg\\.PortableServer,org\\.omg\\.PortableServer\\.CurrentPackage,org\\.omg\\.PortableServer\\.POAManagerPackage,org\\.omg\\.PortableServer\\.POAPackage,org\\.omg\\.PortableServer\\.portable,org\\.omg\\.PortableServer\\.ServantLocatorPackage,org\\.omg\\.SendingContext,org\\.omg\\.stub\\.java\\.rmi,org\\.w3c\\.dom,org\\.xml\\.sax,org\\.xml\\.sax\\.ext,org\\.xml\\.sax\\.helpers
+# R3 treats any standard JDK classes as system packages
+mangen.R3.syspackages=java\\..*,sun\\..*,com\\.sun\\..*,${mangen.jdk.javax.packages}
+# R4 system packages are only considered to be java.*
+mangen.R4.syspackages=java\\..*
+
+# Initial rule to "process" each bundle's JAR. Without this, no class scanning
+# will be done. Hence this will nearly always be the first rule.
+mangen-rule-initial-0=ProcessBundles
+# App specific rule to ignore Ant packages
+mangen-rule-initial-1=Ignore imports(org\\.apache\\.tools\\.ant.*)
+
+
+# R3 rule to ignore all standard JDK classes.
+#
+# The rule contains an option, in this case imports(), which contains a comma
+# separate list of option qualifiers, in this case a set of regex patterns to
+# match against package name.
+#
+# Care needs to be taken with delimitter chars - ',' and space (' ') should
+# not be used except as shown since the tokenizer needs them for parsing.
+#
+mangen-rule-R3-0=Ignore imports(${mangen.R3.syspackages})
+mangen-rule-R3-1=DontImportOwnExports
+mangen-rule-R3-2=ResolveImportsToExports sys-packages(${mangen.R3.syspackages})
+
+# R4 rule is only to ignore java.* classes
+mangen-rule-R4-0=Ignore imports(${mangen.R4.syspackages})
+mangen-rule-R4-1=DontImportOwnExports
+mangen-rule-R4-2=ResolveImportsToExports sys-packages(${mangen.R4.syspackages})
+
+# General rules. These use the default rule name and will also be used if no
+# "mangen.rulesets" property is specified.
+#mangen-rule-0=Merge existing imports(.*) exports(.*)
+mangen-rule-0=Merge fixed imports(.*) exports(.*)
+
+# The AttribuetStamp rule allows atrtibutes to be supplied to be include with
+# mangen generated package names. Shown commented out here to give an example
+#
+#mangen-rule-1=AttributeStamp imports(org\\.osgi\\.framework;version="1.2.0") exports(org\\.osgi\\.service\\.cm;version="2.0.0")
+
+# Final rules. We use these here to run a rule that will force an update to the
+# bundle manifests. The "overwrite" option tells mangen to overwrite the bundle
+# with the new version.
+#
+mangen-rule-final-0=UpdateBundles
+#mangen-rule-final-0=UpdateBundles overwrite
+
+#
+# Details of which mangen reports need to be run
+#
+mangen-report-0=RuleReport
+#mangen-report-1=BundleReport show-local-rules show-differences
+mangen-report-1=BundleReport show-local-rules
+
+#-----------------------------------------------------------------------------
+#
+# Properties below this section should not be changed without a good
+# understanding of mangen's internal operation
+#
+
+# Specifies which scanner class to use.
+#mangen.scanner.class=com.ascert.openosgi.mangen.BCELScanner
+mangen.scanner.class=org.ungoverned.osgi.mangen.ASMClassScanner
+
+# Diagnostic tracing
+#mangen.trace=on
+
+
diff --git a/tools/mangen/src/main/resources/obr.properties b/tools/mangen/src/main/resources/obr.properties
new file mode 100644
index 0000000..22fed88
--- /dev/null
+++ b/tools/mangen/src/main/resources/obr.properties
@@ -0,0 +1,100 @@
+# ------------------------------------------------------------------
+# Example mangen properties for generation of OBR descriptors
+# ------------------------------------------------------------------
+
+#
+# We don't use a ProcessBundles rule, hence no imports/exports will be generated
+# Rather than assume this will always be the case, we use a "Merge" rule to
+# merge the current imports/exports into the mangen set. This allows the
+# ObrReport to use the mangen generated set, and ensures that
+# descriptors can be created either from existing import/exports or generated
+# imports/exports.
+#
+mangen.rulesets=mangen-rule-
+mangen-rule-0=Merge existing imports(.*) exports(.*)
+
+#
+# Create the OBR descriptor report
+#
+mangen-report-0=ObrReport skip-jars(.*-src\\.jar)
+
+#-----------------------------------------------------------------------------
+#
+# Properties below this section should not be changed without a good
+# understanding of mangen's internal operation
+#
+
+# Specifies which scanner class to use.
+mangen.scanner.class=com.ascert.openosgi.mangen.BCELScanner
+#mangen.scanner.class=org.ungoverned.osgi.mangen.ASMClassScanner
+
+#
+# Controls which format of OBR descriptor to create
+#
+mangen.obr.ver=1
+
+#
+# Formats for OBR 1 descriptor
+#
+mangen.obr.descr.1=\
+<bundle>\n \
+ <bundle-name>@@hdr:Bundle-Name@@</bundle-name>\n \
+ <bundle-description>@@hdr:Bundle-Description@@</bundle-description>\n \
+ <bundle-updatelocation>@@hdr:Bundle-UpdateLocation@@</bundle-updatelocation>\n \
+ <bundle-sourceurl>@@hdr:Bundle-SourceUrl@@</bundle-sourceurl>\n \
+ <bundle-version>@@hdr:Bundle-Version@@</bundle-version>\n \
+ <bundle-docurl>@@hdr:Bundle-DocURL@@</bundle-docurl>\n \
+ <bundle-category>@@hdr:Bundle-Category@@</bundle-category>\n \
+ @@imports@@\
+ @@exports@@\n\
+</bundle>
+
+mangen.obr.import.1=<import-package package="@@pkg:name@@" @@import-ver@@/>\n
+mangen.obr.import.ver.1=specification-version="@@pkg:ver@@"
+
+mangen.obr.export.1=<export-package package="@@pkg:name@@" @@export-ver@@/>\n
+mangen.obr.export.ver.1=specification-version="@@pkg:ver@@"
+
+#
+# Formats for OBR 2 descriptor
+#
+mangen.obr.descr.2=\
+<bundle>\n \
+ <bundle-name>@@hdr:Bundle-Name@@</bundle-name>\n \
+ <bundle-symbolicname>@@hdr:Bundle-SymbolicName@@</bundle-symbolicname>\n \
+ <bundle-description>@@hdr:Bundle-Description@@</bundle-description>\n \
+ <bundle-url>@@hdr:Bundle-URL@@</bundle-url>\n \
+ <bundle-sourceurl>@@hdr:Bundle-SourceUrl@@</bundle-sourceurl>\n \
+ <bundle-version>@@hdr:Bundle-Version@@</bundle-version>\n \
+ <bundle-docurl>@@hdr:Bundle-DocURL@@</bundle-docurl>\n \
+ <bundle-category>@@hdr:Bundle-Category@@</bundle-category>\n \
+ @@imports@@ \
+ @@exports@@\n\
+</bundle>
+
+mangen.obr.import.2= \
+ <requirement>\n \
+ <![CDATA[\n \
+ (&(type=Export-Package)\n \
+ (name=@@pkg:name@@)\n \
+ @@import-ver@@)\n \
+ ]]>\n \
+ </requirement>\n
+mangen.obr.import.ver.2=(version>=@@pkg:ver@@)
+
+mangen.obr.export.2= \
+ <capability>\n \
+ <property name="type"\n \
+ type="java.lang.String"\n \
+ value="Export-Package"/>\n \
+ <property name="name"\n \
+ type="java.lang.String"\n \
+ value="@@pkg:name@@"/>\n \
+ @@export-ver@@\
+ </capability>
+mangen.obr.export.ver.2=\
+ <property name="version"\n \
+ type="org.ungoverned.osgi.bundle.bundlerepository.R4Version"\n \
+ value="@@pkg:ver@@"/>\n \
+
+