org.objectweb.fractal.mind.adl.xml.DTDHandler 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.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import org.objectweb.asm.Type;
import org.xml.sax.SAXException;
import com.wutka.dtd.DTD;
import com.wutka.dtd.DTDAttribute;
import com.wutka.dtd.DTDCardinal;
import com.wutka.dtd.DTDChoice;
import com.wutka.dtd.DTDContainer;
import com.wutka.dtd.DTDElement;
import com.wutka.dtd.DTDItem;
import com.wutka.dtd.DTDMixed;
import com.wutka.dtd.DTDName;
import com.wutka.dtd.DTDParser;
import com.wutka.dtd.DTDProcessingInstruction;
import com.wutka.dtd.DTDSequence;
class DTDHandler {
void checkDTD(final InputStream is, final XMLNodeClassLoader loader)
throws IOException, ClassNotFoundException, SAXException {
// Associates DTD elements to AST node names
// Keys are AST node names, values are DTDElement objects
final Map astElements = new HashMap();
final DTD dtd = new DTDParser(new InputStreamReader(is)).parse();
final Iterator> i = dtd.items.iterator();
while (i.hasNext()) {
final Object item = i.next();
if (item instanceof DTDProcessingInstruction) {
final String pi = ((DTDProcessingInstruction) item).getText();
if (pi.startsWith("add")) {
final Map args = checkProcessingInstruction(pi);
final String ast = args.get("ast");
final String itf = args.get("itf");
if (ast == null || itf == null) {
throw new SAXException("Invalid processing instruction "
+ "('ast' and/or 'itf' argument missing): " + pi);
}
DTDElement astElement = astElements.get(ast);
if (astElement == null) {
astElement = new DTDElement(ast);
astElement.setContent(new DTDSequence());
astElements.put(ast, astElement);
}
checkASTClass(loader.loadClass(itf), astElement);
loader.addASTNodeInterface(ast, itf);
} else if (pi.startsWith("map")) {
final Map args = checkProcessingInstruction(pi);
final String ast = args.get("ast");
final String xml = args.get("xml");
final String type = args.get("type");
if (ast == null || (xml == null && type == null)) {
throw new SAXException("Invalid processing instruction "
+ "('ast', 'xml' and/or 'type' argument missing): " + pi);
}
if (xml != null) {
if (xml.indexOf('.') == -1) {
loader.addASTNodeMapping(ast, xml);
} else {
if (ast.indexOf('.') == -1) {
throw new SAXException("Invalid processing instruction "
+ "(incompatible 'xml' and 'ast' arguments)" + pi);
}
loader.addASTAttributeMapping(ast, xml);
}
}
if (type != null) {
if (ast.indexOf('.') != -1) {
throw new SAXException("Invalid processing instruction "
+ "(incompatible 'type' and 'ast' arguments)" + pi);
}
loader.addASTTypeMapping(ast, type);
}
}
} else if (item instanceof DTDElement) {
final DTDElement xmlElem = (DTDElement) item;
final String xmlName = xmlElem.getName();
final String astName = loader.getASTName(xmlName);
final DTDElement astElem = astElements.get(astName);
if (astElem == null) {
throw new SAXException(
"Invalid DTD : no AST node defined for element '" + xmlName + "'");
}
checkDTDItem(xmlElem.getContent(), new HashSet());
checkDTDItem(astElem.getContent(), new HashSet());
final Arities xmlArities = getArities(xmlElem.getContent(), null);
final Arities astArities = getArities(astElem.getContent(), null);
Iterator> x = xmlArities.keySet().iterator();
while (x.hasNext()) {
final String xmlSubNode = (String) x.next();
final String astSubNode = loader.getASTType(loader
.getASTName(xmlSubNode));
final Arity xmlSubNodeArity = xmlArities.get(xmlSubNode);
final Arity astSubNodeArity = astArities.get(astSubNode);
if (astSubNodeArity == null) {
throw new SAXException(
"Invalid DTD : no AST node defined for sub element '"
+ xmlSubNode + "' of element '" + xmlName + "'");
}
if ((astSubNodeArity.max > 1 && xmlSubNodeArity.max <= 1)
|| (astSubNodeArity.max <= 1 && xmlSubNodeArity.max > 1)) {
throw new SAXException("Invalid DTD : arity of sub element '"
+ xmlSubNode + "' of element '" + xmlName
+ "' incompatible with arity of AST" + " sub node '"
+ astSubNode + "' of AST node '" + astName + "'");
}
}
Iterator> a = astArities.keySet().iterator();
while (a.hasNext()) {
final String astSubNode = (String) a.next();
final Set astSubNodeNames = loader.getASTNames(astSubNode);
boolean found = false;
for (final String astSubNodeName : astSubNodeNames) {
final String xmlSubNode = loader.getXMLElement(astSubNodeName);
final Arity xmlSubNodeArity = xmlArities.get(xmlSubNode);
if (xmlSubNodeArity != null) {
found = true;
break;
}
}
if (!found)
throw new SAXException("Invalid DTD : no sub element defined in '"
+ xmlName + "' element for sub node '" + astSubNode
+ "' of AST node '" + astName + "'");
}
x = xmlElem.attributes.keySet().iterator();
while (x.hasNext()) {
final String xmlAttr = (String) x.next();
final String astAttr = loader.getASTAttribute(xmlName, xmlAttr);
if (astElem.getAttribute(astAttr) == null) {
throw new SAXException(
"Invalid DTD : no AST attribute defined for XML attribute '"
+ xmlAttr + "' of element '" + xmlName + "'");
}
}
a = astElem.attributes.keySet().iterator();
while (a.hasNext()) {
final String astAttr = (String) a.next();
final String xmlAttr = loader.getXMLAttribute(astName, astAttr);
if (xmlElem.getAttribute(xmlAttr) == null) {
throw new SAXException(
"Invalid DTD : no XML attribute defined in element '" + xmlName
+ "' for attribute '" + astAttr + "' of AST node '"
+ astName + "'");
}
}
}
}
}
private Map checkProcessingInstruction(final String pi)
throws SAXException {
final Map m = new HashMap();
final StringTokenizer st = new StringTokenizer(pi);
if (st.hasMoreTokens()) {
st.nextToken();
} else {
throw new SAXException("Invalid processing instruction: " + pi);
}
while (st.hasMoreTokens()) {
final String t = st.nextToken();
final int p = t.indexOf("=\"");
if (p == -1 || !t.endsWith("\"")) {
throw new SAXException("Invalid processing instruction: " + pi);
}
final String key = t.substring(0, p);
final String value = t.substring(p + 2, t.length() - 1);
m.put(key, value);
}
return m;
}
private void checkASTClass(final Class> c, final DTDElement elt) {
final Set meths = new HashSet();
final Method[] methods = c.getMethods();
for (final Method method : methods) {
meths.add(method.getName() + Type.getMethodDescriptor(method));
}
final Iterator> i = meths.iterator();
while (i.hasNext()) {
final String meth = (String) i.next();
if (meth.startsWith("get") && meth.endsWith("()Ljava/lang/String;")) {
String attr = meth.substring(3, meth.indexOf('('));
if (meths.contains("set" + attr + "(Ljava/lang/String;)V")) {
attr = Character.toLowerCase(attr.charAt(0)) + attr.substring(1);
elt.setAttribute(attr, new DTDAttribute(attr));
}
} else if (meth.startsWith("get") && meth.indexOf("()") != -1) {
String subElt = meth.substring(3, meth.indexOf('('));
String subEltDesc = meth.substring(meth.indexOf(')') + 1);
DTDName dtdName = null;
if (subEltDesc.charAt(0) == '[') {
if (subElt.endsWith("s")) {
subElt = subElt.substring(0, subElt.length() - 1);
subEltDesc = subEltDesc.substring(1);
final String addMeth = "add" + subElt + "(" + subEltDesc + ")V";
final String removeMeth = "remove" + subElt + "(" + subEltDesc
+ ")V";
if (meths.contains(addMeth) && meths.contains(removeMeth)) {
subElt = Character.toLowerCase(subElt.charAt(0))
+ subElt.substring(1);
dtdName = new DTDName(subElt);
dtdName.setCardinal(DTDCardinal.ZEROMANY);
}
}
} else if (meths.contains("set" + subElt + "(" + subEltDesc + ")V")) {
subElt = Character.toLowerCase(subElt.charAt(0))
+ subElt.substring(1);
dtdName = new DTDName(subElt);
}
if (dtdName != null) {
boolean add = true;
final DTDItem[] items = ((DTDSequence) elt.getContent()).getItems();
for (int j = 0; j < items.length; ++j) {
if (((DTDName) items[j]).getValue().equals(dtdName.getValue())) {
add = false;
break;
}
}
if (add) {
((DTDSequence) elt.getContent()).add(dtdName);
}
}
}
}
}
private void checkDTDItem(final DTDItem item, final Set names) {
if (item instanceof DTDContainer) {
final DTDItem[] items = ((DTDContainer) item).getItems();
for (final DTDItem element : items) {
checkDTDItem(element, names);
}
} else if (item instanceof DTDName) {
final String name = ((DTDName) item).getValue();
if (names.contains(name)) {
throw new RuntimeException(
"Regular expressions with several occurences "
+ "of the same sub element name are not supported");
}
names.add(name);
}
}
private Arities getArities(final DTDItem item, final Arity arity) {
final Arities result = new Arities();
final Arity a = arity == null ? new Arity(item) : arity
.mult(new Arity(item));
if (item instanceof DTDChoice) {
for (final DTDItem subItem : ((DTDChoice) item).getItems()) {
result.union(getArities(subItem, a));
}
} else if (item instanceof DTDSequence) {
for (final DTDItem subItem : ((DTDSequence) item).getItems()) {
result.add(getArities(subItem, a));
}
} else if (item instanceof DTDMixed) {
for (final DTDItem subItem : ((DTDMixed) item).getItems()) {
result.union(getArities(subItem, a));
}
} else if (item instanceof DTDName) {
result.put(((DTDName) item).getValue(), a);
}
return result;
}
static class Arities extends HashMap {
void add(final Arities arities) {
final Iterator> i = arities.keySet().iterator();
while (i.hasNext()) {
final String item = (String) i.next();
final Arity a = getArity(item);
final Arity b = arities.getArity(item);
put(item, a.add(b));
}
}
void mult(final Arities arities) {
final Iterator> i = arities.keySet().iterator();
while (i.hasNext()) {
final String item = (String) i.next();
final Arity a = getArity(item);
final Arity b = arities.getArity(item);
put(item, a.mult(b));
}
}
void union(final Arities arities) {
final Iterator> i = arities.keySet().iterator();
while (i.hasNext()) {
final String item = (String) i.next();
final Arity a = getArity(item);
final Arity b = arities.getArity(item);
put(item, a.union(b));
}
}
private Arity getArity(final String item) {
final Arity a = get(item);
return a == null ? new Arity(0, 0) : a;
}
}
static class Arity {
final float min;
final float max;
Arity(final float min, final float max) {
this.min = min;
this.max = max;
}
Arity(final DTDItem i) {
final DTDCardinal c = i.getCardinal();
if (c == DTDCardinal.NONE) {
min = 1;
max = 1;
} else if (c == DTDCardinal.OPTIONAL) {
min = 0;
max = 1;
} else if (c == DTDCardinal.ZEROMANY) {
min = 0;
max = Float.POSITIVE_INFINITY;
} else { // if (c == DTDCardinal.ONEMANY)
min = 0;
max = Float.POSITIVE_INFINITY;
}
}
Arity add(final Arity a) {
return new Arity(min + a.min, max + a.max);
}
Arity mult(final Arity a) {
return new Arity(min * a.min, max * a.max);
}
Arity union(final Arity a) {
return new Arity(Math.min(min, a.min), Math.max(max, a.max));
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy