1 23 24 package org.objectweb.fractal.adl.xml; 25 26 import org.objectweb.fractal.adl.xml.XMLNodeClassLoader; 27 28 import com.wutka.dtd.DTD; 29 import com.wutka.dtd.DTDParser; 30 import com.wutka.dtd.DTDProcessingInstruction; 31 import com.wutka.dtd.DTDElement; 32 import com.wutka.dtd.DTDSequence; 33 import com.wutka.dtd.DTDAttribute; 34 import com.wutka.dtd.DTDName; 35 import com.wutka.dtd.DTDCardinal; 36 import com.wutka.dtd.DTDItem; 37 import com.wutka.dtd.DTDChoice; 38 import com.wutka.dtd.DTDMixed; 39 import com.wutka.dtd.DTDContainer; 40 41 import java.io.InputStream ; 42 import java.io.IOException ; 43 import java.io.InputStreamReader ; 44 import java.util.Map ; 45 import java.util.HashMap ; 46 import java.util.Iterator ; 47 import java.util.StringTokenizer ; 48 import java.util.Set ; 49 import java.util.HashSet ; 50 import java.lang.reflect.Method ; 51 52 import org.objectweb.asm.Type; 53 import org.xml.sax.SAXException ; 54 55 class DTDHandler { 56 57 void checkDTD (final InputStream is, final XMLNodeClassLoader loader) 58 throws IOException , ClassNotFoundException , SAXException 59 { 60 Map astElements = new HashMap (); 63 64 DTD dtd = new DTDParser(new InputStreamReader (is)).parse(); 65 Iterator i = dtd.items.iterator(); 66 while (i.hasNext()) { 67 Object item = i.next(); 68 69 if (item instanceof DTDProcessingInstruction) { 70 String pi = ((DTDProcessingInstruction)item).getText(); 71 72 if (pi.startsWith("add")) { 73 Map args = checkProcessingInstruction(pi); 74 String ast = (String )args.get("ast"); 75 String itf = (String )args.get("itf"); 76 77 if (ast == null || itf == null) { 78 throw new SAXException ( 79 "Invalid processing instruction " + 80 "('ast' and/or 'itf' argument missing): " + 81 pi); 82 } 83 84 DTDElement astElement = (DTDElement)astElements.get(ast); 85 if (astElement == null) { 86 astElement = new DTDElement(ast); 87 astElement.setContent(new DTDSequence()); 88 astElements.put(ast, astElement); 89 } 90 checkASTClass(loader.loadClass(itf), astElement); 91 92 loader.addASTNodeInterface(ast, itf); 93 } else if (pi.startsWith("map")) { 94 Map args = checkProcessingInstruction(pi); 95 String xml = (String )args.get("xml"); 96 String ast = (String )args.get("ast"); 97 98 if (xml == null || ast == null) { 99 throw new SAXException ( 100 "Invalid processing instruction " + 101 "('xml' and/or 'ast' argument missing): " + 102 pi); 103 } 104 105 if (xml.indexOf('.') == -1) { 106 loader.addASTNodeMapping(ast, xml); 107 } else { 108 if (ast.indexOf('.') == -1) { 109 throw new SAXException ( 110 "Invalid processing instruction " + 111 "(incompatible 'xml' and 'ast' arguments)" + 112 pi); 113 } 114 loader.addASTAttributeMapping(ast, xml); 115 } 116 } 117 } else if (item instanceof DTDElement) { 118 DTDElement xmlElem = (DTDElement)item; 119 String xmlName = xmlElem.getName(); 120 String astName = loader.getASTName(xmlName); 121 DTDElement astElem = (DTDElement)astElements.get(astName); 122 if (astElem == null) { 123 throw new SAXException ( 124 "Invalid DTD : no AST node defined for element '" + xmlName + "'"); 125 } 126 checkDTDItem(xmlElem.getContent(), new HashSet ()); 127 checkDTDItem(astElem.getContent(), new HashSet ()); 128 Arities xmlArities = getArities(xmlElem.getContent(), null); 129 Arities astArities = getArities(astElem.getContent(), null); 130 131 Iterator x = xmlArities.keySet().iterator(); 132 while (x.hasNext()) { 133 String xmlSubNode = (String )x.next(); 134 String astSubNode = loader.getASTName(xmlSubNode); 135 Arity xmlSubNodeArity = (Arity)xmlArities.get(xmlSubNode); 136 Arity astSubNodeArity = (Arity)astArities.get(astSubNode); 137 if (astSubNodeArity == null) { 138 throw new SAXException ( 139 "Invalid DTD : no AST node defined for sub element '" + 140 xmlSubNode + "' of element '" + xmlName + "'"); 141 } 142 if ((astSubNodeArity.max > 1 && xmlSubNodeArity.max <= 1) || 143 (astSubNodeArity.max <= 1 && xmlSubNodeArity.max > 1)) 144 { 145 throw new SAXException ( 146 "Invalid DTD : arity of sub element '" + xmlSubNode + 147 "' of element '" + xmlName + "' incompatible with arity of AST" + 148 " sub node '" + astSubNode + "' of AST node '" + astName + "'"); 149 } 150 } 151 152 Iterator a = astArities.keySet().iterator(); 153 while (a.hasNext()) { 154 String astSubNode = (String )a.next(); 155 String xmlSubNode = loader.getXMLElement(astSubNode); 156 Arity xmlSubNodeArity = (Arity)xmlArities.get(xmlSubNode); 157 if (xmlSubNodeArity == null) { 158 throw new SAXException ( 159 "Invalid DTD : no sub element defined in '" + xmlName + 160 "' element for sub node '" + astSubNode + "' of AST node '" + 161 astName + "'"); 162 } 163 } 164 165 x = xmlElem.attributes.keySet().iterator(); 166 while (x.hasNext()) { 167 String xmlAttr = (String )x.next(); 168 String astAttr = loader.getASTAttribute(xmlName, xmlAttr); 169 if (astElem.getAttribute(astAttr) == null) { 170 throw new SAXException ( 171 "Invalid DTD : no AST attribute defined for XML attribute '" + 172 xmlAttr + "' of element '" + xmlName + "'"); 173 } 174 } 175 176 a = astElem.attributes.keySet().iterator(); 177 while (a.hasNext()) { 178 String astAttr = (String )a.next(); 179 String xmlAttr = loader.getXMLAttribute(astName, astAttr); 180 if (xmlElem.getAttribute(xmlAttr) == null) { 181 throw new SAXException ( 182 "Invalid DTD : no XML attribute defined in element '" + xmlName + 183 "' for attribute '" + astAttr + "' of AST node '" + astName + "'"); 184 } 185 } 186 } 187 } 188 } 189 190 private Map checkProcessingInstruction (final String pi) throws SAXException { 191 Map m = new HashMap (); 192 StringTokenizer st = new StringTokenizer (pi); 193 if (st.hasMoreTokens()) { 194 st.nextToken(); 195 } else { 196 throw new SAXException ("Invalid processing instruction: " + pi); 197 } 198 while (st.hasMoreTokens()) { 199 String t = st.nextToken(); 200 int p = t.indexOf("=\""); 201 if (p == -1 || !t.endsWith("\"")) { 202 throw new SAXException ("Invalid processing instruction: " + pi); 203 } 204 String key = t.substring(0, p); 205 String value = t.substring(p + 2, t.length() - 1); 206 m.put(key, value); 207 } 208 return m; 209 } 210 211 private void checkASTClass (final Class c, final DTDElement elt) { 212 Set meths = new HashSet (); 213 Method [] methods = c.getMethods(); 214 for (int i = 0; i < methods.length; i++) { 215 Method method = methods[i]; 216 meths.add(method.getName() + Type.getMethodDescriptor(method)); 217 } 218 Iterator i = meths.iterator(); 219 while (i.hasNext()) { 220 String meth = (String )i.next(); 221 if (meth.startsWith("get") && meth.endsWith("()Ljava/lang/String;")) { 222 String attr = meth.substring(3, meth.indexOf('(')); 223 if (meths.contains("set" + attr + "(Ljava/lang/String;)V")) { 224 attr = Character.toLowerCase(attr.charAt(0)) + attr.substring(1); 225 elt.setAttribute(attr, new DTDAttribute(attr)); 226 } 227 } else if (meth.startsWith("get") && meth.indexOf("()") != -1) { 228 String subElt = meth.substring(3, meth.indexOf('(')); 229 String subEltDesc = meth.substring(meth.indexOf(')') + 1); 230 DTDName dtdName = null; 231 if (subEltDesc.charAt(0) == '[') { 232 if (subElt.endsWith("s")) { 233 subElt = subElt.substring(0, subElt.length() - 1); 234 subEltDesc = subEltDesc.substring(1); 235 String addMeth = "add" + subElt + "(" + subEltDesc + ")V"; 236 String removeMeth = "remove" + subElt + "(" + subEltDesc + ")V"; 237 if (meths.contains(addMeth) && meths.contains(removeMeth)) { 238 subElt = Character.toLowerCase(subElt.charAt(0)) + subElt.substring(1); 239 dtdName = new DTDName(subElt); 240 dtdName.setCardinal(DTDCardinal.ZEROMANY); 241 } 242 } 243 } else if (meths.contains("set" + subElt + "(" + subEltDesc + ")V")) { 244 subElt = Character.toLowerCase(subElt.charAt(0)) + subElt.substring(1); 245 dtdName = new DTDName(subElt); 246 } 247 if (dtdName != null) { 248 boolean add = true; 249 DTDItem[] items = ((DTDSequence)elt.getContent()).getItems(); 250 for (int j = 0; j < items.length; ++j) { 251 if (((DTDName)items[j]).getValue().equals(dtdName.getValue())) { 252 add = false; 253 break; 254 } 255 } 256 if (add) { 257 ((DTDSequence)elt.getContent()).add(dtdName); 258 } 259 } 260 } 261 } 262 } 263 264 private void checkDTDItem (final DTDItem item, final Set names) { 265 if (item instanceof DTDContainer) { 266 DTDItem[] items = ((DTDContainer)item).getItems(); 267 for (int i = 0; i < items.length; i++) { 268 checkDTDItem(items[i], names); 269 } 270 } else if (item instanceof DTDName) { 271 String name = ((DTDName)item).getValue(); 272 if (names.contains(name)) { 273 throw new RuntimeException ( 274 "Regular expressions with several occurences " + 275 "of the same sub element name are not supported"); 276 } else { 277 names.add(name); 278 } 279 } 280 } 281 282 private Arities getArities (final DTDItem item, final Arity arity) { 283 Arities result = new Arities(); 284 Arity a = arity == null ? new Arity(item) : arity.mult(new Arity(item)); 285 if (item instanceof DTDChoice) { 286 DTDItem[] items = ((DTDChoice)item).getItems(); 287 for (int j = 0; j < items.length; j++) { 288 result.union(getArities(items[j], a)); 289 } 290 } else if (item instanceof DTDSequence) { 291 DTDItem[] items = ((DTDSequence)item).getItems(); 292 for (int j = 0; j < items.length; j++) { 293 result.add(getArities(items[j], a)); 294 } 295 } else if (item instanceof DTDMixed) { 296 throw new RuntimeException ("Mixed content not supported"); 297 } else if (item instanceof DTDName) { 298 result.put(((DTDName)item).getValue(), a); 299 } 300 return result; 301 } 302 303 static class Arities extends HashMap { 304 305 public void add (final Arities arities) { 306 Iterator i = arities.keySet().iterator(); 307 while (i.hasNext()) { 308 String item = (String )i.next(); 309 Arity a = getArity(item); 310 Arity b = arities.getArity(item); 311 put(item, a.add(b)); 312 } 313 } 314 315 public void mult (final Arities arities) { 316 Iterator i = arities.keySet().iterator(); 317 while (i.hasNext()) { 318 String item = (String )i.next(); 319 Arity a = getArity(item); 320 Arity b = arities.getArity(item); 321 put(item, a.mult(b)); 322 } 323 } 324 325 public void union (final Arities arities) { 326 Iterator i = arities.keySet().iterator(); 327 while (i.hasNext()) { 328 String item = (String )i.next(); 329 Arity a = getArity(item); 330 Arity b = arities.getArity(item); 331 put(item, a.union(b)); 332 } 333 } 334 335 private Arity getArity (final String item) { 336 Arity a = (Arity)get(item); 337 return a == null ? new Arity(0, 0) : a; 338 } 339 } 340 341 static class Arity { 342 343 final float min; 344 345 final float max; 346 347 public Arity (final float min, final float max) { 348 this.min = min; 349 this.max = max; 350 } 351 352 public Arity (final DTDItem i) { 353 DTDCardinal c = i.getCardinal(); 354 if (c == DTDCardinal.NONE) { 355 min = 1; 356 max = 1; 357 } else if (c == DTDCardinal.OPTIONAL) { 358 min = 0; 359 max = 1; 360 } else if (c == DTDCardinal.ZEROMANY) { 361 min = 0; 362 max = Float.POSITIVE_INFINITY; 363 } else { 364 min = 0; 365 max = Float.POSITIVE_INFINITY; 366 } 367 } 368 369 public Arity add (final Arity a) { 370 return new Arity(min + a.min, max + a.max); 371 } 372 373 public Arity mult (final Arity a) { 374 return new Arity(min * a.min, max * a.max); 375 } 376 377 public Arity union (final Arity a) { 378 return new Arity(Math.min(min, a.min), Math.max(max, a.max)); 379 } 380 } 381 } 382 | Popular Tags |