You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by cl...@apache.org on 2011/08/18 13:32:22 UTC

svn commit: r1159173 [2/5] - in /felix/trunk/ipojo: ant/ ant/src/main/java/org/apache/felix/ipojo/task/ manipulator/ manipulator/src/main/java/org/apache/felix/ipojo/manipulation/ manipulator/src/main/java/org/apache/felix/ipojo/manipulator/ manipulato...

Modified: felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/Pojoization.java
URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/Pojoization.java?rev=1159173&r1=1159172&r2=1159173&view=diff
==============================================================================
--- felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/Pojoization.java (original)
+++ felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/Pojoization.java Thu Aug 18 11:32:19 2011
@@ -19,45 +19,29 @@
 package org.apache.felix.ipojo.manipulator;
 
 import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.Vector;
-import java.util.jar.Attributes;
-import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
-import java.util.jar.JarOutputStream;
-import java.util.jar.Manifest;
 
-import org.apache.felix.ipojo.manipulation.InnerClassManipulator;
-import org.apache.felix.ipojo.manipulation.Manipulator;
-import org.apache.felix.ipojo.manipulation.MethodCreator;
-import org.apache.felix.ipojo.manipulation.annotations.MetadataCollector;
-import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.manipulator.manifest.FileManifestProvider;
+import org.apache.felix.ipojo.manipulator.metadata.AnnotationMetadataProvider;
+import org.apache.felix.ipojo.manipulator.metadata.CompositeMetadataProvider;
+import org.apache.felix.ipojo.manipulator.metadata.EmptyMetadataProvider;
+import org.apache.felix.ipojo.manipulator.metadata.FileMetadataProvider;
+import org.apache.felix.ipojo.manipulator.metadata.StreamMetadataProvider;
+import org.apache.felix.ipojo.manipulator.render.MetadataRenderer;
+import org.apache.felix.ipojo.manipulator.reporter.SystemReporter;
+import org.apache.felix.ipojo.manipulator.store.DirectoryResourceStore;
+import org.apache.felix.ipojo.manipulator.store.JarFileResourceStore;
+import org.apache.felix.ipojo.manipulator.store.builder.DefaultManifestBuilder;
+import org.apache.felix.ipojo.manipulator.store.mapper.WABResourceMapper;
+import org.apache.felix.ipojo.manipulator.util.Metadatas;
+import org.apache.felix.ipojo.manipulator.util.Strings;
+import org.apache.felix.ipojo.manipulator.visitor.check.CheckFieldConsistencyVisitor;
+import org.apache.felix.ipojo.manipulator.visitor.writer.ManipulatedResourcesWriter;
 import org.apache.felix.ipojo.metadata.Element;
-import org.apache.felix.ipojo.xml.parser.ParseException;
 import org.apache.felix.ipojo.xml.parser.SchemaResolver;
-import org.apache.felix.ipojo.xml.parser.XMLMetadataParser;
-import org.objectweb.asm.ClassReader;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import org.xml.sax.SAXParseException;
-import org.xml.sax.XMLReader;
-import org.xml.sax.helpers.XMLReaderFactory;
 
 /**
  * Pojoization allows creating an iPOJO bundle from a "normal" bundle.
@@ -71,36 +55,6 @@ public class Pojoization {
     public static final String IPOJO_PACKAGE_VERSION = " 1.8.0";
 
     /**
-     * List of component types.
-     */
-    private List m_components;
-
-    /**
-     * Metadata (in internal format).
-     */
-    private List/*Element*/ m_metadata = new ArrayList/*Element*/();
-
-    /**
-     * Errors which occur during the manipulation.
-     */
-    private List m_errors = new ArrayList();
-
-    /**
-     * Warnings which occur during the manipulation.
-     */
-    private List m_warnings = new ArrayList();
-
-    /**
-     * Class map (class name, byte[]).
-     */
-    private Map m_classes = new HashMap();
-
-    /**
-     * Referenced packages by the composite.
-     */
-    private List m_referredPackages;
-
-    /**
      * Flag describing if we need or not compute annotations.
      * By default, compute the annotations.
      */
