KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > fractal > adl > xml > DTDHandler


1 /***
2  * Fractal ADL Parser
3  * Copyright (C) 2002-2004 France Telecom R&D
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  *
19  * Contact: Eric.Bruneton@rd.francetelecom.com
20  *
21  * Author: Eric Bruneton
22  */

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 JavaDoc;
42 import java.io.IOException JavaDoc;
43 import java.io.InputStreamReader JavaDoc;
44 import java.util.Map JavaDoc;
45 import java.util.HashMap JavaDoc;
46 import java.util.Iterator JavaDoc;
47 import java.util.StringTokenizer JavaDoc;
48 import java.util.Set JavaDoc;
49 import java.util.HashSet JavaDoc;
50 import java.lang.reflect.Method JavaDoc;
51
52 import org.objectweb.asm.Type;
53 import org.xml.sax.SAXException JavaDoc;
54
55 class DTDHandler {
56
57   void checkDTD (final InputStream JavaDoc is, final XMLNodeClassLoader loader)
58     throws IOException JavaDoc, ClassNotFoundException JavaDoc, SAXException JavaDoc
59   {
60     // Associates DTD elements to AST node names
61
// Keys are AST node names, values are DTDElement objects
62
Map JavaDoc astElements = new HashMap JavaDoc();
63
64     DTD dtd = new DTDParser(new InputStreamReader JavaDoc(is)).parse();
65     Iterator JavaDoc i = dtd.items.iterator();
66     while (i.hasNext()) {
67       Object JavaDoc item = i.next();
68
69       if (item instanceof DTDProcessingInstruction) {
70         String JavaDoc pi = ((DTDProcessingInstruction)item).getText();
71
72         if (pi.startsWith("add")) {
73           Map JavaDoc args = checkProcessingInstruction(pi);
74           String JavaDoc ast = (String JavaDoc)args.get("ast");
75           String JavaDoc itf = (String JavaDoc)args.get("itf");
76
77           if (ast == null || itf == null) {
78             throw new SAXException JavaDoc(
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 JavaDoc args = checkProcessingInstruction(pi);
95           String JavaDoc xml = (String JavaDoc)args.get("xml");
96           String JavaDoc ast = (String JavaDoc)args.get("ast");
97
98           if (xml == null || ast == null) {
99             throw new SAXException JavaDoc(
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 JavaDoc(
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 JavaDoc xmlName = xmlElem.getName();
120         String JavaDoc astName = loader.getASTName(xmlName);
121         DTDElement astElem = (DTDElement)astElements.get(astName);
122         if (astElem == null) {
123           throw new SAXException JavaDoc(
124             "Invalid DTD : no AST node defined for element '" + xmlName + "'");
125         }
126         checkDTDItem(xmlElem.getContent(), new HashSet JavaDoc());
127         checkDTDItem(astElem.getContent(), new HashSet JavaDoc());
128         Arities xmlArities = getArities(xmlElem.getContent(), null);
129         Arities astArities = getArities(astElem.getContent(), null);
130
131         Iterator JavaDoc x = xmlArities.keySet().iterator();
132         while (x.hasNext()) {
133           String JavaDoc xmlSubNode = (String JavaDoc)x.next();
134           String JavaDoc 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 JavaDoc(
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 JavaDoc(
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 JavaDoc a = astArities.keySet().iterator();
153         while (a.hasNext()) {
154           String JavaDoc astSubNode = (String JavaDoc)a.next();
155           String JavaDoc xmlSubNode = loader.getXMLElement(astSubNode);
156           Arity xmlSubNodeArity = (Arity)xmlArities.get(xmlSubNode);
157           if (xmlSubNodeArity == null) {
158             throw new SAXException JavaDoc(
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 JavaDoc xmlAttr = (String JavaDoc)x.next();
168           String JavaDoc astAttr = loader.getASTAttribute(xmlName, xmlAttr);
169           if (astElem.getAttribute(astAttr) == null) {
170             throw new SAXException JavaDoc(
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 JavaDoc astAttr = (String JavaDoc)a.next();
179           String JavaDoc xmlAttr = loader.getXMLAttribute(astName, astAttr);
180           if (xmlElem.getAttribute(xmlAttr) == null) {
181             throw new SAXException JavaDoc(
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 JavaDoc checkProcessingInstruction (final String JavaDoc pi) throws SAXException JavaDoc {
191     Map JavaDoc m = new HashMap JavaDoc();
192     StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(pi);
193     if (st.hasMoreTokens()) {
194       st.nextToken();
195     } else {
196       throw new SAXException JavaDoc("Invalid processing instruction: " + pi);
197     }
198     while (st.hasMoreTokens()) {
199       String JavaDoc t = st.nextToken();
200       int p = t.indexOf("=\"");
201       if (p == -1 || !t.endsWith("\"")) {
202         throw new SAXException JavaDoc("Invalid processing instruction: " + pi);
203       }
204       String JavaDoc key = t.substring(0, p);
205       String JavaDoc 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 JavaDoc c, final DTDElement elt) {
212     Set JavaDoc meths = new HashSet JavaDoc();
213     Method JavaDoc[] methods = c.getMethods();
214     for (int i = 0; i < methods.length; i++) {
215       Method JavaDoc method = methods[i];
216       meths.add(method.getName() + Type.getMethodDescriptor(method));
217     }
218     Iterator JavaDoc i = meths.iterator();
219     while (i.hasNext()) {
220       String JavaDoc meth = (String JavaDoc)i.next();
221       if (meth.startsWith("get") && meth.endsWith("()Ljava/lang/String;")) {
222         String JavaDoc 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 JavaDoc subElt = meth.substring(3, meth.indexOf('('));
229         String JavaDoc 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 JavaDoc addMeth = "add" + subElt + "(" + subEltDesc + ")V";
236             String JavaDoc 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 JavaDoc 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 JavaDoc name = ((DTDName)item).getValue();
272       if (names.contains(name)) {
273         throw new RuntimeException JavaDoc(
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 JavaDoc("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 JavaDoc {
304
305     public void add (final Arities arities) {
306       Iterator JavaDoc i = arities.keySet().iterator();
307       while (i.hasNext()) {
308         String JavaDoc item = (String JavaDoc)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 JavaDoc i = arities.keySet().iterator();
317       while (i.hasNext()) {
318         String JavaDoc item = (String JavaDoc)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 JavaDoc i = arities.keySet().iterator();
327       while (i.hasNext()) {
328         String JavaDoc item = (String JavaDoc)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 JavaDoc 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 /*if (c == DTDCardinal.ONEMANY)*/ {
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