All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.objectweb.fractal.mind.adl.xml.XMLNodeClassLoader Maven / Gradle / Ivy

There is a newer version: 0.1-beta-2
Show newest version
/***
 * Fractal ADL Parser
 * Copyright (C) 2002-2004 France Telecom R&D
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Contact: [email protected]
 *
 * Author: Eric Bruneton
 * Contributors: Ali Erdem Ozcan
 */

package org.objectweb.fractal.mind.adl.xml;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import org.objectweb.fractal.adl.NodeClassLoader;

/**
 * A {@link NodeClassLoader} that generates {@link XMLNode} node classes.
 */
class XMLNodeClassLoader extends NodeClassLoader {

  // --------------------------------------------------------------------------
  // AST node definitions
  // --------------------------------------------------------------------------

  /**
   * Stores the set of AST interfaces that each AST node must implement. Keys
   * are AST node names, values are sets of internal interface names.
   */
  private final Map> astItfs        = new HashMap>();

  // --------------------------------------------------------------------------
  // Mapping between AST names and XML names
  // --------------------------------------------------------------------------

  /**
   * Stores the AST node names corresponding to each XML element name. Keys are
   * XML element names, values are AST node names.
   */
  private final Map      astNodes       = new HashMap();

  /**
   * Stores the XML element names corresponding to each AST node name. Keys are
   * AST node names, values are XML element names.
   */
  private final Map      xmlElements    = new HashMap();

  /**
   * Stores the AST type names corresponding to each XML element name. Keys are
   * XML element names, values are AST type names.
   */
  private final Map      astTypes       = new HashMap();

  /**
   * Stored sets of XML element name corresponding to each AST node type. Keys
   * are AST node types, values are sets of AST node names.
   */
  private final Map> astTypeToNodes = new HashMap>();

  /**
   * Stores the AST attribute names corresponding to each XML attribute name.
   * Keys are XML node.attribute names, values are AST attribute names.
   */
  private final Map      astAttributes  = new HashMap();

  /**
   * Stores the XML attribute names corresponding to each AST attribute name.
   * Keys are AST node.attribute names, values are XML attribute names.
   */
  private final Map      xmlAttributes  = new HashMap();

  // --------------------------------------------------------------------------
  // Constructor
  // --------------------------------------------------------------------------

  /**
   * @see NodeClassLoader#NodeClassLoader(ClassLoader)
   */
  public XMLNodeClassLoader(final ClassLoader parent) {
    super(parent);
  }

  // --------------------------------------------------------------------------
  // Mapping initialization and accessor methods
  // --------------------------------------------------------------------------

  void addASTNodeInterface(final String astNode, final String itf) {
    Set s = astItfs.get(astNode);
    if (s == null) {
      s = new HashSet();
      astItfs.put(astNode, s);
    }
    s.add(itf.replace('.', '/'));
  }

  void addASTNodeMapping(final String astName, final String xmlName) {
    xmlElements.put(astName, xmlName);
    astNodes.put(xmlName, astName);
  }

  void addASTTypeMapping(final String astName, final String astType) {
    astTypes.put(astName, astType);
    Set astNames = astTypeToNodes.get(astType);
    if (astNames == null) {
      astNames = new HashSet();
      astNames.add(astType);
      astTypeToNodes.put(astType, astNames);
    }
    astNames.add(astName);
  }

  void addASTAttributeMapping(final String astName, final String xmlName) {
    xmlAttributes.put(astName, xmlName.substring(xmlName.indexOf('.') + 1));
    astAttributes.put(xmlName, astName.substring(astName.indexOf('.') + 1));
  }

  String getASTName(final String xmlElement) {
    final String astName = astNodes.get(xmlElement);
    return astName == null ? xmlElement : astName;
  }

  String getXMLElement(final String astNode) {
    final String xmlElement = xmlElements.get(astNode);
    return xmlElement == null ? astNode : xmlElement;
  }

  String getASTType(final String astNode) {
    final String astType = astTypes.get(astNode);
    return astType == null ? astNode : astType;
  }

  Set getASTNames(final String astNode) {
    Set astNames = astTypeToNodes.get(astNode);
    if (astNames != null) return astNames;

    astNames = new HashSet();
    astNames.add(astNode);
    return astNames;
  }

  String getASTAttribute(final String xmlNode, final String xmlAttr) {
    final String astAttribute = astAttributes.get(xmlNode + "." + xmlAttr);
    return astAttribute == null ? xmlAttr : astAttribute;
  }

  String getXMLAttribute(final String astNode, final String astAttr) {
    final String xmlAttribute = xmlAttributes.get(astNode + "." + astAttr);
    return xmlAttribute == null ? astAttr : xmlAttribute;
  }

  String getASTClassName(final String xmlElement) {
    String astNode = astNodes.get(xmlElement);
    if (astNode == null) {
      astNode = xmlElement;
    }
    return getASTClass(astNode);
  }

  // --------------------------------------------------------------------------
  // Overridden ClassLoader method
  // --------------------------------------------------------------------------

  @Override
  protected Class findClass(final String name) throws ClassNotFoundException {

    if (isASTClass(name)) {
      final byte[] b = generateClass(getASTNode(name));
      /*
       * DEBUG try { java.io.FileOutputStream fos = new
       * java.io.FileOutputStream(name + ".class"); fos.write(b); fos.close(); }
       * catch (Exception _) { }
       */
      return defineClass(name, b);
    }

    return super.findClass(name);
  }

  // --------------------------------------------------------------------------
  // Class generation methods
  // --------------------------------------------------------------------------

