KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openlaszlo > xml > internal > XMLRPCCompiler


1 /* ****************************************************************************
2  * XMLRPCCompiler.java
3  * ****************************************************************************/

4
5 /* J_LZ_COPYRIGHT_BEGIN *******************************************************
6 * Copyright 2001-2004 Laszlo Systems, Inc. All Rights Reserved. *
7 * Use is subject to license terms. *
8 * J_LZ_COPYRIGHT_END *********************************************************/

9
10 package org.openlaszlo.xml.internal;
11
12 import java.io.*;
13 import java.util.*;
14 import org.w3c.dom.*;
15 import org.xml.sax.*;
16 import javax.xml.parsers.*;
17 import org.openlaszlo.iv.flash.util.*;
18 import org.openlaszlo.iv.flash.api.action.*;
19 import org.openlaszlo.iv.flash.api.*;
20 import org.openlaszlo.utils.ChainedException;
21 import org.openlaszlo.utils.FileUtils;
22 import org.apache.log4j.*;
23
24 /**
25  * Use XMLRPCCompiler.compile().
26  */

27 public class XMLRPCCompiler
28 {
29     public static Logger mLogger = Logger.getLogger(XMLRPCCompiler.class);
30
31     private static DocumentBuilderFactory factory = null;
32
33     private FlashBuffer body;
34     private Program program;
35
36     /**
37      * Get document builder factory.
38      */

39     static DocumentBuilderFactory getDocumentBuilderFactory() {
40         if (factory == null) {
41             try {
42                 factory = DocumentBuilderFactory.newInstance();
43             } catch (FactoryConfigurationError e) {
44                 throw new RuntimeException JavaDoc(e.getMessage());
45             }
46         }
47         return factory;
48     }
49
50
51     /**
52      * Get the list of first-level children based on tag name.
53      * @param parent element to retrieve first-level children based on tag name.
54      * @param tag tag based to search children.
55      */

56     static List getElementsByTagName(Element parent, String JavaDoc tag)
57     {
58         List list = new Vector();
59         NodeList children = parent.getChildNodes();
60
61         for (int i=0; i < children.getLength(); i++) {
62             Node node = children.item(i);
63             if (node.getNodeType() == Node.ELEMENT_NODE) {
64                 list.add((Element)node);
65             }
66         }
67         return list;
68     }
69
70     /**
71      * Skips through comments and spaces.
72      * @param parent an element.
73      * @param n the nth child element to get from parent (starting from 0).
74      */

75     static Element getChildElement(Element parent, int n) {
76         NodeList children = parent.getChildNodes();
77         int count = 0;
78         for (int i=0; i < children.getLength(); i++) {
79             Node node = children.item(i);
80             if (node.getNodeType() == Node.ELEMENT_NODE) {
81                 if (n == count++) {
82                     mLogger.debug("found " + node.getNodeName());
83                     return (Element)node;
84                 }
85             }
86         }
87         return null;
88     }
89
90     /**
91      * Get text string of an element.
92      * @param parent element to get text from.
93      */

94     static String JavaDoc getFirstChildTextString(Element parent) {
95         Node text = parent.getFirstChild();
96         return text != null && text.getNodeType() == Node.TEXT_NODE
97             ? ( (Text)text ).getData() : "";
98     }
99
100     /**
101      * Write XMLRPC string response to SWF.
102      * @param string XMLRPC string to push.
103      */

104     void writeString(String JavaDoc string)
105         throws IOException {
106         mLogger.debug("writeString");
107         program.push(string);
108     }
109
110     /**
111      * Write XMLRPC integer response to SWF.
112      * @param intval XMLRPC integer string response to parse and push.
113      */

114     void writeInteger(String JavaDoc intval)
115         throws IOException {
116         mLogger.debug("writeInteger");
117         try {
118             program.push(Integer.parseInt(intval));
119         } catch (NumberFormatException JavaDoc e) {
120             throw new IOException(e.getMessage());
121         }
122     }
123
124     /**
125      * Write XMLRPC double response to SWF.
126      * @param doubleval XMLRPC 'double' string response to parse to float and
127      * push.
128      */

129     void writeDouble(String JavaDoc doubleval)
130         throws IOException {
131         mLogger.debug("writeDouble");
132         try {
133             // Program doesn't have a push(double).
134
// This code taken from Program.push( Object data) in jgenerator-2.2.
135
body.writeByte(Actions.PushData);
136             body.writeWord(8+1);
137             body.writeByte(6);
138             long dbits = Double.doubleToLongBits(Double.parseDouble(doubleval));
139             body.writeDWord((int)(dbits>>>32));
140             body.writeDWord((int)(dbits&0xffffffffL));
141         } catch (NumberFormatException JavaDoc e) {
142             throw new IOException(e.getMessage());
143         }
144     }
145
146     /**
147      * Snarfed from Program.push(Object o) in jgenerator-2.2.
148      */

149     void pushBoolean(boolean b)
150     {
151         body.writeByte(Actions.PushData);
152         body.writeWord(1+1);
153         body.writeByte(5);
154         body.writeByte(b?1:0);
155     }
156
157     /**
158      * Write XMLRPC boolean response to SWF.
159      * @param boleanval XMLRPC boolean string response to parse and push.
160      */

161     void writeBoolean(String JavaDoc booleanval)
162         throws IOException {
163         mLogger.debug("writeBoolean");
164         try {
165             pushBoolean(Integer.parseInt(booleanval) != 0);
166         } catch (NumberFormatException JavaDoc e) {
167             if (booleanval.equals("false"))
168                 pushBoolean(false);
169             else if (booleanval.equals("true"))
170                 pushBoolean(true);
171             else
172                 throw new IOException("not a boolean value");
173         }
174     }
175
176     /**
177      * Write XMLRPC array response to SWF.
178      * @param array XMLRPC array element to parse and push.
179      */

180     void writeArray(Element array)
181         throws IOException {
182         mLogger.debug("writeArray");
183         List data = getElementsByTagName(array, "data");
184         if (data.size() != 1)
185             throw new IOException("Invalid number of data elements in array");
186
187         List values = getElementsByTagName((Element)data.get(0), "value");
188         for (int i = values.size()-1; i >= 0; --i) {
189             writeValue((Element)values.get(i));
190         }
191         // Now push the array length and initialize it
192
program.push(values.size());
193         body.writeByte(Actions.InitArray);
194     }
195
196     /**
197      * Write XMLRPC struct response to SWF.
198      * @param struct XMLRPC struct element to parse and push.
199      */

200     void writeStruct(Element struct)
201         throws IOException {
202         mLogger.debug("writeStruct");
203         List members = getElementsByTagName(struct, "member");
204         // Push member values in reverse order
205
for (int i = members.size()-1; i >= 0; --i) {
206             writeMember((Element)members.get(i));
207         }
208         // Now push length of hash values and initialize it
209
program.push(members.size());
210         body.writeByte(Actions.InitObject);
211     }
212
213     /**
214      * Write XMLRPC struct member to SWF. Helper for writeStruct.
215      * @param member XMLRPC struct member element to parse and push.
216      */

217     void writeMember(Element member)
218         throws IOException {
219         mLogger.debug("writeMember");
220         Element name = getChildElement(member, 0);
221         Element value = getChildElement(member, 1);
222         if (name == null || value == null)
223             throw new IOException("Name or value appears to be null.");
224         if (! name.getNodeName().equals("name"))
225             throw new IOException("Name does not appear to be the first argument in member");
226         if (! value.getNodeName().equals("value"))
227             throw new IOException("Value does not appear to be the second argument in member");
228
229         // when initing hash, value are popped before names
230
program.push(getFirstChildTextString(name));
231         writeValue(value);
232     }
233
234     /**
235      * Unimplemented.
236      */

237     void writeDateTime(Element datetimeval)
238         throws RuntimeException JavaDoc {
239         mLogger.debug("writeDateTime");
240         throw new RuntimeException JavaDoc("datetime.iso8601 unimplemented");
241     }
242
243     /**
244      * Unimplemented.
245      */

246     void writeBase64(Element base64val) {
247         mLogger.debug("writeBase64");
248         throw new RuntimeException JavaDoc("base64 unimplemented");
249     }
250
251
252     /**
253      * Write XMLRPC response to SWF.
254      * @param value
255      */

256     void writeValue(Element value)
257         throws IOException {
258         mLogger.debug("writeValue");
259
260         Element type = getChildElement(value, 0);
261         if (type == null) {
262             writeString(getFirstChildTextString(value));
263             return;
264         }
265
266         String JavaDoc t = type.getTagName();
267         if (t.equals("string")) {
268             writeString(getFirstChildTextString(type));
269         } else if (t.equals("int") || t.equals("i4")) {
270             writeInteger(getFirstChildTextString(type));
271         } else if (t.equals("double")) {
272             writeDouble(getFirstChildTextString(type));
273         } else if (t.equals("boolean")) {
274             writeBoolean(getFirstChildTextString(type));
275         } else if (t.equals("struct")) {
276             writeStruct(type);
277         } else if (t.equals("array")) {
278             writeArray(type);
279         } else if (t.equals("dateTime.iso8601")) {
280             writeDateTime(type);
281         } else if (t.equals("base64")) {
282             writeBase64(type);
283         }
284     }
285
286     /**
287      *
288      */

289     void writeParams(Element params)
290         throws IOException {
291         mLogger.debug("writeParams");
292
293         Element param = getChildElement(params, 0);
294         if (! param.getTagName().equals("param"))
295             throw new IOException("Invalid params body");
296
297         Element value = getChildElement(param, 0);
298         if (! value.getTagName().equals("value"))
299             throw new IOException("Invalid param body");
300
301         writeValue(value);
302     }
303
304     /**
305      * Write XMLRPC fault response struct to SWF.
306      * @param faul the fault element.
307      */

308     void writeFault(Element fault)
309         throws IOException {
310         mLogger.debug("writeFault");
311
312         Element value = getChildElement(fault, 0);
313         if (! value.getTagName().equals("value"))
314             throw new IOException("Invalid param body");
315
316         writeValue(value);
317     }
318
319     /**
320      *
321      */

322     void writeXMLRPCData(Element element)
323         throws IOException {
324         mLogger.debug("writeXMLRPCData");
325
326         if (! element.getTagName().equals("methodResponse"))
327             throw new IOException("Invalid XMLRPC response");
328
329         Element child = getChildElement(element, 0);
330         if (child != null) {
331             if (child.getTagName().equals("params")) {
332                 writeParams(child);
333                 return;
334             } else if (child.getTagName().equals("fault")) {
335                 writeFault(child);
336                 return;
337             }
338         }
339         // TODO: [2004-02-20 pkang] this should return actionscript for xmlrpc
340
// client lib
341
throw new IOException("bad XMLRPC message body");
342     }
343
344
345     /**
346      * Produces a JGenerator Flash Program containing executable SWF codes to
347      * build an XML datasource structure which represents this XML stream.
348      *
349      * @param element xml response document element
350      * @param xmlsize size of xml used to create initial flash buffer.
351      * @return Flash Program containing entire SWF with header
352      */

353     public Program makeProgram(Element element, int xmlsize)
354         throws IOException {
355         mLogger.debug("makeProgram");
356
357         // Room at the end of the buffer for maybe some callback code to the
358
// runtime to say we're done.
359
final int MISC = 4096;
360         // Allocate enough room to hold the data nodes and strings ; it should
361
// be < (input XML filesize * scalar)
362
body = new FlashBuffer((int) (Math.floor(xmlsize * 3) + MISC));
363         program = new Program(body);
364
365         writeXMLRPCData(element);
366
367         // call into the viewsystem
368
program.push("_parent");
369         program.getVar();
370         program.push(2);
371         program.push("_parent");
372         program.getVar();
373         program.push("loader");
374         body.writeByte(Actions.GetMember);
375         program.push("returnData");
376         program.callMethod();
377         program.pop();
378
379         return program;
380     }
381
382
383     /**
384      * Make SWF
385      *
386      * @param xpp parser which is pointing at XML data
387      * @return FlashFile containing entire SWF with header
388      */

389     public FlashFile makeSWF(Element element, int xmlsize, int swfversion)
390         throws IOException {
391         mLogger.debug("makeSWF");
392
393         // Create FlashFile object nd include action bytes
394
FlashFile file = FlashFile.newFlashFile();
395         Script s = new Script(1);
396         file.setMainScript(s);
397         file.setVersion(swfversion);
398         Frame frame = s.newFrame();
399         Program program = makeProgram(element, xmlsize);
400         frame.addFlashObject(new DoAction(program));
401         return file;
402     }
403
404
405     /**
406      * Get XML to output stream SWF
407      *
408      * @param xpp an XPP XML parser which points to the XML data
409      * @return swf input stream
410      */

411     public byte[] getSWF(Element element, int xmlsize, int swfversion)
412         throws IOException {
413         mLogger.debug("getSWF");
414         int i = 0;
415         try {
416             FlashFile file = makeSWF(element, xmlsize, swfversion);
417             FlashOutput fob = file.generate();
418             byte[] buf = new byte[fob.getSize()];
419             System.arraycopy(fob.getBuf(), 0, buf, 0, fob.getSize());
420             return buf;
421         } catch (IVException e) {
422             throw new ChainedException(e);
423         } catch (IOException e) {
424             mLogger.error("io error getting SWF: " + e.getMessage());
425             throw e;
426         }
427     }
428
429     /**
430      * @see compile(Reader, int)
431      */

432     public static byte[] compile(String JavaDoc xmlrpc, int swfversion)
433         throws IOException {
434         return compile(new StringReader(xmlrpc), xmlrpc.length(), swfversion);
435     }
436
437     /**
438      * Compile the XMLRPC response to SWF bytecode.
439      * @param in XMLRPC input.
440      * @return SWF bytecode for flash client.
441      */

442     public static byte[] compile(Reader reader, int xmlsize, int swfversion)
443         throws IOException {
444         mLogger.debug("compile(reader,xmlsize,swfversion)");
445         try {
446             // TODO: [2004-02-20 pkang] do we worry about character encoding?
447
DocumentBuilder builder = getDocumentBuilderFactory().newDocumentBuilder();
448             Document document = builder.parse(new InputSource(reader));
449             return new XMLRPCCompiler().getSWF(document.getDocumentElement(), xmlsize, swfversion);
450         } catch (Exception JavaDoc e) {
451             mLogger.error("Caught exception at compile: " + e);
452             StringWriter trace = new StringWriter();
453             e.printStackTrace(new PrintWriter(trace));
454             return compileFault(trace.toString(), swfversion);
455         }
456     }
457
458
459     public static String JavaDoc xmlFaultResponse(int code, String JavaDoc message) {
460         return new StringBuffer JavaDoc("<?xml version=\"1.0\"?>")
461             .append("<methodResponse>")
462             .append("<fault>")
463             .append("<value>")
464             .append("<struct>")
465             .append("<member>")
466             .append("<name>faultCode</name>")
467             .append("<value><int>").append(code).append("</int></value>")
468             .append("</member>")
469             .append("<member>")
470             .append("<name>faultString</name>")
471             .append("<value><string>")
472             .append(message)
473             .append("</string></value>")
474             .append("</member>")
475             .append("</struct>")
476             .append("</value>")
477             .append("</fault>")
478             .append("</methodResponse>")
479             .toString();
480     }
481
482     public static byte[] compileResponse(int code, String JavaDoc message, int swfversion)
483         throws IOException {
484         String JavaDoc fault = xmlFaultResponse(code, message);
485         try {
486             DocumentBuilder builder = getDocumentBuilderFactory().newDocumentBuilder();
487             Document document = builder.parse
488                 (new InputSource(new StringReader(fault)));
489             return new XMLRPCCompiler().getSWF(document.getDocumentElement(), fault.length(), swfversion);
490         } catch (Exception JavaDoc e) {
491             mLogger.error("Caught exception at compileFault: " + message, e);
492             throw new IOException(e.getMessage());
493         }
494     }
495
496     /**
497      * Used by compiler to send back exception messages.
498      */

499     public static byte[] compileFault(String JavaDoc message, int swfversion) throws IOException {
500         return compileResponse(-1, message, swfversion);
501     }
502
503
504     /**
505      * Main.
506      */

507     public static void main(String JavaDoc[] args) {
508         System.out.println("args: " + args.length);
509         if (args.length != 1) {
510             System.err.println("Usage: XMLRPCCompiler xmlrpcfile");
511             return;
512         }
513         try {
514             File file = new File(args[0]);
515             InputStream in = new ByteArrayInputStream(compile(new FileReader(file),
516                                                               (int)file.length(), 6));
517             OutputStream out = new FileOutputStream("xmlrpc.swf");
518             FileUtils.send(in, out, 4096);
519         } catch (Exception JavaDoc e) {
520             e.printStackTrace();
521         }
522     }
523 }
524
Popular Tags