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
+
+}