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());
+ }
+
+ }
+}