org.objectweb.fractal.mind.adl.xml.XMLNodeClassLoader Maven / Gradle / Ivy
/***
* 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