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 2007/06/24 17:11:35 UTC

svn commit: r550247 [2/3] - in /felix/trunk/ipojo/manipulator: ./ src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/felix/ src/main/java/org/apache/felix/ipojo/ src/main/java/org/apache/felix/ipojo/mani...

Added: 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?view=auto&rev=550247
==============================================================================
--- felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/Pojoization.java (added)
+++ felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/Pojoization.java Sun Jun 24 08:11:33 2007
@@ -0,0 +1,610 @@
+/* 
+ * 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.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+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.Manipulator;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.xml.parser.ParseException;
+import org.apache.felix.ipojo.xml.parser.XMLMetadataParser;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+/**
+ * Pojoization allows creating an iPOJO bundle from a "normal" bundle.  
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class Pojoization {
+
+    /**
+     * List of component types.
+     */
+    private List m_components;
+
+    /**
+     * Metadata (in internal format).
+     */
+    private Element[] m_metadata;
+
+    /**
+     * Errors occured during the manipulation.
+     */
+    private List m_errors = new ArrayList();
+
+    /**
+     * Warnings occured during the manipulation.
+     */
+    private List m_warnings = new ArrayList();
+
+    /**
+     * Class map (jar entry, byte[]).
+     */
+    private HashMap m_classes = new HashMap();
+
+    /**
+     * Referendec packages by the composite.
+     */
+    private List m_referredPackages;
+
+    /**
+     * Add an error in the error list.
+     * @param mes : error message.
+     */
+    private void error(String mes) {
+        System.out.println("An error occurs during the pojoization : " + mes);
+        m_errors.add(mes);
+    }
+
+    /**
+     * Add a warning in the warning list.
+     * @param mes : warning message
+     */
+    public void warn(String mes) {
+        System.out.println("An warning occurs during the pojoization : " + mes);
+        m_warnings.add(mes);
+    }
+
+    public List getErrors() {
+        return m_errors;
+    }
+
+    /**
+     * Manipulate a normal bundle.
+     * It will create an iPOJO bundle based on the given metadata file.
+     * The original and final bundle must be different.
+     * @param in : original bundle.
+     * @param out : final bundle.
+     * @param metadataFile : iPOJO metadata file (XML). 
+     */
+    public void pojoization(File in, File out, File metadataFile) {
+        // Get the metadata.xml location
+        String path = metadataFile.getAbsolutePath();
+        if (!path.startsWith("/")) {
+            path = "/" + path;
+        }
+        m_metadata = parseXMLMetadata(path);
+
+        // Get the list of declared component
+        m_components = getDeclaredComponents(m_metadata);
+
+        // Start the manipulation
+        manipulation(in, 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");
+            }
+        }
+    }
+
+    /**
+     * Manipulate the Bundle.
+     * @param in : original bundle
+     * @param out : final bundle
+     */
+    private void manipulation(File in, File out) {
+        // Get a jar file from the given file
+        JarFile inputJar = null;
+        try {
+            inputJar = new JarFile(in);
+        } catch (IOException e) {
+            error("Cannot the input file is not a JarFile : " + in.getAbsolutePath());
+            return;
+        }
+
+        manipulateComponents(inputJar); // Manipulate classes
+        Manifest mf = doManifest(inputJar); // 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 file " + out.getAbsolutePath() + " 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 = inputJar.entries();
+            while (entries.hasMoreElements()) {
+                JarEntry curEntry = (JarEntry) entries.nextElement();
+                // Check if we need to manipulate the clazz
+                if (m_classes.containsKey(curEntry.getName())) {
+                    JarEntry je = new JarEntry(curEntry.getName());
+                    byte[] outClazz = (byte[]) m_classes.get(curEntry.getName());
+                    if (outClazz.length != 0) {
+                        jos.putNextEntry(je); // copy the entry header to jos
+                        jos.write(outClazz);
+                        jos.closeEntry();
+                    } else { // The class is already manipulated
+                        InputStream currIn = inputJar.getInputStream(curEntry);
+                        int c;
+                        int i = 0;
+                        while ((c = currIn.read()) >= 0) {
+                            jos.write(c);
+                            i++;
+                        }
+                        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 = inputJar.getInputStream(curEntry);
+                        int c;
+                        int i = 0;
+                        while ((c = currIn.read()) >= 0) {
+                            jos.write(c);
+                            i++;
+                        }
+                        currIn.close();
+                        jos.closeEntry();
+                    }
+                }
+            }
+        } catch (IOException e) {
+            error("Cannot manipulate the Jar file : " + e.getMessage() + " - Cause : " + e.getCause());
+            return;
+        }
+
+        try {
+            inputJar.close();
+            jos.close();
+            fos.close();
+            jos = null;
+            fos = null;
+        } catch (IOException e) {
+            error("Cannot close the new Jar file : " + e.getMessage() + " - Cause : " + e.getCause());
+            return;
+        }
+    }
+
+    /**
+     * Manipulate classes of the input Jar.
+     * @param inputJar : input bundle.
+     */
+    private void manipulateComponents(JarFile inputJar) {
+        Enumeration entries = inputJar.entries();
+        while (entries.hasMoreElements()) {
+            JarEntry curEntry = (JarEntry) entries.nextElement();
+            // Check if we need to manipulate the clazz
+            for (int i = 0; i < m_components.size(); i++) {
+                ComponentInfo ci = (ComponentInfo) m_components.get(i);
+                if (ci.m_classname.equals(curEntry.getName())) {
+                    byte[] outClazz = manipulateComponent(inputJar, curEntry, ci);
+                    m_classes.put(curEntry.getName(), outClazz);
+                }
+            }
+        }
+    }
+
+    /**
+     * Create the manifest.
+     * Set the bundle activator, imports, iPOJO-components clauses
+     * @param initial : initial Jar file.
+     * @return the generated manifest.
+     */
+    private Manifest doManifest(JarFile initial) {
+        Manifest mf = null;
+        try {
+            mf = initial.getManifest(); // Get the initial manifest
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        Attributes att = mf.getMainAttributes();
+        setBundleActivator(att); // Set the bundle activator
+        setImports(att); // Set the imports (add ipojo and handler namespaces
+        setPOJOMetadata(att); // Add iPOJO-Component
+        setCreatedBy(att); // Add iPOJO to the creators
+        return mf;
+    }
+
+    /**
+     * Manipulate a component class.
+     * @param inputJar : input bundle
+     * @param je : Jar entry of the classes
+     * @param ci : attached component info (containing metadata and manipulation metadata)
+     * @return the generated class (byte array)
+     */
+    private byte[] manipulateComponent(JarFile inputJar, JarEntry je, ComponentInfo ci) {
+        Manipulator man = new Manipulator();
+        try {
+            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;
+            }
+            currIn.close();
+            byte[] out = man.manipulate(in); // iPOJO manipulation
+            // Insert information to metadata
+            ci.m_componentMetadata.addElement(man.getManipulationMetadata());
+            ci.m_isManipulated = true;
+            return out;
+        } catch (IOException e) {
+            error("Cannot manipulate the class " + je.getName() + " : " + e.getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * Return the list of "concrete" component.
+     * @param meta : metadata.
+     * @return the list of compionent info requiring a manipulation.
+     */
+    private List getDeclaredComponents(Element[] meta) {
+        List componentClazzes = new ArrayList();
+        for (int i = 0; i < meta.length; i++) {
+            if (meta[i].getName().equalsIgnoreCase("component") && meta[i].containsAttribute("className")) {
+                String name = meta[i].getAttribute("classname");
+                name = name.replace('.', '/');
+                name += ".class";
+                componentClazzes.add(new ComponentInfo(name, meta[i]));
+            }
+        }
+        return 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;
+
+        /**
+         * 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;
+        }
+    }
+
+    /**
+     * Set the bundle activator in the manifest.
+     * @param att : manifest attribute.
+     */
+    private void setBundleActivator(Attributes att) {
+        if (att.containsKey("Bundle-Activator")) {
+            warn("The bundle contains another Bundle-Activator : " + att.getValue("Bundle-Activator") + " - Replace the existing one");
+        }
+        att.putValue("Bundle-Activator", "org.apache.felix.ipojo.Activator");
+    }
+
+    /**
+     * Set the create-by in the manifest.
+     * @param att : manifest attribute.
+     */
+    private void setCreatedBy(Attributes att) {
+        String prev = att.getValue("Created-By");
+        att.putValue("Created-By", prev + " & iPOJO");
+    }
+
+    /**
+     * 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", "0.7.3");
+        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.cm", verCM);
+        }
+        
+
+        // Add handler namespace
+        String[][] namespaces = computeHandlerNamespace();
+        for (int j = 0; j < namespaces.length; j++) {
+            for (int k = 0; k < namespaces[j].length; k++) {
+                if (!namespaces[j][k].equals("")) {
+                    int lastIndex = namespaces[j][k].lastIndexOf('.');
+                    String ns = namespaces[j][k].substring(0, lastIndex);
+                    if (!imports.containsKey(ns)) {
+                        imports.put(ns, new TreeMap());
+                    }
+                }
+            }
+        }
+
+        // Add refered 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) {
+        String meta = "";
+        for (int i = 0; i < m_metadata.length; i++) {
+            meta += buildManifestMetadata(m_metadata[i], "");
+        }
+        att.putValue("iPOJO-Components", meta);
+    }
+
+    /**
+     * Build the list of namespaces used in the metadata. (first-order only). 
+     * @return the list of namespaces [array of component [ array of namespace ] ].
+     */
+    private String[][] computeHandlerNamespace() {
+        String[][] ns = new String[m_metadata.length][];
+        for (int i = 0; i < m_metadata.length; i++) {
+            ns[i] = m_metadata[i].getNamespaces();
+        }
+        return ns;
+    }
+
+    /**
+     * 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 LinkedHashMap();
+        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);
+                } 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.keySet().iterator(); i.hasNext();) {
+            String name = (String) i.next();
+            Map map = (Map) exports.get(name);
+            sb.append(del);
+            sb.append(name);
+
+            for (Iterator j = map.keySet().iterator(); j.hasNext();) {
+                String key = (String) j.next();
+
+                // Skip directives we do not recognize
+                if (key.endsWith(":") && allowedDirectives.indexOf(key) < 0) {
+                    continue;
+                }
+
+                String value = (String) map.get(key);
+                sb.append(";");
+                sb.append(key);
+                sb.append("=");
+                boolean dirty = value.indexOf(',') >= 0 || value.indexOf(';') >= 0;
+                if (dirty) {
+                    sb.append("\"");
+                }
+                sb.append(value);
+                if (dirty) {
+                    sb.append("\"");
+                }
+            }
+            del = ", ";
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Parse XML Metadata.
+     * @param path : path of the file to parse.
+     * @return the parsed element array.
+     */
+    private Element[] parseXMLMetadata(String path) {
+        File metadata = new File(path);
+        URL url;
+        Element[] meta = null;
+        try {
+            url = metadata.toURI().toURL();
+            if (url == null) {
+                error("Cannot find the metdata file : " + path);
+                return null;
+            }
+
+            InputStream stream = url.openStream();
+            XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
+            XMLMetadataParser handler = new XMLMetadataParser();
+            parser.setContentHandler(handler);
+            InputSource is = new InputSource(stream);
+            parser.parse(is);
+            meta = handler.getMetadata();
+            m_referredPackages = handler.getReferredPackages();
+            stream.close();
+
+        } catch (MalformedURLException e) {
+            error("Malformed Mtadata URL for " + path);
+            return null;
+        } catch (IOException e) {
+            error("Cannot open the file : " + path);
+            return null;
+        } catch (ParseException e) {
+            error("Parsing Error when parsing the XML file " + path + " : " + e.getMessage());
+            return null;
+        } catch (SAXException e) {
+            error("Parsing Error when parsing (Sax Error) the XML file " + path + " : " + e.getMessage());
+            return null;
+        }
+
+        if (meta == null || meta.length == 0) {
+            warn("Neither component, neither instance in " + path);
+        }
+
+        return meta;
+    }
+
+    /**
+     * Generate manipulation metadata.
+     * @param element : actual element. 
+     * @param actual : actual manipulation metadata.
+     * @return : given amnipulation metadata + manipulation metadata of the given element.
+     */
+    private String buildManifestMetadata(Element element, String actual) {
+        String result = "";
+        if (element.getNameSpace().equals("")) {
+            result = actual + element.getName() + " { ";
+        } else {
+            result = actual + element.getNameSpace() + ":" + element.getName() + " { ";
+        }
+
+        for (int i = 0; i < element.getAttributes().length; i++) {
+            if (element.getAttributes()[i].getNameSpace().equals("")) {
+                result = result + "$" + element.getAttributes()[i].getName() + "=\"" + element.getAttributes()[i].getValue() + "\" ";
+            } else {
+                result = result + "$" + element.getAttributes()[i].getNameSpace() + ":" + element.getAttributes()[i].getName() + "=\"" + element.getAttributes()[i].getValue() + "\" ";
+            }
+        }
+
+        for (int i = 0; i < element.getElements().length; i++) {
+            result = buildManifestMetadata(element.getElements()[i], result);
+        }
+
+        return result + "}";
+    }
+
+    public List getWarnings() {
+        return m_warnings;
+    }
+
+}

Added: felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/QuotedTokenizer.java
URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/QuotedTokenizer.java?view=auto&rev=550247
==============================================================================
--- felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/QuotedTokenizer.java (added)
+++ felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/QuotedTokenizer.java Sun Jun 24 08:11:33 2007
@@ -0,0 +1,204 @@
+/* 
+ * 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.ArrayList;
+import java.util.List;
+
+/**
+ * Parse on OSGi Manifest clause. 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class QuotedTokenizer {
+    /**
+     * String to parse.
+     */
+    String m_string;
+
+    /**
+     * Index.
+     */
+    int m_index = 0;
+
+    /**
+     * Default spearators to use.
+     */
+    String m_separators;
+
+    /**
+     * Does the tokenizer returns token.
+     */
+    boolean m_returnTokens;
+
+    /**
+     * Does the tokenizer should ignore white space.
+     */
+    boolean m_ignoreWhiteSpace = true;
+
+    /**
+     * Peek.
+     */
+    String m_peek;
+
+    /**
+     * Separator.
+     */
+    char m_separator;
+
+    /**
+     * Constructors.
+     * @param string : input String.
+     * @param separators : separators.
+     * @param returnTokens : should the tokenizer return tokens ?
+     */
+    public QuotedTokenizer(String string, String separators, boolean returnTokens) {
+        if (string == null) {
+            throw new IllegalArgumentException("string argument must be not null");
+        }
+        this.m_string = string;
+        this.m_separators = separators;
+        this.m_returnTokens = returnTokens;
+    }
+
+    /**
+     * Constructors.
+     * Fixe returnTokens to false.
+     * @param string : input String.
+     * @param separators : separators
+     */
+    public QuotedTokenizer(String string, String separators) {
+        this(string, separators, false);
+    }
+
+    /**
+     * Get the next token.
+     * @param separators : separators to used.
+     * @return : the next token.
+     */
+    public String nextToken(String separators) {
+        m_separator = 0;
+        if (m_peek != null) {
+            String tmp = m_peek;
+            m_peek = null;
+            return tmp;
+        }
+
+        if (m_index == m_string.length()) { return null; }
+
+        StringBuffer sb = new StringBuffer();
+
+        while (m_index < m_string.length()) {
+            char c = m_string.charAt(m_index++);
+
+            if (Character.isWhitespace(c)) {
+                if (m_index == m_string.length()) {
+                    break;
+                } else {
+                    continue;
+                }
+            }
+
+            if (separators.indexOf(c) >= 0) {
+                if (m_returnTokens) {
+                    m_peek = Character.toString(c);
+                } else {
+                    m_separator = c;
+                }
+                break;
+            }
+
+            switch (c) {
+                case '"':
+                case '\'':
+                    quotedString(sb, c);
+                    break;
+
+                default:
+                    sb.append(c);
+            }
+        }
+        String result = sb.toString().trim();
+        if (result.length() == 0 && m_index == m_string.length()) { return null; }
+        return result;
+    }
+
+    /**
+     * Get the next token.
+     * Used the defined separators.
+     * @return the next token.
+     */
+    public String nextToken() {
+        return nextToken(m_separators);
+    }
+
+    /**
+     * Append the next quote to the given String Buffer.
+     * @param sb : accumulator.
+     * @param c : quote.
+     */
+    private void quotedString(StringBuffer sb, char c) {
+        char quote = c;
+        while (m_index < m_string.length()) {
+            c = m_string.charAt(m_index++);
+            if (c == quote) { break; }
+            if (c == '\\' && m_index < m_string.length() && m_string.charAt(m_index + 1) == quote) {
+                c = m_string.charAt(m_index++);
+            }
+            sb.append(c);
+        }
+    }
+
+    public String[] getTokens() {
+        return getTokens(0);
+    }
+
+    /**
+     * Get the list of tokens.
+     * @param cnt : array length.
+     * @return : the array of token.
+     */
+    private String[] getTokens(int cnt) {
+        String token = nextToken();
+        if (token == null) {
+            return new String[cnt];
+        }
+
+        String[] result = getTokens(cnt + 1);
+        result[cnt] = token;
+        return result;
+    }
+
+    public char getSeparator() {
+        return m_separator;
+    }
+
+    /**
+     * Get token list.
+     * @return the list of token.
+     */
+    public List getTokenSet() {
+        List list = new ArrayList();
+        String token = nextToken();
+        while (token != null) {
+            list.add(token);
+            token = nextToken();
+        }
+        return list;
+    }
+}

Added: felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/xml/parser/ParseException.java
URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/xml/parser/ParseException.java?view=auto&rev=550247
==============================================================================
--- felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/xml/parser/ParseException.java (added)
+++ felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/xml/parser/ParseException.java Sun Jun 24 08:11:33 2007
@@ -0,0 +1,40 @@
+/* 
+ * 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.xml.parser;
+
+/**
+ * Exceptions thrown by parsers.
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class ParseException extends Exception {
+
+    /**
+     * serialVersionUID.
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Parsing error.
+     * @param msg : the error emssage.
+     */
+    public ParseException(String msg) {
+        super(msg);
+    }
+
+}

Added: felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/xml/parser/XMLMetadataParser.java
URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/xml/parser/XMLMetadataParser.java?view=auto&rev=550247
==============================================================================
--- felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/xml/parser/XMLMetadataParser.java (added)
+++ felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/xml/parser/XMLMetadataParser.java Sun Jun 24 08:11:33 2007
@@ -0,0 +1,311 @@
+/* 
+ * 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.xml.parser;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+
+/**
+ * XML Metadata parser.
+ * 
+ * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
+ */
+public class XMLMetadataParser implements ContentHandler {
+
+    /**
+     * Element of the metadata.
+     */
+    private Element[] m_elements = new Element[0];
+
+    /**
+     * Get componenet type metadata. (both component and composite)
+     * 
+     * @return a components metadata
+     * @throws ParseException
+     *             when an error occurs in the xml parsing
+     */
+    public Element[] getComponentsMetadata() throws ParseException {
+        Element[] comp = m_elements[0].getElements("Component");
+        Element[] compo = m_elements[0].getElements("Composite");
+        Element[] metadata = new Element[comp.length + compo.length];
+        int l = 0;
+        for (int i = 0; i < comp.length; i++) {
+            metadata[l] = comp[i];
+            l++;
+        }
+        for (int i = 0; i < compo.length; i++) {
+            metadata[l] = compo[i];
+            l++;
+        }
+        return metadata;
+    }
+
+    /**
+     * Get packages referenced by composite.
+     * 
+     * @return the list of referenced packages.
+     */
+    public List getReferredPackages() {
+        List referred = new ArrayList();
+        Element[] compo = m_elements[0].getElements("Composite");
+        for (int i = 0; i < compo.length; i++) {
+            for (int j = 0; j < compo[i].getElements().length; j++) {
+                if (compo[i].getElements()[j].containsAttribute("specification")) {
+                    String p = compo[i].getElements()[j].getAttribute("specification");
+                    int last = p.lastIndexOf('.');
+                    if (last != -1) {
+                        referred.add(p.substring(0, last));
+                    }
+                }
+            }
+        }
+        return referred;
+    }
+
+    /**
+     * Get parsed metadata.
+     * The document must be parsed before calling this method. 
+     * @return : all the metadata.
+     * @throws ParseException : occurs if an error occurs during the parsing.
+     */
+    public Element[] getMetadata() throws ParseException {
+        Element[] comp = m_elements[0].getElements("Component");
+        Element[] compo = m_elements[0].getElements("Composite");
+        Element[] conf = m_elements[0].getElements("Instance");
+        Element[] metadata = new Element[comp.length + conf.length + compo.length];
+        int l = 0;
+        for (int i = 0; i < comp.length; i++) {
+            metadata[l] = comp[i];
+            l++;
+        }
+        for (int i = 0; i < compo.length; i++) {
+            metadata[l] = compo[i];
+            l++;
+        }
+        for (int i = 0; i < conf.length; i++) {
+            metadata[l] = conf[i];
+            l++;
+        }
+        return metadata;
+    }
+
+
+    /**
+     * Characters.
+     * @param ch : character
+     * @param start : start
+     * @param length : length
+     * @throws SAXException : can never occurs.
+     * @see org.xml.sax.ContentHandler#characters(char[], int, int)
+     */
+    public void characters(char[] ch, int start, int length) throws SAXException {
+        // NOTHING TO DO
+
+    }
+
+
+    /**
+     * End the document.
+     * @throws SAXException : can never occrus.
+     * @see org.xml.sax.ContentHandler#endDocument()
+     */
+    public void endDocument() throws SAXException {
+    }
+
+
+    /**
+     * End of an element.
+     * @param namespaceURI : element namespace
+     * @param localName : local name
+     * @param qName : qualified name
+     * @throws SAXException : occurs when the element is malformed
+     * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
+     */
+    public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
+        // Get the last element of the list
+        Element lastElement = removeLastElement();
+
+        // Check if the name is consitent with the name of this end tag
+        if (!lastElement.getName().equalsIgnoreCase(qName) && !lastElement.getNameSpace().equals(namespaceURI)) {
+            throw new SAXException("Parse error when ending an element : " + qName + " [" + namespaceURI + "]");
+        }
+
+        // The name is consitent
+        // Add this element last element with if it is not the root
+        if (m_elements.length != 0) {
+            Element newQueue = m_elements[m_elements.length - 1];
+            newQueue.addElement(lastElement);
+        } else {
+            // It is the last element
+            addElement(lastElement);
+        }
+
+    }
+
+    /**
+     * End prefix mapping.
+     * @param prefix : ended prefix
+     * @throws SAXException : can never occurs.
+     * @see org.xml.sax.ContentHandler#endPrefixMapping(java.lang.String)
+     */
+    public void endPrefixMapping(String prefix) throws SAXException {
+        // NOTHING TO DO
+    }
+
+
+    /**
+     * Ignore whitespace.
+     * @param ch : character
+     * @param start : start
+     * @param length : length
+     * @throws SAXException : can never occurs. 
+     * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
+     */
+    public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
+        // NOTHING TO DO
+    }
+
+    /**
+     * Process an instruction.
+     * @param target : target
+     * @param data : data
+     * @throws SAXException : can never occurs.
+     * @see org.xml.sax.ContentHandler#processingInstruction(java.lang.String, java.lang.String)
+     */
+    public void processingInstruction(String target, String data) throws SAXException {
+        // DO NOTHING
+    }
+
+    /**
+     * Set Document locator.
+     * @param locator : new locator.
+     * @see org.xml.sax.ContentHandler#setDocumentLocator(org.xml.sax.Locator)
+     */
+    public void setDocumentLocator(Locator locator) {
+        // NOTHING TO DO
+
+    }
+
+    /**
+     * Skipped entity.
+     * @param name : name.
+     * @throws SAXException : can never occurs.
+     * @see org.xml.sax.ContentHandler#skippedEntity(java.lang.String)
+     */
+    public void skippedEntity(String name) throws SAXException {
+        // NOTHING TO DO
+
+    }
+
+    /**
+     * Start a document.
+     * @throws SAXException : can never occurs.
+     * @see org.xml.sax.ContentHandler#startDocument()
+     */
+    public void startDocument() throws SAXException {
+    }
+
+
+    /**
+     * Start an element.
+     * @param namespaceURI : element namespace.
+     * @param localName : local element.
+     * @param qName : qualified name.
+     * @param atts : attribute
+     * @throws SAXException : occurs if the element cannot be parsed correctly.
+     * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
+     */
+    public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
+        Element elem = new Element(localName, namespaceURI);
+
+        for (int i = 0; i < atts.getLength(); i++) {
+            String name = (String) atts.getLocalName(i);
+            String ns = (String) atts.getURI(i);
+            String value = (String) atts.getValue(i);
+            Attribute att = new Attribute(name, ns, value);
+            elem.addAttribute(att);
+        }
+
+        addElement(elem);
+
+    }
+
+    /**
+     * Start a prefix mapping.
+     * @param prefix : prefix.
+     * @param uri : uri.
+     * @throws SAXException : can never occurs.
+     * @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String, java.lang.String)
+     */
+    public void startPrefixMapping(String prefix, String uri) throws SAXException {
+        // NOTHING TO DO
+
+    }
+
+    /**
+     * Add an element.
+     * @param elem : the element to add
+     */
+    private void addElement(Element elem) {
+        for (int i = 0; (m_elements != null) && (i < m_elements.length); i++) {
+            if (m_elements[i] == elem) {
+                return;
+            }
+        }
+
+        if (m_elements != null) {
+            Element[] newElementsList = new Element[m_elements.length + 1];
+            System.arraycopy(m_elements, 0, newElementsList, 0, m_elements.length);
+            newElementsList[m_elements.length] = elem;
+            m_elements = newElementsList;
+        } else {
+            m_elements = new Element[] { elem };
+        }
+    }
+
+    /**
+     * Remove an element.
+     * @return : the removed element.
+     */
+    private Element removeLastElement() {
+        int idx = -1;
+        idx = m_elements.length - 1;
+        Element last = m_elements[idx];
+        if (idx >= 0) {
+            if ((m_elements.length - 1) == 0) {
+                // It is the last element of the list;
+                m_elements = new Element[0];
+            } else {
+                // Remove the last element of the list :
+                Element[] newElementsList = new Element[m_elements.length - 1];
+                System.arraycopy(m_elements, 0, newElementsList, 0, idx);
+                m_elements = newElementsList;
+            }
+        }
+        return last;
+    }
+
+}

Added: felix/trunk/ipojo/manipulator/src/main/java/org/objectweb/asm/commons/AdviceAdapter.java
URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/manipulator/src/main/java/org/objectweb/asm/commons/AdviceAdapter.java?view=auto&rev=550247
==============================================================================
--- felix/trunk/ipojo/manipulator/src/main/java/org/objectweb/asm/commons/AdviceAdapter.java (added)
+++ felix/trunk/ipojo/manipulator/src/main/java/org/objectweb/asm/commons/AdviceAdapter.java Sun Jun 24 08:11:33 2007
@@ -0,0 +1,650 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.objectweb.asm.commons;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ * A {@link org.objectweb.asm.MethodAdapter} to insert before, after and around
+ * advices in methods and constructors. <p> The behavior for constructors is
+ * like this: <ol>
+ * 
+ * <li>as long as the INVOKESPECIAL for the object initialization has not been
+ * reached, every bytecode instruction is dispatched in the ctor code visitor</li>
+ * 
+ * <li>when this one is reached, it is only added in the ctor code visitor and
+ * a JP invoke is added</li>
+ * 
+ * <li>after that, only the other code visitor receives the instructions</li>
+ * 
+ * </ol>
+ * 
+ * @author Eugene Kuleshov
+ * @author Eric Bruneton
+ */
+public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes
+{
+    private static final Object THIS = new Object();
+    private static final Object OTHER = new Object();
+
+    protected int methodAccess;
+    protected String methodDesc;
+
+    private boolean constructor;
+    private boolean superInitialized;
+    private ArrayList stackFrame;
+    private HashMap branches;
+
+    /**
+     * Creates a new {@link AdviceAdapter}.
+     * 
+     * @param mv the method visitor to which this adapter delegates calls.
+     * @param access the method's access flags (see {@link Opcodes}).
+     * @param name the method's name.
+     * @param desc the method's descriptor (see {@link Type Type}).
+     */
+    public AdviceAdapter(
+        final MethodVisitor mv,
+        final int access,
+        final String name,
+        final String desc)
+    {
+        super(mv, access, name, desc);
+        methodAccess = access;
+        methodDesc = desc;
+
+        constructor = "<init>".equals(name);
+    }
+
+    public void visitCode() {
+        mv.visitCode();
+        if (!constructor) {
+            superInitialized = true;
+            onMethodEnter();
+        } else {
+            stackFrame = new ArrayList();
+            branches = new HashMap();
+        }
+    }
+
+    public void visitLabel(final Label label) {
+        mv.visitLabel(label);
+
+        if (constructor && branches != null) {
+            ArrayList frame = (ArrayList) branches.get(label);
+            if (frame != null) {
+                stackFrame = frame;
+                branches.remove(label);
+            }
+        }
+    }
+
+    public void visitInsn(final int opcode) {
+        if (constructor) {
+            switch (opcode) {
+                case RETURN: // empty stack
+                    onMethodExit(opcode);
+                    break;
+
+                case IRETURN: // 1 before n/a after
+                case FRETURN: // 1 before n/a after
+                case ARETURN: // 1 before n/a after
+                case ATHROW: // 1 before n/a after
+                    popValue();
+                    popValue();
+                    onMethodExit(opcode);
+                    break;
+
+                case LRETURN: // 2 before n/a after
+                case DRETURN: // 2 before n/a after
+                    popValue();
+                    popValue();
+                    onMethodExit(opcode);
+                    break;
+
+                case NOP:
+                case LALOAD: // remove 2 add 2
+                case DALOAD: // remove 2 add 2
+                case LNEG:
+                case DNEG:
+                case FNEG:
+                case INEG:
+                case L2D:
+                case D2L:
+                case F2I:
+                case I2B:
+                case I2C:
+                case I2S:
+                case I2F:
+                case Opcodes.ARRAYLENGTH:
+                    break;
+
+                case ACONST_NULL:
+                case ICONST_M1:
+                case ICONST_0:
+                case ICONST_1:
+                case ICONST_2:
+                case ICONST_3:
+                case ICONST_4:
+                case ICONST_5:
+                case FCONST_0:
+                case FCONST_1:
+                case FCONST_2:
+                case F2L: // 1 before 2 after
+                case F2D:
+                case I2L:
+                case I2D:
+                    pushValue(OTHER);
+                    break;
+
+                case LCONST_0:
+                case LCONST_1:
+                case DCONST_0:
+                case DCONST_1:
+                    pushValue(OTHER);
+                    pushValue(OTHER);
+                    break;
+
+                case IALOAD: // remove 2 add 1
+                case FALOAD: // remove 2 add 1
+                case AALOAD: // remove 2 add 1
+                case BALOAD: // remove 2 add 1
+                case CALOAD: // remove 2 add 1
+                case SALOAD: // remove 2 add 1
+                case POP:
+                case IADD:
+                case FADD:
+                case ISUB:
+                case LSHL: // 3 before 2 after
+                case LSHR: // 3 before 2 after
+                case LUSHR: // 3 before 2 after
+                case L2I: // 2 before 1 after
+                case L2F: // 2 before 1 after
+                case D2I: // 2 before 1 after
+                case D2F: // 2 before 1 after
+                case FSUB:
+                case FMUL:
+                case FDIV:
+                case FREM:
+                case FCMPL: // 2 before 1 after
+                case FCMPG: // 2 before 1 after
+                case IMUL:
+                case IDIV:
+                case IREM:
+                case ISHL:
+                case ISHR:
+                case IUSHR:
+                case IAND:
+                case IOR:
+                case IXOR:
+                case MONITORENTER:
+                case MONITOREXIT:
+                    popValue();
+                    break;
+
+                case POP2:
+                case LSUB:
+                case LMUL:
+                case LDIV:
+                case LREM:
+                case LADD:
+                case LAND:
+                case LOR:
+                case LXOR:
+                case DADD:
+                case DMUL:
+                case DSUB:
+                case DDIV:
+                case DREM:
+                    popValue();
+                    popValue();
+                    break;
+
+                case IASTORE:
+                case FASTORE:
+                case AASTORE:
+                case BASTORE:
+                case CASTORE:
+                case SASTORE:
+                case LCMP: // 4 before 1 after
+                case DCMPL:
+                case DCMPG:
+                    popValue();
+                    popValue();
+                    popValue();
+                    break;
+
+                case LASTORE:
+                case DASTORE:
+                    popValue();
+                    popValue();
+                    popValue();
+                    popValue();
+                    break;
+
+                case DUP:
+                    pushValue(peekValue());
+                    break;
+
+                case DUP_X1:
+                    // TODO optimize this
+                {
+                    Object o1 = popValue();
+                    Object o2 = popValue();
+                    pushValue(o1);
+                    pushValue(o2);
+                    pushValue(o1);
+                }
+                    break;
+
+                case DUP_X2:
+                    // TODO optimize this
+                {
+                    Object o1 = popValue();
+                    Object o2 = popValue();
+                    Object o3 = popValue();
+                    pushValue(o1);
+                    pushValue(o3);
+                    pushValue(o2);
+                    pushValue(o1);
+                }
+                    break;
+
+                case DUP2:
+                    // TODO optimize this
+                {
+                    Object o1 = popValue();
+                    Object o2 = popValue();
+                    pushValue(o2);
+                    pushValue(o1);
+                    pushValue(o2);
+                    pushValue(o1);
+                }
+                    break;
+
+                case DUP2_X1:
+                    // TODO optimize this
+                {
+                    Object o1 = popValue();
+                    Object o2 = popValue();
+                    Object o3 = popValue();
+                    pushValue(o2);
+                    pushValue(o1);
+                    pushValue(o3);
+                    pushValue(o2);
+                    pushValue(o1);
+                }
+                    break;
+
+                case DUP2_X2:
+                    // TODO optimize this
+                {
+                    Object o1 = popValue();
+                    Object o2 = popValue();
+                    Object o3 = popValue();
+                    Object o4 = popValue();
+                    pushValue(o2);
+                    pushValue(o1);
+                    pushValue(o4);
+                    pushValue(o3);
+                    pushValue(o2);
+                    pushValue(o1);
+                }
+                    break;
+
+                case SWAP: {
+                    Object o1 = popValue();
+                    Object o2 = popValue();
+                    pushValue(o1);
+                    pushValue(o2);
+                }
+                    break;
+            }
+        } else {
+            switch (opcode) {
+                case RETURN:
+                case IRETURN:
+                case FRETURN:
+                case ARETURN:
+                case LRETURN:
+                case DRETURN:
+                case ATHROW:
+                    onMethodExit(opcode);
+                    break;
+            }
+        }
+        mv.visitInsn(opcode);
+    }
+
+    public void visitVarInsn(final int opcode, final int var) {
+        super.visitVarInsn(opcode, var);
+
+        if (constructor) {
+            switch (opcode) {
+                case ILOAD:
+                case FLOAD:
+                    pushValue(OTHER);
+                    break;
+                case LLOAD:
+                case DLOAD:
+                    pushValue(OTHER);
+                    pushValue(OTHER);
+                    break;
+                case ALOAD:
+                    pushValue(var == 0 ? THIS : OTHER);
+                    break;
+                case ASTORE:
+                case ISTORE:
+                case FSTORE:
+                    popValue();
+                    break;
+                case LSTORE:
+                case DSTORE:
+                    popValue();
+                    popValue();
+                    break;
+            }
+        }
+    }
+
+    public void visitFieldInsn(
+        final int opcode,
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        mv.visitFieldInsn(opcode, owner, name, desc);
+
+        if (constructor) {
+            char c = desc.charAt(0);
+            boolean longOrDouble = c == 'J' || c == 'D';
+            switch (opcode) {
+                case GETSTATIC:
+                    pushValue(OTHER);
+                    if (longOrDouble) {
+                        pushValue(OTHER);
+                    }
+                    break;
+                case PUTSTATIC:
+                    popValue();
+                    if (longOrDouble) {
+                        popValue();
+                    }
+                    break;
+                case PUTFIELD:
+                    popValue();
+                    if (longOrDouble) {
+                        popValue();
+                        popValue();
+                    }
+                    break;
+                // case GETFIELD:
+                default:
+                    if (longOrDouble) {
+                        pushValue(OTHER);
+                    }
+            }
+        }
+    }
+
+    public void visitIntInsn(final int opcode, final int operand) {
+        mv.visitIntInsn(opcode, operand);
+
+        if (constructor && opcode!=NEWARRAY) {
+            pushValue(OTHER);
+        }
+    }
+
+    public void visitLdcInsn(final Object cst) {
+        mv.visitLdcInsn(cst);
+
+        if (constructor) {
+            pushValue(OTHER);
+            if (cst instanceof Double || cst instanceof Long) {
+                pushValue(OTHER);
+            }
+        }
+    }
+
+    public void visitMultiANewArrayInsn(final String desc, final int dims) {
+        mv.visitMultiANewArrayInsn(desc, dims);
+
+        if (constructor) {
+            for (int i = 0; i < dims; i++) {
+                popValue();
+            }
+            pushValue(OTHER);
+        }
+    }
+
+    public void visitTypeInsn(final int opcode, final String name) {
+        mv.visitTypeInsn(opcode, name);
+
+        // ANEWARRAY, CHECKCAST or INSTANCEOF don't change stack
+        if (constructor && opcode == NEW) {
+            pushValue(OTHER);
+        }
+    }
+
+    public void visitMethodInsn(
+        final int opcode,
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        mv.visitMethodInsn(opcode, owner, name, desc);
+
+        if (constructor) {
+            Type[] types = Type.getArgumentTypes(desc);
+            for (int i = 0; i < types.length; i++) {
+                popValue();
+                if (types[i].getSize() == 2) {
+                    popValue();
+                }
+            }
+            switch (opcode) {
+                // case INVOKESTATIC:
+                // break;
+
+                case INVOKEINTERFACE:
+                case INVOKEVIRTUAL:
+                    popValue(); // objectref
+                    break;
+
+                case INVOKESPECIAL:
+                    Object type = popValue(); // objectref
+                    if (type == THIS && !superInitialized) {
+                        onMethodEnter();
+                        superInitialized = true;
+                        // once super has been initialized it is no longer
+                        // necessary to keep track of stack state
+                        constructor = false;
+                    }
+                    break;
+            }
+
+            Type returnType = Type.getReturnType(desc);
+            if (returnType != Type.VOID_TYPE) {
+                pushValue(OTHER);
+                if (returnType.getSize() == 2) {
+                    pushValue(OTHER);
+                }
+            }
+        }
+    }
+
+    public void visitJumpInsn(final int opcode, final Label label) {
+        mv.visitJumpInsn(opcode, label);
+
+        if (constructor) {
+            switch (opcode) {
+                case IFEQ:
+                case IFNE:
+                case IFLT:
+                case IFGE:
+                case IFGT:
+                case IFLE:
+                case IFNULL:
+                case IFNONNULL:
+                    popValue();
+                    break;
+
+                case IF_ICMPEQ:
+                case IF_ICMPNE:
+                case IF_ICMPLT:
+                case IF_ICMPGE:
+                case IF_ICMPGT:
+                case IF_ICMPLE:
+                case IF_ACMPEQ:
+                case IF_ACMPNE:
+                    popValue();
+                    popValue();
+                    break;
+
+                case JSR:
+                    pushValue(OTHER);
+                    break;
+            }
+            addBranch(label);
+        }
+    }
+
+    public void visitLookupSwitchInsn(
+        final Label dflt,
+        final int[] keys,
+        final Label[] labels)
+    {
+        mv.visitLookupSwitchInsn(dflt, keys, labels);
+
+        if (constructor) {
+            popValue();
+            addBranches(dflt, labels);
+        }
+    }
+
+    public void visitTableSwitchInsn(
+        final int min,
+        final int max,
+        final Label dflt,
+        final Label[] labels)
+    {
+        mv.visitTableSwitchInsn(min, max, dflt, labels);
+
+        if (constructor) {
+            popValue();
+            addBranches(dflt, labels);
+        }
+    }
+
+    private void addBranches(final Label dflt, final Label[] labels) {
+        addBranch(dflt);
+        for (int i = 0; i < labels.length; i++) {
+            addBranch(labels[i]);
+        }
+    }
+
+    private void addBranch(final Label label) {
+        if (branches.containsKey(label)) {
+            return;
+        }
+        ArrayList frame = new ArrayList();
+        frame.addAll(stackFrame);
+        branches.put(label, frame);
+    }
+
+    private Object popValue() {
+        return stackFrame.remove(stackFrame.size() - 1);
+    }
+
+    private Object peekValue() {
+        return stackFrame.get(stackFrame.size() - 1);
+    }
+
+    private void pushValue(final Object o) {
+        stackFrame.add(o);
+    }
+
+    /**
+     * Called at the beginning of the method or after super class class call in
+     * the constructor. <br><br>
+     * 
+     * <i>Custom code can use or change all the local variables, but should not
+     * change state of the stack.</i>
+     */
+    protected abstract void onMethodEnter();
+
+    /**
+     * Called before explicit exit from the method using either return or throw.
+     * Top element on the stack contains the return value or exception instance.
+     * For example:
+     * 
+     * <pre>
+     *   public void onMethodExit(int opcode) {
+     *     if(opcode==RETURN) {
+     *         visitInsn(ACONST_NULL);
+     *     } else if(opcode==ARETURN || opcode==ATHROW) {
+     *         dup();
+     *     } else {
+     *         if(opcode==LRETURN || opcode==DRETURN) {
+     *             dup2();
+     *         } else {
+     *             dup();
+     *         }
+     *         box(Type.getReturnType(this.methodDesc));
+     *     }
+     *     visitIntInsn(SIPUSH, opcode);
+     *     visitMethodInsn(INVOKESTATIC, owner, "onExit", "(Ljava/lang/Object;I)V");
+     *   }
+     *
+     *   // an actual call back method
+     *   public static void onExit(int opcode, Object param) {
+     *     ...
+     * </pre>
+     * 
+     * <br><br>
+     * 
+     * <i>Custom code can use or change all the local variables, but should not
+     * change state of the stack.</i>
+     * 
+     * @param opcode one of the RETURN, IRETURN, FRETURN, ARETURN, LRETURN,
+     *        DRETURN or ATHROW
+     * 
+     */
+    protected abstract void onMethodExit(int opcode);
+
+    // TODO onException, onMethodCall
+
+}