@@ -109,65 +63,54 @@ public class Pojoization {
     /**
      * Flag describing if we need or not use local XSD files
      * (i.e. use the {@link SchemaResolver} or not).
-     * If <code>true</code> the local XSD are not used.
+     * If <code>true</code> the local XSD are used (default to {@literal false}).
      */
-    private boolean m_ignoreLocalXSD;
+    private boolean m_useLocalXSD = false;
 
     /**
-     * Input jar file.
+     * Reporter for error reporting.
      */
-    private JarFile m_inputJar;
+    private Reporter m_reporter;
 
-    /**
-     * The manipulated directory.
-     */
-    private File m_dir;
+    public Pojoization() {
+        this(new SystemReporter());
+    }
 
-    /**
-     * The manifest location.
-     */
-    private File m_manifest;
+    public Pojoization(Reporter m_reporter) {
+        this.m_reporter = m_reporter;
+    }
 
     /**
-     * Manifest attribute filter, with default iPOJO filters
+     * Activates annotation processing.
      */
-    private List/*IManifestAttributeFilter*/ m_manifestAttributeFilters = new ArrayList/*IManifestAttributeFilter*/();
+    public void disableAnnotationProcessing() {
+        m_ignoreAnnotations = true;
+    }
 
     /**
-     * Add an error in the error list.
-     * @param mes : error message.
+     * Activates the entity resolver loading
+     * XSD files from the classloader.
      */
-    protected void error(String mes) {
-        System.err.println(mes);
-        m_errors.add(mes);
+    public void setUseLocalXSD() {
+        m_useLocalXSD = true;
     }
 
     /**
-     * Add a warning in the warning list.
-     * @param mes : warning message
+     * @return all the errors (fatal) reported by the manipulation process.
      */
-    public void warn(String mes) {
-        m_warnings.add(mes);
-    }
-
     public List getErrors() {
-        return m_errors;
+        // Simple delegation for backward compatibility
+        return m_reporter.getErrors();
     }
 
     /**
-     * Activates annotation processing.
+     * @return all the warnings (non fatal) reported by the manipulation process.
      */
-    public void disableAnnotationProcessing() {
-        m_ignoreAnnotations = true;
+    public List getWarnings() {
+        // Simple delegation for backward compatibility
+        return m_reporter.getWarnings();
     }
 
-    /**
-     * Activates the entity resolver loading
-     * XSD files from the classloader.
-     */
-    public void setUseLocalXSD() {
-        m_ignoreLocalXSD = false;
-    }
 
     /**
      * Manipulates an input bundle.
@@ -178,34 +121,41 @@ public class Pojoization {
      * @param metadata the iPOJO metadata input stream.
      */
     public void pojoization(File in, File out, InputStream metadata) {
-        parseXMLMetadata(metadata);
-        if (m_metadata == null) { // An error occurs during the parsing.
-            return;
-        }
-        // m_metadata can be either an empty array or an Element
-        // array with component type description. It also can be null
-        // if no metadata file is given.
 
+        StreamMetadataProvider provider = new StreamMetadataProvider(metadata, m_reporter);
+        provider.setValidateUsingLocalSchemas(m_useLocalXSD);
+
+        ResourceStore store;
         try {
-            m_inputJar = new JarFile(in);
+            JarFile origin = new JarFile(in);
+            JarFileResourceStore jfrs = new JarFileResourceStore(origin, out);
+            if (in.getName().endsWith(".war")) {
+                // this is a war file, use the right mapper
+                jfrs.setResourceMapper(new WABResourceMapper());
+            }
+            jfrs.setManifest(origin.getManifest());
+            DefaultManifestBuilder dmb = new DefaultManifestBuilder();
+            dmb.setMetadataRenderer(new MetadataRenderer());
+            jfrs.setManifestBuilder(dmb);
+            store = jfrs;
         } catch (IOException e) {
-            error("The input file " + in.getAbsolutePath() + " is not a Jar file");
+            m_reporter.error("The input file " + in.getAbsolutePath() + " is not a Jar file");
             return;
         }
 
-        // Get the list of declared component
-        computeDeclaredComponents();
+        ManipulationVisitor visitor = createDefaultVisitorChain(store);
 
-        // Start the manipulation
-        manipulateJarFile(out);
+        pojoization(store, provider, visitor);
+    }
 
-        // Check that all declared components are manipulated
-        for (int i = 0; i < m_components.size(); i++) {
-            ComponentInfo ci = (ComponentInfo) m_components.get(i);
-            if (!ci.m_isManipulated) {
-                error("The component " + ci.m_classname + " is declared but not in the bundle");
-            }
-        }
+    private ManipulationVisitor createDefaultVisitorChain(ResourceStore store) {
+        ManipulatedResourcesWriter writer = new ManipulatedResourcesWriter();
+        writer.setReporter(m_reporter);
+        writer.setResourceStore(store);
+
+        CheckFieldConsistencyVisitor checkFieldConsistencyVisitor = new CheckFieldConsistencyVisitor(writer);
+        checkFieldConsistencyVisitor.setReporter(m_reporter);
+        return checkFieldConsistencyVisitor;
     }
 
     /**
@@ -217,31 +167,35 @@ public class Pojoization {
      * @param metadataFile the iPOJO metadata file (XML).
      */
     public void pojoization(File in, File out, File metadataFile) {
-        // Get the metadata.xml location if not null
+
+        MetadataProvider provider = new EmptyMetadataProvider();
         if (metadataFile != null) {
-            parseXMLMetadata(metadataFile);
+            FileMetadataProvider fileMetadataProvider = new FileMetadataProvider(metadataFile, m_reporter);
+            fileMetadataProvider.setValidateUsingLocalSchemas(m_useLocalXSD);
+            provider = fileMetadataProvider;
         }
 
+        ResourceStore store;
         try {
-            m_inputJar = new JarFile(in);
+            JarFile origin = new JarFile(in);
+            JarFileResourceStore jfrs = new JarFileResourceStore(origin, out);
+            if (in.getName().endsWith(".war")) {
+                // this is a war file, use the right mapper
+                jfrs.setResourceMapper(new WABResourceMapper());
+            }
+            jfrs.setManifest(origin.getManifest());
+            DefaultManifestBuilder dmb = new DefaultManifestBuilder();
+            dmb.setMetadataRenderer(new MetadataRenderer());
+            jfrs.setManifestBuilder(dmb);
+            store = jfrs;
         } catch (IOException e) {
-            error("The input file " + in.getAbsolutePath() + " is not a Jar file");
+            m_reporter.error("The input file " + in.getAbsolutePath() + " is not a Jar file");
             return;
         }
 
-        // Get the list of declared component
-        computeDeclaredComponents();
+        ManipulationVisitor visitor = createDefaultVisitorChain(store);
 
-        // Start the manipulation
-        manipulateJarFile(out);
-
-        // Check that all declared components are manipulated
-        for (int i = 0; i < m_components.size(); i++) {
-            ComponentInfo ci = (ComponentInfo) m_components.get(i);
-            if (!ci.m_isManipulated) {
-                error("The component " + ci.m_classname + " is declared but not in the bundle");
-            }
-        }
+        pojoization(store, provider, visitor);
     }
 
     /**
@@ -254,1021 +208,112 @@ public class Pojoization {
      */
     public void directoryPojoization(File directory, File metadataFile, File manifestFile) {
     	// Get the metadata.xml location if not null
+        MetadataProvider provider = new EmptyMetadataProvider();
         if (metadataFile != null) {
-            parseXMLMetadata(metadataFile);
-        }
-
-        if (directory.exists() && directory.isDirectory()) {
-            m_dir = directory;
-        } else {
-            error("The directory " + directory.getAbsolutePath() + " does not exist or is not a directory.");
+            FileMetadataProvider fileMetadataProvider = new FileMetadataProvider(metadataFile, m_reporter);
+            fileMetadataProvider.setValidateUsingLocalSchemas(m_useLocalXSD);
+            provider = fileMetadataProvider;
         }
 
-
+        ManifestProvider manifestProvider;
         if (manifestFile != null) {
-            if (manifestFile.exists()) {
-                m_manifest = manifestFile;
-            } else {
-                error("The manifest file " + manifestFile.getAbsolutePath() + " does not exist");
-            }
-        }
-        // If the manifest is not specified, the m_dir/META-INF/MANIFEST.MF is used.
-
-        // Get the list of declared component
-        computeDeclaredComponents();
-
-        // Start the manipulation
-        manipulateDirectory();
-
-        // Check that all declared components are manipulated
-        for (int i = 0; i < m_components.size(); i++) {
-            ComponentInfo ci = (ComponentInfo) m_components.get(i);
-            if (!ci.m_isManipulated) {
-                error("The component " + ci.m_classname + " is declared but not in the bundle");
-            }
-        }
-
-    }
-
-    /**
-     * Parse the content of the class to detect annotated classes.
-     * @param inC the class to inspect.
-     */
-    private void computeAnnotations(byte[] inC) {
-        ClassReader cr = new ClassReader(inC);
-        MetadataCollector collector = new MetadataCollector();
-        cr.accept(collector, 0);
-
-        if (collector.isIgnoredBecauseOfMissingComponent()) {
-        	// No @Component, just skip.
-            //warn("Annotation processing ignored in " + collector.getClassName() + " - @Component missing");
-        } else if (collector.isComponentType()) {
-            boolean toskip = false;
-            for (int i = 0; !toskip && i < m_metadata.size(); i++) {
-                Element meta = (Element)  m_metadata.get(i);
-                if (! meta.getName().equals("instance") // Only if its a component type definition,
-                                                        // so skip instance declaration
-                        && meta.containsAttribute("name")
-                        && meta.getAttribute("name").equalsIgnoreCase(collector.getComponentTypeDeclaration().getAttribute("name"))) {
-                    toskip = true;
-                    warn("The component type " + collector.getComponentTypeDeclaration().getAttribute("name") + " is overriden by the metadata file");
-                }
-            }
-            if (!toskip) {
-                // if no metadata or empty one, create a new array.
-                Element elem = collector.getComponentTypeDeclaration();
-                m_metadata.add(elem);
-
-                String name = elem.getAttribute("classname");
-                name = name.replace('.', '/');
-                name += ".class";
-
-                // Creates the ComponentInfo and store bytecode
-                ComponentInfo info = new ComponentInfo(name, elem);
-                info.m_bytecode = inC;
-                m_components.add(info);
-
-                // Instantiate ?
-                if (collector.getInstanceDeclaration() != null) {
-                    //warn("Declaring an empty instance of " + elem.getAttribute("classname"));
-                    m_metadata.add(collector.getInstanceDeclaration());
-                }
-            }
-        }
-    }
-
-    /**
-     * Copies an input stream into an output stream but does not close the streams.
-     * @param in the input stream
-     * @param out the output stream
-     * @throws IOException if the stream cannot be copied
-     */
-    private static void copyStreamWithoutClosing(InputStream in, OutputStream out) throws IOException {
-        byte[] b = new byte[4096];
-        for (int n; (n = in.read(b)) != -1;) {
-            out.write(b, 0, n);
-        }
-    }
-
-    /**
-     * Gets the class name from the jar entry.
-     * This method handles the WAR case.
-     * @param entry the entry
-     * @return the class name
-     */
-    private String getClassNameForEntry(JarEntry entry) {
-    	// If we're manipulating a war file remove the WEB-INF/classes prefix.
-    	if (entry.getName().startsWith("WEB-INF/classes/")) {
-        	return entry.getName().substring("WEB-INF/classes/".length());
-        } else {
-        	return entry.getName();
-        }
-    }
-
-    /**
-     * Manipulate the input bundle.
-     * @param out final bundle
-     */
-    private void manipulateJarFile(File out) {
-        manipulateComponents(); // Manipulate classes
-        m_referredPackages = getReferredPackages();
-        Manifest mf = doManifest(); // Compute the manifest
-
-        // Create a new Jar file
-        FileOutputStream fos = null;
-        JarOutputStream jos = null;
-        try {
-            fos = new FileOutputStream(out);
-            jos = new JarOutputStream(fos, mf);
-        } catch (FileNotFoundException e1) {
-            error("Cannot manipulate the Jar file : the output file " + out.getAbsolutePath() + " is not found");
-            return;
-        } catch (IOException e) {
-            error("Cannot manipulate the Jar file : cannot access to " + out.getAbsolutePath());
-            return;
-        }
-
-        try {
-            // Copy classes and resources
-            Enumeration entries = m_inputJar.entries();
-            while (entries.hasMoreElements()) {
-                JarEntry curEntry = (JarEntry) entries.nextElement();
-
-                // If the class was manipulated, write out the manipulated
-                // version of the bytecode
-
-                String classEntry = getClassNameForEntry(curEntry);
-                if (m_classes.containsKey(classEntry)) {
-                    JarEntry je = new JarEntry(curEntry.getName());
-                    byte[] outClazz = (byte[]) m_classes.get(classEntry);
-                    if (outClazz != null && outClazz.length != 0) {
-                        jos.putNextEntry(je); // copy the entry header to jos
-                        jos.write(outClazz);
-                        jos.closeEntry();
-                    } else { // The class is already manipulated
-                        jos.putNextEntry(curEntry);
-                        InputStream currIn = m_inputJar.getInputStream(curEntry);
-                        copyStreamWithoutClosing(currIn, jos);
-
-                        currIn.close();
-                        jos.closeEntry();
-                    }
-                } else {
-                    // Do not copy the manifest
-                    if (!curEntry.getName().equals("META-INF/MANIFEST.MF")) {
-                        // copy the entry header to jos
-                        jos.putNextEntry(curEntry);
-                        InputStream currIn = m_inputJar.getInputStream(curEntry);
-                        copyStreamWithoutClosing(currIn, jos);
-                        currIn.close();
-                        jos.closeEntry();
-                    }
-                }
-            }
-        } catch (IOException e) {
-            error("Cannot manipulate the Jar file : " + e.getMessage());
-            return;
-        }
-
-        try {
-            m_inputJar.close();
-            jos.close();
-            fos.close();
-            jos = null;
-            fos = null;
-        } catch (IOException e) {
-            error("Cannot close the new Jar file : " + e.getMessage());
-            return;
-        }
-    }
-
-    /**
-     * Manipulate the input directory.
-     */
-    private void manipulateDirectory() {
-        manipulateComponents(); // Manipulate classes
-        m_referredPackages = getReferredPackages();
-        Manifest mf = doManifest(); // Compute the manifest
-        if (mf == null) {
-            error("Cannot found input manifest");
-            return;
-        }
-
-        // Write every manipulated file.
-        Iterator it = m_classes.entrySet().iterator();
-        while (it.hasNext()) {
-            Map.Entry entry = (Map.Entry) it.next();
-            String classname = (String) entry.getKey();
-            byte[] clazz = (byte[]) entry.getValue();
-            // The class name is already a path
-            File classFile = new File(m_dir, classname);
-            try {
-                setBytecode(classFile, clazz);
-            } catch (IOException e) {
-                error("Cannot manipulate the file : the output file " +  classname + " is not found");
-                return;
-            }
-        }
-
-        // Write manifest
-        try {
-            writeManifest(mf);
-        } catch (IOException e) {
-            error("Cannot write the manifest file : " + e.getMessage());
-        }
-
-    }
-
-    /**
-     * Manipulate classes of the input Jar.
-     */
-    private void manipulateComponents() {
-
-        // 1. Discover components described with annotations
-        // Only do this if annotations are enabled
-        if (!m_ignoreAnnotations) {
-            Enumeration entries = getClassFiles();
-
-            while (entries.hasMoreElements()) {
-                String curName = (String) entries.nextElement();
+            if (manifestFile.isFile()) {
                 try {
-                    // Need to load the bytecode for each .class entry
-                    byte[] in = getBytecode(curName);
-
-                    // This method adds the class to the component list
-                    // if that bytecode is annotated with @Component.
-
-                    // We check the array size to avoid manipulating empty files
-                    // produced by incremental compilers (like Eclipse Compiler)
-                    if(in != null && in.length > 0) {
-                        computeAnnotations(in);
-                    } else {
-                        error("Cannot compute annotations from " + curName + " : Empty file");
-                    }
+                    manifestProvider = new FileManifestProvider(manifestFile);
                 } catch (IOException e) {
-                    error("Cannot read the class : " + curName);
+                    m_reporter.error("Cannot read Manifest from '" + manifestFile.getAbsolutePath() + "'");
                     return;
                 }
+            } else {
+                m_reporter.error("The manifest file " + manifestFile.getAbsolutePath() + " does not exist");
+                return;
             }
-        }
-
-        // 2. Iterates over the list of discovered components
-        // Note that this list includes components from metadata.xml AND from annotations
-
-        for (int i = 0; i < m_components.size(); i++) {
-            ComponentInfo info = (ComponentInfo) m_components.get(i);
-
-            // Get the bytecode if necessary
-            if (info.m_bytecode == null) {
+        } else {
+            // If the manifest is not specified, the m_dir/META-INF/MANIFEST.MF is used.
+            File metaInf = new File(directory, "META-INF");
+            File original = new File(metaInf, "MANIFEST.MF");
+            if (original.isFile()) {
                 try {
-                    info.m_bytecode = getBytecode(info.m_classname);
+                    manifestProvider = new FileManifestProvider(original);
                 } catch (IOException e) {
-                    error("Cannot extract bytecode for component '" + info.m_classname + "'");
+                    m_reporter.error("Cannot read Manifest from '" + original.getAbsolutePath() + "'");
                     return;
                 }
+            } else {
+                m_reporter.error("The manifest file " + original.getAbsolutePath() + " does not exist");
+                return;
             }
-            // Manipulate the original bytecode and store the modified one
-            byte[] outClazz = manipulateComponent(info.m_bytecode, info);
-            m_classes.put(info.m_classname, outClazz);
-
-            // Are there any inner classes to be manipulated ?
-            if (!info.m_inners.isEmpty()) {
-                for (int k = 0; k < info.m_inners.size(); k++) {
-                    String innerCN = (String) info.m_inners.get(k) + ".class";
-                    try {
-                        // Get the bytecode and start manipulation
-                        byte[] innerClassBytecode = getBytecode(innerCN);
-                        manipulateInnerClass(innerClassBytecode, innerCN, info);
-                    } catch (IOException e) {
-                        error("Cannot manipulate inner class '" + innerCN + "'");
-                        return;
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Gets a byte array that contains the bytecode of the given classname.
-     * This method can be overridden by sub-classes.
-     * @param classname name of a class to be read
-     * @return a byte array
-     * @throws IOException if the classname cannot be read
-     */
-    protected byte[] getBytecode(final String classname) throws IOException {
-
-        InputStream currIn = null;
-        byte[] in = new byte[0];
-        try {
-            // Get the stream to read
-            currIn = getInputStream(classname);
-            int c;
-
-            // Fill the byte array with IS content
-            while ((c = currIn.read()) >= 0) {
-                byte[] in2 = new byte[in.length + 1];
-                System.arraycopy(in, 0, in2, 0, in.length);
-                in2[in.length] = (byte) c;
-                in = in2;
-            }
-        } finally {
-            // Close the stream
-            if (currIn != null) {
-                try {
-                    currIn.close();
-                } catch (IOException e) {
-                    // Ignored
-                }
-            }
-        }
-
-        return in;
-    }
-
-    /**
-     * Gets an input stream on the given class.
-     * This methods manages Jar files and directories.
-     * If also looks into WEB-INF/classes to support WAR files.
-     * This method may be overridden.
-     * @param classname the class name
-     * @return the input stream
-     * @throws IOException if the file cannot be read
-     */
-    protected InputStream getInputStream(String classname) throws IOException {
-        if (m_inputJar != null) {
-            // Fix entry name if needed
-            if (! classname.endsWith(".class")) {
-                classname += ".class";
-            }
-            JarEntry je = m_inputJar.getJarEntry(classname);
-            if (je == null) {
-            	// Try in WEB-INF/classes (WAR files)
-            	je = m_inputJar.getJarEntry("WEB-INF/classes/" + classname);
-            	if (je == null) {
-            		// If still null, throw an exception.
-            		throw new IOException("The class " + classname + " connot be found in the input Jar file");
-            	}
-            }
-            return m_inputJar.getInputStream(je);
-        } else {
-            // Directory
-            File file = new File(m_dir, classname);
-            return new FileInputStream(file);
         }
-    }
 
-    /**
-     * Gets the list of class files.
-     * The content of the returned enumeration contains file names.
-     * It is possible to get input stream on those file by using
-     * {@link Pojoization#getInputStream(String)} method.
-     * @return the list of class files.
-     */
-    private Enumeration getClassFiles() {
-        Vector files = new Vector();
-        if (m_inputJar != null) {
-            Enumeration enumeration = m_inputJar.entries();
-            while (enumeration.hasMoreElements()) {
-                JarEntry je = (JarEntry) enumeration.nextElement();
-                if (je.getName().endsWith(".class")) {
-                    files.add(je.getName());
-                }
-            }
+        DirectoryResourceStore store;
+        if (directory.exists() && directory.isDirectory()) {
+            store = new DirectoryResourceStore(directory);
+            File webinf = new File(directory, "WEB-INF");
+            if (directory.getName().endsWith(".war") || webinf.isDirectory()) {
+                // this is a war file, use the right mapper
+                store.setResourceMapper(new WABResourceMapper());
+            }
+            store.setManifest(manifestProvider.getManifest());
+            DefaultManifestBuilder dmb = new DefaultManifestBuilder();
+            dmb.setMetadataRenderer(new MetadataRenderer());
+            store.setManifestBuilder(dmb);
         } else {
-            searchClassFiles(m_dir, files);
-        }
-        return files.elements();
-    }
-
-    /**
-     * Navigates across directories to find class files.
-     * Sub-classes can override this method to customize the searched
-     * files.
-     * @param dir the directory to analyze
-     * @param classes discovered classes
-     */
-    protected void searchClassFiles(File dir, List classes) {
-        File[] files = dir.listFiles();
-        for (int i = 0; i < files.length; i++) {
-            if (files[i].isDirectory()) {
-                searchClassFiles(files[i], classes);
-            } else if (files[i].getName().endsWith(".class")) {
-                classes.add(computeRelativePath(files[i].getAbsolutePath()));
-            }
+            m_reporter.error("The directory " + directory.getAbsolutePath() + " does not exist or is not a directory.");
+            return;
         }
-    }
-
-//    /**
-//     * Manipulates an inner class.
-//     * @param inputJar input jar
-//     * @param je inner class jar entry
-//     * @param innerClassName inner class name
-//     * @param ci component info of the component owning the inner class
-//     * @throws IOException the inner class cannot be read
-//     */
-//    private void manipulateInnerClass(JarFile inputJar, JarEntry je, String innerClassName, ComponentInfo ci) throws IOException {
-//        InputStream currIn = inputJar.getInputStream(je);
-//        byte[] in = new byte[0];
-//        int c;
-//        while ((c = currIn.read()) >= 0) {
-//            byte[] in2 = new byte[in.length + 1];
-//            System.arraycopy(in, 0, in2, 0, in.length);
-//            in2[in.length] = (byte) c;
-//            in = in2;
-//        }
-//        // Remove '.class' from class name.
-//        InnerClassManipulator man = new InnerClassManipulator(ci.m_classname.substring(0, ci.m_classname.length() - 6), ci.m_fields);
-//        byte[] out = man.manipulate(in);
-//
-//        m_classes.put(je.getName(), out);
-//
-//    }
-
-    /**
-     * Computes a relative path for the given absolute path.
-     * This methods computes the relative path according to the directory
-     * containing classes for the given class path.
-     * @param absolutePath the absolute path of the class
-     * @return the relative path of the class based on the directory containing
-     * classes.
-     */
-    private String computeRelativePath(String absolutePath) {
-        String root = m_dir.getAbsolutePath();
-        String path = absolutePath.substring(root.length() + 1);
-        return path.replace('\\', '/'); // To support Windows systems, the \ are replaced by /
-        //return path.replace('/', File.separatorChar);
-    }
 
-    /**
-     * Manipulates an inner class.
-     * @param in input bytecode of the inner file to manipulate
-     * @param cn the inner class name (ends with .class)
-     * @param ci component info of the component owning the inner class
-     * @throws IOException the inner class cannot be read
-     */
-    private void manipulateInnerClass(byte[] in, String cn, ComponentInfo ci) throws IOException {
-        // Remove '.class' from class name.
-        String name = ci.m_classname.substring(0, ci.m_classname.length() - 6);
-        InnerClassManipulator man = new InnerClassManipulator(name, ci.m_fields);
-        byte[] out = man.manipulate(in);
+        ManipulationVisitor visitor = createDefaultVisitorChain(store);
 
-        m_classes.put(cn, out);
+        pojoization(store, provider, visitor);
 
     }
 
-    /**
-     * Gets the manifest.
-     * This method handles Jar and directories.
-     * For Jar file, the input jar manifest is returned.
-     * For directories, if specified the specifies manifest is returned.
-     * Otherwise, try directory/META-INF/MANIFEST.MF
-     * @return the Manifest.
-     * @throws IOException if the manifest cannot be found
-     */
-    private Manifest getManifest() throws IOException {
-        if (m_inputJar != null) {
-            return m_inputJar.getManifest();
-        } else {
-            return new Manifest(getManifestInputStream());
-        }
-    }
+    public void pojoization(final ResourceStore store,
+                            final MetadataProvider metadata,
+                            final ManipulationVisitor visitor) {
 
-    /**
-     * Create the manifest.
-     * Set the bundle imports and iPOJO-components clauses
-     * @return the generated manifest.
-     */
-    private Manifest doManifest() {
-        Manifest mf = null;
-        try {
-            mf = getManifest();
-        } catch (IOException e) {
-            // Could not happen, the input bundle is a bundle so must have a manifest.
-            error("Cannot get the manifest : " + e.getMessage());
-            return null;
-        }
-        Attributes att = mf.getMainAttributes();
-        setImports(att); // Set the imports (add ipojo and handler namespaces
-        setPOJOMetadata(att); // Add iPOJO-Component
-        setCreatedBy(att); // Add iPOJO to the creators
-        return mf;
-    }
+        ManipulationEngine engine = new ManipulationEngine();
+        engine.setResourceStore(store);
+        engine.setReporter(m_reporter);
+        engine.setManipulationVisitor(visitor);
 
-    /**
-     * Manipulate a component class.
-     * @param in : the byte array of the class to manipulate
-     * @param ci : attached component info (containing metadata and manipulation metadata)
-     * @return the generated class (byte array)
-     */
-    private byte[] manipulateComponent(byte[] in, ComponentInfo ci) {
-        Manipulator man = new Manipulator();
         try {
-            byte[] out = man.manipulate(in); // iPOJO manipulation
-            ci.detectMissingFields(man.getFields()); // Detect missing field
-            // Insert information to metadata
-            ci.m_componentMetadata.addElement(man.getManipulationMetadata());
-            ci.m_isManipulated = true;
-            ci.m_inners = man.getInnerClasses();
-            ci.m_fields = man.getFields().keySet();
-            return out;
-        } catch (IOException e) {
-            error("Cannot manipulate the class " + ci.m_classname + " : " + e.getMessage());
-            return null;
-        }
-    }
-
-    /**
-     * Return the list of "concrete" component.
-     */
-    private void computeDeclaredComponents() {
-        List componentClazzes = new ArrayList();
-        for (int i = 0; i < m_metadata.size(); i++) {
-            Element meta = (Element) m_metadata.get(i);
-            String name = meta.getAttribute("classname");
-            if (name != null) { // Only handler and component have a classname attribute
-                name = name.replace('.', '/');
-                name += ".class";
-                componentClazzes.add(new ComponentInfo(name, meta));
-            }
-        }
-        m_components = componentClazzes;
-    }
-
-    /**
-     * Component Info.
-     * Represent a component type to be manipulated or already manipulated.
-     * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
-     */
-    private class ComponentInfo {
-        /**
-         * Component Type metadata.
-         */
-        Element m_componentMetadata;
-
-        /**
-         * Component Type implementation class.
-         */
-        String m_classname;
-
-        /**
-         * Is the class already manipulated.
-         */
-        boolean m_isManipulated;
-
-        /**
-         * List of inner classes of the implementation class.
-         */
-        List m_inners;
-
-        /**
-         * Set of fields of the implementation class.
-         */
-        Set m_fields;
-
-        /**
-         * Initial (unmodified) bytecode of the component's class.
-         * May be null !!
-         */
-        byte[] m_bytecode;
-
-        /**
-         * Constructor.
-         * @param cn : class name
-         * @param met : component type metadata
-         */
-        ComponentInfo(String cn, Element met) {
-            this.m_classname = cn;
-            this.m_componentMetadata = met;
-            m_isManipulated = false;
-        }
-
-        /**
-         * Detects missing fields.
-         * If a referenced field does not exist in the class
-         * the method throws an error breaking the build process.
-         * @param fields : field found in the manipulated class
-         */
-        void detectMissingFields(Map fields) {
-            // First, compute the list of referred fields
-            List list = new ArrayList();
-            computeReferredFields(list, m_componentMetadata);
-            // Then, try to find each referred field in the given field map
-            for (int i = 0; i < list.size(); i++) {
-                if (!fields.containsKey(list.get(i))) {
-                    error("The field " + list.get(i) + " is referenced in the "
-                            + "metadata but does not exist in the " + m_classname + " class");
-                }
-            }
-        }
 
-        /**
-         * Looks for 'field' attribute in the given metadata.
-         * @param list : discovered field (accumulator)
-         * @param metadata : metadata to inspect
-         */
-        private void computeReferredFields(List list, Element metadata) {
-            String field = metadata.getAttribute("field");
-            if (field != null && ! list.contains(field)) {
-                list.add(field);
-            }
-            for (int i = 0; i < metadata.getElements().length; i++) {
-                computeReferredFields(list, metadata.getElements()[i]);
-            }
-        }
-
-    }
-
-    /**
-     * Set the create-by in the manifest.
-     * @param att : manifest attribute.
-     */
-    private void setCreatedBy(Attributes att) {
-        String prev = att.getValue("Created-By");
-        if (prev == null) {
-            att.putValue("Created-By", "iPOJO " + IPOJO_PACKAGE_VERSION);
-        } else {
-            if (prev.indexOf("iPOJO") == -1) { // Avoid appending iPOJO several times
-                att.putValue("Created-By", prev + " & iPOJO " + IPOJO_PACKAGE_VERSION);
-            }
-        }
-    }
-
-    /**
-     * Add imports to the given manifest attribute list. This method add ipojo imports and handler imports (if needed).
-     * @param att : the manifest attribute list to modify.
-     */
-    private void setImports(Attributes att) {
-        Map imports = parseHeader(att.getValue("Import-Package"));
-        Map ver = new TreeMap();
-        ver.put("version", IPOJO_PACKAGE_VERSION);
-        if (!imports.containsKey("org.apache.felix.ipojo")) {
-            imports.put("org.apache.felix.ipojo", ver);
-        }
-        if (!imports.containsKey("org.apache.felix.ipojo.architecture")) {
-            imports.put("org.apache.felix.ipojo.architecture", ver);
-        }
-        if (!imports.containsKey("org.osgi.service.cm")) {
-            Map verCM = new TreeMap();
-            verCM.put("version", "1.2");
-            imports.put("org.osgi.service.cm", verCM);
-        }
-        if (!imports.containsKey("org.osgi.service.log")) {
-            Map verCM = new TreeMap();
-            verCM.put("version", "1.3");
-            imports.put("org.osgi.service.log", verCM);
-        }
-
-        // Add referred imports from the metadata
-        for (int i = 0; i < m_referredPackages.size(); i++) {
-            String pack = (String) m_referredPackages.get(i);
-            imports.put(pack, new TreeMap());
-        }
-
-        // Write imports
-        att.putValue("Import-Package", printClauses(imports, "resolution:"));
-    }
-
-    /**
-     * Add iPOJO-Components to the given manifest attribute list. This method add the iPOJO-Components header and its value (according to the metadata) to the manifest.
-     * @param att : the manifest attribute list to modify.
-     */
-    private void setPOJOMetadata(Attributes att) {
-        StringBuffer meta = new StringBuffer();
-        for (int i = 0; i < m_metadata.size(); i++) {
-            Element metadata = (Element) m_metadata.get(i);
-            meta.append(buildManifestMetadata(metadata, new StringBuffer()));
-        }
-        if (meta.length() != 0) {
-            att.putValue("iPOJO-Components", meta.toString());
-        }
-    }
-
-    /**
-     * Standard OSGi header parser. This parser can handle the format clauses ::= clause ( ',' clause ) + clause ::= name ( ';' name ) (';' key '=' value )
-     * This is mapped to a Map { name => Map { attr|directive => value } }
-     *
-     * @param value : String to parse.
-     * @return parsed map.
-     */
-    public Map parseHeader(String value) {
-        if (value == null || value.trim().length() == 0) {
-            return new HashMap();
-        }
-
-        Map result = new HashMap();
-        QuotedTokenizer qt = new QuotedTokenizer(value, ";=,");
-        char del;
-        do {
-            boolean hadAttribute = false;
-            Map clause = new HashMap();
-            List aliases = new ArrayList();
-            aliases.add(qt.nextToken());
-            del = qt.getSeparator();
-            while (del == ';') {
-                String adname = qt.nextToken();
-                if ((del = qt.getSeparator()) != '=') {
-                    if (hadAttribute) {
-                        throw new IllegalArgumentException("Header contains name field after attribute or directive: " + adname + " from " + value);
-                    }
-                    aliases.add(adname);
+            // Creates a composite to store multiple metadata providers
+            CompositeMetadataProvider composite = new CompositeMetadataProvider(m_reporter);
+            composite.addMetadataProvider(metadata);
+
+            if (!m_ignoreAnnotations) {
+                composite.addMetadataProvider(new AnnotationMetadataProvider(store, m_reporter));
+            }
+
+            // Get metadata
+            List<Element> metadatas = composite.getMetadatas();
+
+            // Construct ManipulationUnits
+            // And collect non-component metadata
+            for (Element meta : metadatas) {
+                String name = Metadatas.getComponentType(meta);
+                if (name != null) {
+                    // Only handler and component have a classname attribute
+                    String path = Strings.asResourcePath(name);
+                    engine.addManipulationUnit(new ManipulationUnit(path, meta));
                 } else {
-                    String advalue = qt.nextToken();
-                    clause.put(adname, advalue);
-                    del = qt.getSeparator();
-                    hadAttribute = true;
-                }
-            }
-            for (Iterator i = aliases.iterator(); i.hasNext();) {
-                result.put(i.next(), clause);
-            }
-        } while (del == ',');
-        return result;
-    }
-
-    /**
-     * Print a standard Map based OSGi header.
-     *
-     * @param exports : map { name => Map { attribute|directive => value } }
-     * @param allowedDirectives : list of allowed directives.
-     * @return the clauses
-     */
-    public String printClauses(Map exports, String allowedDirectives) {
-        StringBuffer sb = new StringBuffer();
-        String del = "";
-
-        for (Iterator i = exports.entrySet().iterator(); i.hasNext();) {
-            Map.Entry entry = (Map.Entry) i.next();
-            String name = (String) entry.getKey();
-            Map map = (Map) entry.getValue();
-            sb.append(del);
-            sb.append(name);
-
-            for (Iterator j = map.entrySet().iterator(); j.hasNext();) {
-                Map.Entry entry2 = (Map.Entry) j.next();
-                String key = (String) entry2.getKey();
-
-                // Skip directives we do not recognize
-                if (key.endsWith(":") && allowedDirectives.indexOf(key) < 0) {
-                    continue;
-                }
-
-                String value = (String) entry2.getValue();
-                sb.append(";");
-                sb.append(key);
-                sb.append("=");
-                boolean dirty = value.indexOf(',') >= 0 || value.indexOf(';') >= 0;
-                if (dirty) {
-                    sb.append("\"");
+                    visitor.visitMetadata(meta);
                 }
-                sb.append(value);
-                if (dirty) {
-                    sb.append("\"");
-                }
-            }
-            del = ", ";
-        }
-        return sb.toString();
-    }
-
-    /**
-     * Parse the XML metadata from the given file.
-     * @param metadataFile the metadata file
-     */
-    private void parseXMLMetadata(File metadataFile) {
-    	if (metadataFile.isDirectory()) {
-    		// Traverse the directory and parse all files.
-    		File[] files = metadataFile.listFiles();
-    		for (int i = 0; i< files.length; i++) {
-    			parseXMLMetadata(files[i]);
-    		}
-    	} else if (metadataFile.getName().endsWith(".xml")) { // Detect XML by extension,
-    														  // others are ignored.
-	        try {
-	            InputStream stream = null;
-	            URL url = metadataFile.toURI().toURL();
-	            if (url == null) {
-	                warn("Cannot find the metadata file : " + metadataFile.getAbsolutePath());
-	                m_metadata = new ArrayList/*Element*/();
-	            } else {
-	                stream = url.openStream();
-	                parseXMLMetadata(stream); // m_metadata is set by the method.
-	            }
-	        } catch (MalformedURLException e) {
-	            error("Cannot open the metadata input stream from " + metadataFile.getAbsolutePath() + ": " + e.getMessage());
-	            m_metadata = null;
-	        } catch (IOException e) {
-	            error("Cannot open the metadata input stream: " + metadataFile.getAbsolutePath() + ": " + e.getMessage());
-	            m_metadata = null;
-	        }
-
-	        // m_metadata can be either an empty array or an Element
-	        // array with component type description. It also can be null
-	        // if no metadata file is given.
-    	}
-    }
-
-    /**
-     * Parses XML Metadata.
-     * @param stream metadata input stream.
-     */
-    private void parseXMLMetadata(InputStream stream) {
-        Element[] meta = null;
-        try {
-            XMLReader parser = XMLReaderFactory.createXMLReader();
-            XMLMetadataParser handler = new XMLMetadataParser();
-            parser.setContentHandler(handler);
-            parser.setFeature("http://xml.org/sax/features/validation",
-                    true);
-            parser.setFeature("http://apache.org/xml/features/validation/schema",
-                    true);
-
-            parser.setErrorHandler(handler);
-
-            if (! m_ignoreLocalXSD) {
-                parser.setEntityResolver(new SchemaResolver());
             }
 
-            InputSource is = new InputSource(stream);
-            parser.parse(is);
-            meta = handler.getMetadata();
-            stream.close();
-
         } catch (IOException e) {
-            error("Cannot open the metadata input stream: " + e.getMessage());
-        } catch (ParseException e) {
-            error("Parsing error when parsing the XML file: " + e.getMessage());
-        } catch (SAXParseException e) {
-            error("Error during metadata parsing at line " + e.getLineNumber() + " : " + e.getMessage());
-        } catch (SAXException e) {
-            error("Parsing error when parsing (Sax Error) the XML file: " + e.getMessage());
-        }
-
-        if (meta == null || meta.length == 0) {
-            warn("Neither component types, nor instances in the XML metadata");
-        }
-
-        m_metadata.addAll(Arrays.asList(meta));
-    }
-
-    /**
-     * Get packages referenced by component.
-     * @return the list of referenced packages.
-     */
-    private List getReferredPackages() {
-        List referred = new ArrayList();
-        for (int i = 0; i < m_metadata.size(); i++) {
-            Element[] elems = ((Element) m_metadata.get(i)).getElements();
-            for (int j = 0; j < elems.length; j++) {
-                String att = elems[j].getAttribute("specification");
-                if (att != null) {
-                    int last = att.lastIndexOf('.');
-                    if (last != -1) {
-                        referred.add(att.substring(0, last));
-                    }
-                }
-            }
-        }
-        return referred;
-    }
-
-    /**
-     * Generate manipulation metadata.
-     * @param element : actual element.
-     * @param actual : actual manipulation metadata.
-     * @return : given manipulation metadata + manipulation metadata of the given element.
-     */
-    private StringBuffer buildManifestMetadata(Element element, StringBuffer actual) {
-    	// If the element is already here, do not re-add the element.
-        if(isInjectedElement(element)) {
-            return actual;
-        }
-
-        StringBuffer result = new StringBuffer();
-        if (element.getNameSpace() == null) {
-            result.append(actual + element.getName() + " { ");
-        } else {
-            result.append(actual + element.getNameSpace() + ":" + element.getName() + " { ");
-        }
-
-        Attribute[] atts = element.getAttributes();
-        for (int i = 0; i < atts.length; i++) {
-            Attribute current = (Attribute) atts[i];
-
-            if (current.getNameSpace() == null) {
-                result.append("$" + current.getName() + "=\"" + current.getValue() + "\" ");
-            } else {
-                result.append("$" + current.getNameSpace() + ":" + current.getName() + "=\"" + current.getValue() + "\" ");
-            }
-        }
-
-        Element[] elems = element.getElements();
-        for (int i = 0; i < elems.length; i++) {
-            result = buildManifestMetadata(elems[i], result);
-        }
-
-        result.append("}");
-        return result;
-    }
-
-    /**
-     * Checks if the given element is an iPOJO generated element from a prior treatment
-     * @return <code>true</code> if the given element was already injected by iPOJO
-     */
-    private boolean isInjectedElement(final Element element) {
-        Attribute[] atts = element.getAttributes();
-
-        if (m_manifestAttributeFilters == null) {
-            return false;
-        }
-
-        for (int i = 0; i < atts.length; i++) {
-
-            // First test : iPOJO injected elements filter
-            String value = atts[i].getValue();
-            if (value.startsWith(MethodCreator.PREFIX)
-                || value.contains("org.apache.felix.ipojo.InstanceManager")
-                || value.contains("_setInstanceManager")) {
-                    return true;
-            }
-
-            // Second test : customized filters
-            Iterator iterator = m_manifestAttributeFilters.iterator();
-            while (iterator.hasNext()) {
-                ManifestAttributeFilter filter = (ManifestAttributeFilter) iterator.next();
-                if (filter.accept(atts[i])) {
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Adds a Manifest attribute filter to the list
-     * @param filter The new Manifest attribute filter
-     */
-    public void addManifestAttributeFilters(final ManifestAttributeFilter filter) {
-        if (filter != null) {
-            m_manifestAttributeFilters.add(filter);
-        }
-    }
-
-    /**
-     * Retrieves an OutputStream to write in the Manifest.
-     * @return A valid output stream
-     */
-    protected void writeManifest(Manifest mf) throws IOException {
-        if (m_manifest == null) {
-            m_manifest = new File(m_dir, "META-INF/MANIFEST.MF");
-            if (! m_manifest.exists()) {
-                throw new IOException("Cannot find the manifest file : " + m_manifest.getAbsolutePath());
-            }
-        } else {
-            if (! m_manifest.exists()) {
-                throw new IOException("Cannot find the manifest file : " + m_manifest.getAbsolutePath());
-            }
+            m_reporter.error("Cannot load metadata " + e.getMessage());
+            return;
         }
 
-        mf.write(new FileOutputStream(m_manifest));
-    }
-
-    /**
-     * Retrieves an InputStream to read the Manifest.
-     * @return A valid input stream
-     */
-    protected InputStream getManifestInputStream() throws IOException {
-        if (m_manifest == null) {
-            File manFile = new File(m_dir, "META-INF/MANIFEST.MF");
-            if (manFile.exists()) {
-                return new FileInputStream(manFile);
-            } else {
-                throw new IOException("Cannot find the manifest file : " + manFile.getAbsolutePath());
-            }
-        } else {
-            if (m_manifest.exists()) {
-                return new FileInputStream(m_manifest);
-            } else {
-                throw new IOException("Cannot find the manifest file : " + m_manifest.getAbsolutePath());
-            }
-        }
-    }
+        // Start the manipulation
+        engine.generate();
 
-    /**
-     * Writes the .class raw data to the storage support
-     * @param classFile - Output .class file
-     * @param rawClass - Raw class representation
-     * @throws IOException - Something wrong occurred while writing the file
-     */
-    protected void setBytecode(final File classFile, final byte[] rawClass) throws IOException {
-        OutputStream os = new FileOutputStream(classFile);
-        os.write(rawClass);
-        os.close();
-    }
+        // Tell the visitor that we have finished
+        visitor.visitEnd();
 
-    public List getWarnings() {
-        return m_warnings;
     }
-
 }
 

Added: felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/Reporter.java
URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/Reporter.java?rev=1159173&view=auto
==============================================================================
--- felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/Reporter.java (added)
+++ felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/Reporter.java Thu Aug 18 11:32:19 2011
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.ipojo.manipulator;
+
+import java.util.List;
+
+/**
+ * A {@code Reporter} is responsible to handle feedback from within the
+ * manipulation process in order to let API consumers deal with errors
+ * and warnings.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface Reporter {
+
+    /**
+     * Add aa trace message
+     * It accepts a {@link Throwable} as last argument.
+     * @param message trace message.
+     * @param args message's argument
+     */
+    void trace(String message, Object... args);
+
+    /**
+     * Add an informative message
+     * It accepts a {@link Throwable} as last argument.
+     * @param message info message.
+     * @param args message's argument
+     */
+    void info(String message, Object... args);
+
+    /**
+     * Add a message in the warning list.
+     * It accepts a {@link Throwable} as last argument.
+     * @param message warning message.
+     * @param args message's argument
+     */
+    void warn(String message, Object... args);
+
+    /**
+     * Add a message in the error list.
+     * It accepts a {@link Throwable} as last argument.
+     * @param message error message.
+     * @param args message's argument
+     */
+    void error(String message, Object... args);
+
+    /**
+     * @return all the errors (fatal) reported by the manipulation process.
+     */
+    List<String> getErrors();
+
+    /**
+     * @return all the warnings (non fatal) reported by the manipulation process.
+     */
+    List<String> getWarnings();
+}

Added: felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/ResourceStore.java
URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/ResourceStore.java?rev=1159173&view=auto
==============================================================================
--- felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/ResourceStore.java (added)
+++ felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/ResourceStore.java Thu Aug 18 11:32:19 2011
@@ -0,0 +1,77 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.ipojo.manipulator;
+
+import org.apache.felix.ipojo.metadata.Element;
+
+import java.io.IOException;
+import java.util.jar.Manifest;
+
+/**
+ * Abstract input/output for the manipulation process.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ResourceStore {
+
+    /**
+     * Return the bytecode of the given class name.
+     * @param path normalized resource path (format: {@literal org/objectweb/asm/Visitor.class})
+     * @return the byte array representing the given class
+     * @throws IOException if resource was not found
+     */
+    byte[] read(String path) throws IOException;
+
+    /**
+     * Browse all resources available in this store.
+     * @param visitor is called for each available resource
+     */
+    void accept(ResourceVisitor visitor);
+
+    /**
+     * Notify the store that resource will be written.
+     * @throws IOException if there was an error
+     */
+    void open() throws IOException;
+
+    /**
+     * Writes the given Element into this store.
+     * Typically a store implementation will use this to build a Manifest.
+     * @param metadata Element metadata to be inserted
+     */
+    void writeMetadata(Element metadata);
+
+    /**
+     * Notify the builder that a new resource has been built and should
+     * be stored in the resulting bundle.
+     * @param resourcePath resource name of the class (format: {@literal org/objectweb/asm/Visitor.class})
+     * @param resource content of the resource
+     * @throws IOException if there was an error storing the resource
+     */
+    void write(String resourcePath, byte[] resource) throws IOException;
+
+    /**
+     * Close the store: no methods will be called anymore on this instance.
+     * @throws IOException if close failed
+     */
+    void close() throws IOException;
+
+
+}

Added: felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/ResourceVisitor.java
URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/ResourceVisitor.java?rev=1159173&view=auto
==============================================================================
--- felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/ResourceVisitor.java (added)
+++ felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/ResourceVisitor.java Thu Aug 18 11:32:19 2011
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.ipojo.manipulator;
+
+/**
+ * A {@code ResourceVisitor} is invoked when a potential class is
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface ResourceVisitor {
+
+    /**
+     * Visit a resource discovered in the original bundle.
+     * @param name resource's name (format: {@literal org/objectweb/asm/Visitor.class})
+     */
+    void visit(String name);
+}

Added: felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/manifest/DirectManifestProvider.java
URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/manifest/DirectManifestProvider.java?rev=1159173&view=auto
==============================================================================
--- felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/manifest/DirectManifestProvider.java (added)
+++ felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/manifest/DirectManifestProvider.java Thu Aug 18 11:32:19 2011
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.ipojo.manipulator.manifest;
+
+import java.util.jar.Manifest;
+
+import org.apache.felix.ipojo.manipulator.ManifestProvider;
+
+/**
+ * A {@code DirectManifestProvider} simply serves an already available {@link Manifest}.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class DirectManifestProvider implements ManifestProvider {
+
+    /**
+     * Served Manifest.
+     */
+    private Manifest manifest;
+
+    /**
+     * Construct a provider servicing the given manifest.
+     * @param manifest Manifest to be serviced
+     */
+    public DirectManifestProvider(Manifest manifest) {
+        this.manifest = manifest;
+    }
+
+    public Manifest getManifest() {
+        return manifest;
+    }
+}

Added: felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/manifest/FileManifestProvider.java
URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/manifest/FileManifestProvider.java?rev=1159173&view=auto
==============================================================================
--- felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/manifest/FileManifestProvider.java (added)
+++ felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/manifest/FileManifestProvider.java Thu Aug 18 11:32:19 2011
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.ipojo.manipulator.manifest;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.jar.Manifest;
+
+import org.apache.felix.ipojo.manipulator.ManifestProvider;
+import org.apache.felix.ipojo.manipulator.util.Streams;
+
+/**
+ * A {@code FileManifestProvider} reads a {@link Manifest} from the given input {@link File}.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FileManifestProvider implements ManifestProvider {
+
+    /**
+     * Read manifest.
+     */
+    private Manifest manifest;
+
+    /**
+     * Read the manifest from the given input file
+     * @param resource File pointing to a Manifest file
+     * @throws IOException if there is an error during File IO operations
+     *         or if the file is not a valid manifest.
+     */
+    public FileManifestProvider(File resource) throws IOException {
+        manifest = new Manifest();
+        InputStream is = null;
+        try {
+            is = new FileInputStream(resource);
+            manifest.read(is);
+        } finally {
+            Streams.close(is);
+        }
+    }
+
+    public Manifest getManifest() {
+        return manifest;
+    }
+}

Added: felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/metadata/AnnotationMetadataProvider.java
URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/metadata/AnnotationMetadataProvider.java?rev=1159173&view=auto
==============================================================================
--- felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/metadata/AnnotationMetadataProvider.java (added)
+++ felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/metadata/AnnotationMetadataProvider.java Thu Aug 18 11:32:19 2011
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.ipojo.manipulator.metadata;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.felix.ipojo.manipulation.annotations.MetadataCollector;
+import org.apache.felix.ipojo.manipulator.MetadataProvider;
+import org.apache.felix.ipojo.manipulator.Reporter;
+import org.apache.felix.ipojo.manipulator.ResourceStore;
+import org.apache.felix.ipojo.manipulator.ResourceVisitor;
+import org.apache.felix.ipojo.metadata.Element;
+import org.objectweb.asm.ClassReader;
+
+/**
+ * A {@code AnnotationMetadataProvider} loads iPOJO metadata from bytecode of classes.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AnnotationMetadataProvider implements MetadataProvider {
+
+    private ResourceStore store;
+
+    private Reporter reporter;
+
+    public AnnotationMetadataProvider(final ResourceStore store,
+                                      final Reporter reporter) {
+        this.store = store;
+        this.reporter = reporter;
+    }
+
+    public List<Element> getMetadatas() throws IOException {
+
+        final List<Element> metadata = new ArrayList<Element>();
+
+
+        store.accept(new ResourceVisitor() {
+            public void visit(String name) {
+                if (name.endsWith(".class")) {
+
+                    // Read file's content
+                    byte[] data = null;
+                    try {
+                        data = store.read(name);
+                    } catch (IOException e) {
+                        reporter.warn("Cannot read content of " + name);
+                    }
+
+                    // We check the array size to avoid manipulating empty files
+                    // produced by incremental compilers (like Eclipse Compiler)
+                    if (data != null && data.length > 0) {
+                        computeAnnotations(data, metadata);
+                    } else {
+                        reporter.error("Cannot compute annotations from " + name + " : Empty file");
+                    }
+                }
+            }
+        });
+
+        return metadata;
+    }
+
+    /**
+     * Parse the content of the class to detect annotated classes.
+     * @param bytecode the class to inspect.
+     * @param metadata
+     */
+    private void computeAnnotations(byte[] bytecode, List<Element> metadata) {
+
+        ClassReader cr = new ClassReader(bytecode);
+        MetadataCollector collector = new MetadataCollector();
+        cr.accept(collector, 0);
+
+        if (collector.isIgnoredBecauseOfMissingComponent()) {
+        	// No @Component, just skip.
+            //warn("Annotation processing ignored in " + collector.getClassName() + " - @Component missing");
+        } else if (collector.isComponentType()) {
+
+            // if no metadata or empty one, create a new array.
+            metadata.add(collector.getComponentTypeDeclaration());
+
+            // Instantiate ?
+            if (collector.getInstanceDeclaration() != null) {
+                //warn("Declaring an empty instance of " + elem.getAttribute("classname"));
+                metadata.add(collector.getInstanceDeclaration());
+            }
+        }
+    }
+
+
+}

Added: felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/metadata/CacheableMetadataProvider.java
URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/metadata/CacheableMetadataProvider.java?rev=1159173&view=auto
==============================================================================
--- felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/metadata/CacheableMetadataProvider.java (added)
+++ felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/metadata/CacheableMetadataProvider.java Thu Aug 18 11:32:19 2011
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.ipojo.manipulator.metadata;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.felix.ipojo.manipulator.MetadataProvider;
+import org.apache.felix.ipojo.metadata.Element;
+
+/**
+ * A {@code CacheableMetadataProvider} provides simple caching methods to avoid
+ * calling {@link MetadataProvider#getMetadatas()} twice.
+ *
+ * TODO Do we really need this ? ATM it is not used ...
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class CacheableMetadataProvider implements MetadataProvider {
+
+    /**
+     * Delegate.
+     */
+    private MetadataProvider delegate;
+
+    /**
+     * Cached elements.
+     */
+    private List<Element> cached;
+
+    public CacheableMetadataProvider(MetadataProvider delegate) {
+        this.delegate = delegate;
+    }
+
+    public List<Element> getMetadatas() throws IOException {
+
+        // Only ask the delegate if cache is empty
+        if (cached == null) {
+            cached = new ArrayList<Element>();
+            cached.addAll(delegate.getMetadatas());
+        }
+        return cached;
+    }
+}

Added: felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/metadata/CompositeMetadataProvider.java
URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/metadata/CompositeMetadataProvider.java?rev=1159173&view=auto
==============================================================================
--- felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/metadata/CompositeMetadataProvider.java (added)
+++ felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/metadata/CompositeMetadataProvider.java Thu Aug 18 11:32:19 2011
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.ipojo.manipulator.metadata;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.felix.ipojo.manipulator.MetadataProvider;
+import org.apache.felix.ipojo.manipulator.Reporter;
+import org.apache.felix.ipojo.metadata.Element;
+
+/**
+ * A {@code CompositeMetadataProvider} is responsible to detect duplicates
+ * component's declaration.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class CompositeMetadataProvider implements MetadataProvider {
+
+    private List<MetadataProvider> providers = new ArrayList<MetadataProvider>();
+    private Reporter reporter;
+
+    public CompositeMetadataProvider(Reporter reporter) {
+        this.reporter = reporter;
+    }
+
+    public void addMetadataProvider(MetadataProvider provider) {
+        this.providers.add(provider);
+    }
+
+    public List<Element> getMetadatas() throws IOException {
+        List<Element> metadata = new ArrayList<Element>();
+        for (MetadataProvider provider : providers) {
+
+            List<Element> loaded = provider.getMetadatas();
+
+            // Analyze each newly loaded metadata
+            // And find duplicate component definition
+
+            for (Element meta : loaded) {
+                if (isInstance(meta)) {
+                    // This is an instance, just add it to the list
+                    metadata.add(meta);
+                } else {
+                    // Handler or Component
+                    // Finds duplicate (if any)
+                    String name = getComponentName(meta);
+                    if (name != null) {
+                        if (isDuplicate(metadata, name)) {
+                            // TODO Try to add more information here, but what ?
+                            reporter.warn("The component type " + name + " is duplicated.");
+                        } else {
+                            metadata.add(meta);
+                        }
+                    } else {
+                        // no name, strange, but add it to the list
+                        metadata.add(meta);
+                    }
+                }
+            }
+        }
+        return metadata;
+    }
+
+    private boolean isDuplicate(List<Element> elements, String name) {
+        for (Element element : elements) {
+            if (!isInstance(element) && name.equals(getComponentName(element))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private String getComponentName(Element element) {
+        return element.getAttribute("name");
+    }
+
+    private boolean isInstance(Element element) {
+        return "instance".equals(element.getName());
+    }
+}

Added: felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/metadata/EmptyMetadataProvider.java
URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/metadata/EmptyMetadataProvider.java?rev=1159173&view=auto
==============================================================================
--- felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/metadata/EmptyMetadataProvider.java (added)
+++ felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/metadata/EmptyMetadataProvider.java Thu Aug 18 11:32:19 2011
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.ipojo.manipulator.metadata;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.felix.ipojo.manipulator.MetadataProvider;
+import org.apache.felix.ipojo.metadata.Element;
+
+/**
+ * A {@code EmptyMetadataProvider} simply returns an empty list of Element.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class EmptyMetadataProvider implements MetadataProvider {
+
+    public List<Element> getMetadatas() throws IOException {
+        // Do not use Collections.emptyList() as the manipulator
+        // adds discovered components in this list
+        // TODO maybe the manipulator can have it's own list add we simply addAll() ?
+        return new ArrayList<Element>();
+    }
+}

Added: felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/metadata/FileMetadataProvider.java
URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/metadata/FileMetadataProvider.java?rev=1159173&view=auto
==============================================================================
--- felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/metadata/FileMetadataProvider.java (added)
+++ felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/metadata/FileMetadataProvider.java Thu Aug 18 11:32:19 2011
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.ipojo.manipulator.metadata;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.felix.ipojo.manipulator.MetadataProvider;
+import org.apache.felix.ipojo.manipulator.Reporter;
+import org.apache.felix.ipojo.metadata.Element;
+
+/**
+ * A {@code FileMetadataProvider} is responsible of loading all the {@literal .xml} files in the given directory.
+ * It also accepts a direct reference to a {@literal metadata.xml} file.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FileMetadataProvider implements MetadataProvider {
+
+    /**
+     * Metadata source (file or directory).
+     */
+    private File source;
+
+    /**
+     * Feedback reporter.
+     */
+    private Reporter reporter;
+
+    /**
+     * Validate using local schemas or not ?
+     */
+    private boolean validateUsingLocalSchemas = false;
+
+    /**
+     * Constructs a metadata provider using the given source File (directory
+     * or file) to load iPOJO metadata.
+     * @param source source of the metadata
+     * @param reporter feedback reporter
+     */
+    public FileMetadataProvider(File source, Reporter reporter) {
+        this.source = source;
+        this.reporter = reporter;
+    }
+
+    public void setValidateUsingLocalSchemas(boolean validateUsingLocalSchemas) {
+        this.validateUsingLocalSchemas = validateUsingLocalSchemas;
+    }
+
+    public List<Element> getMetadatas() throws IOException {
+
+        List<Element> metadata = new ArrayList<Element>();
+        traverse(source, metadata);
+        return metadata;
+    }
+
+    private void traverse(File file, List<Element> metadata) {
+        if (file.isDirectory()) {
+            // Traverse the directory and parse all files.
+            File[] files = file.listFiles();
+            for (File child : files) {
+                traverse(child, metadata);
+            }
+        } else if (file.getName().endsWith(".xml")) { // Detect XML by extension,
+                                                      // others are ignored.
+            loadFileMetadata(file, metadata);
+        }
+
+    }
+
+    private void loadFileMetadata(File file, List<Element> metadata) {
+        try {
+            // Sanity check, should be OK, but we never know ...
+            InputStream stream = null;
+            URL url = file.toURI().toURL();
+            if (url == null) {
+                reporter.warn("Cannot find the metadata file : " + source.getAbsolutePath());
+            } else {
+                stream = url.openStream();
+                StreamMetadataProvider provider = new StreamMetadataProvider(stream, reporter);
+                provider.setValidateUsingLocalSchemas(validateUsingLocalSchemas);
+                metadata.addAll(provider.getMetadatas());
+            }
+        } catch (MalformedURLException e) {
+            reporter.error("Cannot open the metadata input stream from " + source.getAbsolutePath() + ": " + e.getMessage());
+        } catch (IOException e) {
+            reporter.error("Cannot open the metadata input stream: " + source.getAbsolutePath() + ": " + e.getMessage());
+        }
+
+    }
+}