  private byte[] generateClass(final String astNodeName)
      throws ClassNotFoundException {
    final String owner = getASTClass(astNodeName).replace('.', '/');
    final String xmlNode = Type.getInternalName(XMLNode.class);
    // Must use the org.objectweb.fractal.adl.xml.XMLNode.class as well
    // since the abstracts methods whose implementations will be generated
    // make part of this super class.
    final String superXMLNode = Type
        .getInternalName(org.objectweb.fractal.adl.xml.XMLNode.class);
    final Set itfSet = astItfs.get(astNodeName);
    if (itfSet == null) {
      throw new ClassNotFoundException("Unable to load class " + astNodeName);
    }
    final String[] itfs = itfSet.toArray(new String[itfSet.size()]);

    final ClassWriter cw = generateClass(getASTClassName(astNodeName),
        getASTType(astNodeName), xmlNode, itfs);

    final MethodVisitor xsa = cw.visitMethod(ACC_PUBLIC, "xmlSetAttributes",
        "(Lorg/xml/sax/Attributes;)V", null, null);

    final MethodVisitor xan = cw.visitMethod(ACC_PUBLIC, "xmlAddNode",
        "(Ljava/lang/String;L" + superXMLNode + ";)V", null, null);
    final Label xanEnd = new Label();

    final Set methods = new HashSet();
    for (final String itf : itfs) {
      for (final Method meth : loadClass(itf.replace('/', '.')).getMethods()) {
        final String name = meth.getName();
        final String desc = Type.getMethodDescriptor(meth);

        if (methods.contains(name + desc)) {
          continue;
        }
        methods.add(name + desc);

        if (name.startsWith("get")) {
          final String field = getFieldName(name, 3);

          if (desc.startsWith("()[")) {
            final String astName = getASTName(name, false);
            final Set astNames = astTypeToNodes.get(astName);
            if (astNames != null) {
              for (final String subAstName : astNames) {
                generateAddNodesMethod(xan, owner, subAstName, field,
                    "Ljava/util/List;", xanEnd, true);
              }
            } else {
              generateAddNodesMethod(xan, owner, astName, field,
                  "Ljava/util/List;", xanEnd, true);
            }
          } else {
            // case of a getter method for a sub node with arity 1
            // or ?
            final String fieldDesc = desc.substring(2);

            if (fieldDesc.equals("Ljava/lang/String;")) {
              // case of an attribute:
              // generates code in "xmlSetAttributes"
              xsa.visitVarInsn(ALOAD, 0);
              xsa.visitVarInsn(ALOAD, 1);
              xsa.visitLdcInsn(getXMLAttribute(astNodeName, getASTName(name,
                  true)));
              xsa.visitMethodInsn(INVOKEINTERFACE, "org/xml/sax/Attributes",
                  "getValue", "(Ljava/lang/String;)Ljava/lang/String;");
              xsa.visitFieldInsn(PUTFIELD, owner, field, fieldDesc);
            } else {
              // case of a sub node:
              generateAddNodeMethod(xan, owner, name, field, fieldDesc, xanEnd,
                  true);
            }
          }
        }
      }
    }

    xsa.visitInsn(RETURN);
    xsa.visitMaxs(0, 0);

    xan.visitLabel(xanEnd);
    xan.visitInsn(RETURN);
    xan.visitMaxs(0, 0);

    return cw.toByteArray();
  }

  private void generateAddNodeMethod(final MethodVisitor cv,
      final String owner, final String name, final String field,
      final String fieldDesc, final Label end, final boolean add) {
    final String s = getASTName(name, true);
    final Label l = generateIf(cv, getXMLElement(s));
    cv.visitVarInsn(ALOAD, 0);
    if (add) {
      cv.visitVarInsn(ALOAD, 2);
      cv.visitTypeInsn(CHECKCAST, fieldDesc
          .substring(1, fieldDesc.length() - 1));
    } else {
      cv.visitInsn(ACONST_NULL);
    }
    cv.visitFieldInsn(PUTFIELD, owner, field, fieldDesc);
    cv.visitJumpInsn(GOTO, end);
    cv.visitLabel(l);
  }

  private void generateAddNodesMethod(final MethodVisitor cv,
      final String owner, final String name, final String field,
      final String fieldDesc, final Label end, final boolean add) {
    final Label l = generateIf(cv, getXMLElement(name));
    cv.visitVarInsn(ALOAD, 0);
    cv.visitFieldInsn(GETFIELD, owner, field, fieldDesc);
    cv.visitVarInsn(ALOAD, 2);
    cv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", add
        ? "add"
        : "remove", add ? "(Ljava/lang/Object;)Z" : "(Ljava/lang/Object;)Z");
    cv.visitInsn(POP);
    cv.visitJumpInsn(GOTO, end);
    cv.visitLabel(l);
  }

  private Label generateIf(final MethodVisitor cv, final String name) {
    final Label l = new Label();
    cv.visitVarInsn(ALOAD, 1);
    cv.visitLdcInsn(name);
    cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "equals",
        "(Ljava/lang/Object;)Z");
    cv.visitJumpInsn(IFEQ, l);
    return l;
  }

  // --------------------------------------------------------------------------
  // Naming rules
  // --------------------------------------------------------------------------

  private static final String PKG = "org.objectweb.fractal.adl.ast.xml.";

  private static String getASTClass(final String astNodeName) {
    return PKG + astNodeName + "Impl";
  }

  private static boolean isASTClass(final String name) {
    return name.startsWith(PKG) && name.endsWith("Impl");
  }

  private static String getASTNode(final String astClassName) {
    return astClassName.substring(PKG.length(), astClassName.length() - 4);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy