KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2
3 TODO
4
5 + wrap resultset as sepearate compiled XML, splice together
6 <resultset>
7  <body/>
8  <headers/>
9 </resultset>
10    
11 + rename "root"
12
13 + add whitespace preserve option flag
14
15         // Write the header
16         xmlResponse.append("<resultset>");
17         
18             
19         // Get body
20         xmlResponse.append("<body>\n");
21         String body = null;
22
23         </body>
24                     xmlResponse.append("<headers>\n");
25             data.appendResponseHeadersAsXML(xmlResponse);
26             xmlResponse.append("</headers>");
27         }
28
29         // End the resultset tag
30         xmlResponse.append("</resultset>");
31 */

32
33
34
35 /* ****************************************************************************
36  * DataCompiler.java
37  *
38  * Compile XML directly to SWF bytecodes.
39  * ****************************************************************************/

40
41 /* J_LZ_COPYRIGHT_BEGIN *******************************************************
42 * Copyright 2001-2004 Laszlo Systems, Inc. All Rights Reserved. *
43 * Use is subject to license terms. *
44 * J_LZ_COPYRIGHT_END *********************************************************/

45
46 package org.openlaszlo.xml.internal;
47
48 import java.io.*;
49 import java.util.*;
50
51 import org.jdom.Attribute;
52 import org.jdom.Comment;
53 import org.jdom.Document;
54 import org.jdom.Element;
55 import org.jdom.JDOMException;
56 import org.jdom.input.SAXBuilder;
57 import org.jdom.output.*;
58
59
60 import org.openlaszlo.iv.flash.util.*;
61 import org.openlaszlo.iv.flash.api.action.*;
62 import org.openlaszlo.iv.flash.api.*;
63 import org.openlaszlo.compiler.CompilationError;
64 import org.openlaszlo.utils.ChainedException;
65 import org.openlaszlo.utils.FileUtils;
66 import org.openlaszlo.utils.HashIntTable;
67
68 import org.xmlpull.v1.XmlPullParser;
69 import org.xmlpull.v1.XmlPullParserException;
70 import org.xmlpull.v1.XmlPullParserFactory;
71
72 import org.apache.log4j.*;
73
74
75 /**
76  * Takes XML in various forms, and serializes it to isomorphic actionscript
77  * bytecode.
78  *
79  * @author Max Carlson, Henry Minsky
80  * @version 1.1
81  */

82 public class DataCompiler extends DataCommon {
83     /* Logger */
84     private static Logger mLogger = Logger.getLogger(DataCompiler.class);
85     private static XmlPullParserFactory factory = null;
86
87     /** global var to hold the image of the stack when splitting frames */
88     static int MISC_BUFSIZ = 4096;
89     static String JavaDoc STRINGBUF_VAR = "__dcstrbuf__";
90
91
92     private static XmlPullParserFactory getXPPFactory () {
93         if (factory == null) {
94             // Set up the XML Parser factory
95
try {
96                 String JavaDoc sys = null;
97                 try {
98                     sys = System.getProperty(XmlPullParserFactory.PROPERTY_NAME);
99                 } catch (SecurityException JavaDoc se) {
100                 }
101                 factory = XmlPullParserFactory.newInstance(sys, null);
102                 factory.setNamespaceAware(true);
103             } catch (XmlPullParserException e) {
104                 throw new RuntimeException JavaDoc(e.getMessage());
105             }
106         }
107         return factory;
108     }
109
110     public DataCompiler () { }
111
112
113     // compress = false, for back compatibility
114
public static InputStream compile(String JavaDoc x, String JavaDoc headers,
115                                       int flashVersion,
116                                       boolean addwrapper,
117                                       boolean trimWhitespace
118                                       ) throws IOException, DataCompilerException {
119         return compile(x, headers, flashVersion, addwrapper, trimWhitespace, false);
120     }
121
122
123     /**
124      * Compile XML to SWF
125      *
126      * @param String x XML string to compile
127      * @return input stream
128      *
129      * The size of the input XML will always be greater than the
130      * output .swf (possibly plus some factor, to be measured), so we can allocate a FlashBuffer
131      * with that max size and be done.
132      */

133
134     // Change input from String to Reader, for higher efficiency
135
public static InputStream compile(String JavaDoc x, String JavaDoc headers,
136                                       int flashVersion,
137                                       boolean addwrapper,
138                                       boolean trimWhitespace,
139                                       boolean compress
140                                       ) throws IOException, DataCompilerException {
141         try {
142             // XML Parser for the data body
143
XmlPullParser xppbody = getXPPFactory().newPullParser();
144             xppbody.setInput( new StringReader(x) );
145
146             // XML Parser for the headers
147
XmlPullParser xppheaders = getXPPFactory().newPullParser();
148             if (addwrapper) {
149                 xppheaders.setInput( new StringReader(headers) );
150             }
151             mLogger.debug("compile x="+x+"\nheaders="+headers);
152             return getSWF(xppbody, xppheaders, x.length(), flashVersion, addwrapper, trimWhitespace, compress);
153         } catch (XmlPullParserException ex) {
154             throw new DataCompilerException("Parsing XML: " + ex.getMessage());
155         }
156     }
157
158     public static InputStream compile(String JavaDoc x, int flashVersion) throws IOException, DataCompilerException {
159         try {
160             // XML Parser for the data body
161
XmlPullParser xppbody = getXPPFactory().newPullParser();
162             xppbody.setInput( new StringReader(x) );
163
164             return getSWF(xppbody, null, x.length(), flashVersion, false);
165         } catch (XmlPullParserException ex) {
166             throw new DataCompilerException("Parsing XML: " + ex.getMessage());
167         }
168     }
169
170     /** Create a new string pool buffer */
171     private static DataContext makeDataContext(int flashVersion) {
172         DataContext dc = new DataContext(flashVersion);
173         if (flashVersion == 5) {
174             dc.setEncoding("Cp1252");
175         } else {
176             dc.setEncoding("UTF-8");
177         }
178         return dc;
179     }
180
181     /** Split string into PUSH chunks of less than 64k, and emit string concat
182         instructions to re-assemble them. Leaves string value on stack.
183         
184     */

185     static void splitPushString (String JavaDoc text, Program prog) {
186         // break up into 64K chunks, append to local variable.
187
// To account for multibyte chars, use 10000 to be safe.
188
int index = 0;
189         int len = 0;
190         int nchunks = 0;
191         while (index < text.length()) {
192             if ((text.length() - index) > 10000) {
193                 len = 10000;
194             } else {
195                 len = text.length() - index;
196             }
197             String JavaDoc chunk = text.substring(index, index+len);
198             index += len;
199
200             prog.push(chunk);
201             nchunks++;
202         }
203
204         // concatenate all the strings
205
while (nchunks > 1) {
206             prog.addString();
207             nchunks--;
208         }
209     }
210
211
212
213     static int MAXFRAME_SIZE = 20000;
214
215     // Initial buffer size for program, with space for string constant pool (max 64k)
216
static int FB_INIT_SIZE = MAXFRAME_SIZE * 2 + 128000;
217
218     /* These two hardcoded constants are always added first to string pool of a frame, in this order. */
219     private static byte constructor_idx = 0;
220     private static byte textnode_idx = 1;
221
222     /**
223      * Main loop which pulls XPP events and writes swf bytecodes to build the node structure.
224      *
225      * @param FlashBuffer body flashbuffer to write to
226      * @param xpp XML parser pointing to data
227      * @param programs a list of Flash Programs. Add to it as we create new frames.
228      * enter assuming that root node is on the stack
229      *
230      */

231
232
233     /*
234
235     >>> c("stackarray=['top','middle', 'root']",printInstructions=1)
236       constants 'stackarray' 'middle' 'root' 'top'
237       push 'stackarray' 'root' 'middle' 'top' '3'
238       initArray
239       setVariable
240     >>>
241
242
243     >>> c('while(a.length > 0) a.pop()' , printInstructions=1)
244       constants 'a' 'length' 'pop'
245     L1:
246       push '0.0' 'a'
247       getVariable
248       push 'length'
249       getMember
250       lessThan
251       not
252       branchIfTrue 'L0'
253       push '0' 'a'
254       getVariable
255       push 'pop'
256       callMethod
257       pop
258       branch 'L1'
259     L0:
260     >>>
261
262     */

263
264     private static final DataContext writeXMLData(XmlPullParser xpp, int flashVersion,
265                                                   boolean splitframes, Vector programs,
266                                                   DataContext dc,
267                                                   boolean trimWhitespace, int stackdepth)
268           throws IOException, XmlPullParserException {
269
270         // Use the program we were passed in, it has the root node on top of stack.
271
Program bodyp = (Program) programs.lastElement();
272         FlashBuffer body = bodyp.body();
273
274         int eventType = xpp.getEventType();
275         while (eventType != xpp.END_DOCUMENT) {
276             if(eventType == xpp.START_DOCUMENT) {
277             } else if(eventType == xpp.START_TAG) {
278                 // makeNodeNoText = function (attrs, name, parent)
279
// dup pointer to PARENT (who is at top of stack)
280
// DUP
281
body._writeByte(Actions.PushDuplicate);
282
283
284                 // Fold all the attribute key/value pairs into a single PUSH
285
// Build ATTRIBUTE object
286
int nattrs = xpp.getAttributeCount();
287                 String JavaDoc eltname = xpp.getName();
288
289                 // Check if combined attr+data is > 64k, if so, use individual
290
// pushes with string breaking/merging, rather than compressed merging.
291
int datasize = 0;
292                 for (int i = 0; i < nattrs; i++) {
293                         String JavaDoc attrname = xpp.getAttributeName(i);
294                         datasize += getByteLength(attrname, dc.encoding) + 2;
295                         String JavaDoc attrval = xpp.getAttributeValue(i);
296                         datasize += getByteLength(attrval, dc.encoding) + 2;
297                 }
298
299                 // If if data is too large, use individual split pushses
300
if (datasize > 65500) {
301                     bodyp.push(eltname);
302
303                     for (int i = 0; i < nattrs; i++) {
304                         String JavaDoc attrname = xpp.getAttributeName(i);
305                         //System.out.print("Attr " + attrname);
306
bodyp.push(attrname);
307
308                         String JavaDoc attrval = xpp.getAttributeValue(i);
309                         splitPushString(attrval, bodyp);
310                     }
311
312                     bodyp.push(nattrs);
313                     body._writeByte(Actions.InitObject);
314                 } else {
315
316                 // We're really squeezing things down, so we are going to merge
317
// the PUSH of the element name with the PUSH of the attribute key/value
318
// data and the attribute count. So the stack will look like
319
// [eltname attrname1 attrval1 attrname2 attrval2 ... ... nattrs]
320
// when we're done
321
body._writeByte(Actions.PushData);
322                 // Leave a two byte space for the PUSH length
323
// Mark where we are, so we can rewrite this with correct length later.
324
int push_bufferpos = body.getPos();
325                 body._writeWord(0); // placeholder 16-bit length field
326

327                 // Push element NAME
328
_pushMergedStringDataSymbol(eltname, body, dc);
329
330                 // PUSH := {0x96, lenlo, lenhi, 0x00, char, char, char, ...0x00, }
331
for (int i = 0; i < nattrs; i++) {
332                     String JavaDoc attrname = xpp.getAttributeName(i);
333                     //System.out.print("Attr " + attrname);
334
_pushMergedStringDataSymbol(attrname, body, dc);
335
336                     String JavaDoc attrval = xpp.getAttributeValue(i);
337                     //System.out.println("= " + attrval);
338
_pushMergedStringData(attrval, body, dc);
339                 }
340                 // create the attrs object; push the attr count
341
body._writeByte(0x07); // INT type
342
body._writeDWord(nattrs);
343
344                 // Now go back and fix up the size arg to the PUSH instruction
345
int total_size = body.getPos() - (push_bufferpos + 2);
346                 //System.out.println("pos="+body.getPos()+ " total_size = "+total_size+" push_bufferpos="+push_bufferpos+" nattrs="+nattrs);
347
body.writeWordAt(total_size, push_bufferpos);
348
349                 body._writeByte(Actions.InitObject);
350                 }
351
352                 // stack now has [parent, name, attrs]
353
// Push # of args and node-instantiator-function name
354
// PUSH 3, _mdn
355
// [PUSHDATA, 9, 0, 0x07, 03 00 00 00 0x08, "_m"]
356
body._writeByte(Actions.PushData);
357                 body._writeWord(7);
358                 body._writeByte(0x07); // INT type
359
body._writeDWord(3); // '3' integer constant , number of args to node constructor fn
360
body._writeByte(0x08); // SHORT DICTIONARY LOOKUP type
361
body._writeByte(constructor_idx); // index of "_m" string constant
362
body._writeByte(Actions.CallFunction);
363
364
365                 // We push new node on the stack, so we can reference it as the parent
366
// Stack => [parentnode newnode]
367
// increment the node stack depth
368
stackdepth++;
369             } else if (eventType == xpp.END_TAG) {
370                 // Pop the node off the stack.
371
body._writeByte(Actions.Pop);
372                 stackdepth--;
373
374                 // Good place to check if this frame has gotten large enough to split.
375

376                 // IMPORTANT: recalculate buffer size to what it
377
// really is (since we've been using the fast
378
// _writeXXX functions which dont' update size
379
// automatically);
380
body.setSize(body.getPos());
381
382                 // When we end a frame, we call initArray (stackdepth) to unload to "__dcns".
383
// Then when we start the next frame, we loop to transfer array contents back to stack
384
if (splitframes && body.getSize() > MAXFRAME_SIZE) {
385                     //mLogger.debug("splitting frame "+body.getSize());
386

387                     // dump the stack to a global variable
388
// XXXXX NEED TO COMPENSATE FOR HEADER WRAPPERS!!!!!!!
389
bodyp.push(stackdepth);
390                     body.writeByte(Actions.InitArray);
391                     bodyp.push("__dcns");
392                     body.writeByte(Actions.StackSwap);
393                     bodyp.setVar();
394
395
396                     // prepend the string pool to the program,and add it to the frame list
397
Program withpool = appendStringPool(dc, body);
398                     // replace the last program with one that has a string pool.
399
programs.setElementAt(withpool, programs.size() -1);
400
401                     body = new FlashBuffer(FB_INIT_SIZE);
402                     bodyp = new Program(body);
403                     programs.add(bodyp);
404
405                     dc = makeDataContext(flashVersion);
406                     // Intern the node constructor function names in
407
// the string pool as first entries. Always use
408
// this ordering.
409
addStringConstant(NODE_INSTANTIATOR_FN, dc);
410                     addStringConstant(TEXT_INSTANTIATOR_FN, dc);
411
412                     // The code below is basically this:
413
// while(__dcns.length > 0) { __dcns.pop() }
414
// But each pop() leaves the popped data on stack.
415
//L1:
416
//push '0.0' 'a'
417
// == 34
418
bodyp.push(0); // 8
419
bodyp.push("__dcns"); // 4 + 6 +1= 11
420
//getVariable
421
bodyp.getVar(); // 1
422
//push 'length'
423
bodyp.push("length"); // 4 + "length" +1 = 11
424
//getMember
425
bodyp.getMember(); // 1
426
//lessThan
427
bodyp.lessThan(); //1
428
//not
429
bodyp.logicalNot(); // 1
430
// total = (+ 8 11 1 11 1 1 1 ) = 34
431
//branchIfTrue 'L0'
432

433                     // == 5
434
bodyp.jumpIfTrue(34); // 5 bytes //jump L0:
435

436                     // == 34
437
//push '0' 'a'
438
bodyp.push(0); // 4 + data length = 8
439
bodyp.push("__dcns"); // 4 + "__dcns" + 1 = 11
440
//getVariable
441
bodyp.getVar(); // 1
442
//push 'pop'
443
bodyp.push("pop"); // 4 + 1 + 'pop' = 8
444
//callMethod
445
bodyp.callMethod(); // 1
446
//branch 'L1'
447
bodyp.jump(- (34 + 34 + 5)); //5 bytes // jump L1:
448
//L0:
449
}
450
451             } else if(eventType == xpp.TEXT) {
452                 if (trimWhitespace && xpp.isWhitespace()) {
453                     // If we're supposed to trim whitespace, and text is all whitespace,
454
// then don't create a node.
455
} else {
456                     String JavaDoc text = xpp.getText();
457                     if (trimWhitespace) {
458                         text = text.trim();
459                     }
460
461                     // dup pointer to parent (who is at top of stack)
462
// DUP
463
body._writeByte(Actions.PushDuplicate);
464
465                     // Push text
466

467                     if (getByteLength(text, dc.encoding) > 64000) {
468                         splitPushString (text, bodyp);
469
470                         bodyp.push(2);
471                         bodyp.push(TEXT_INSTANTIATOR_FN);
472                         bodyp.callFunction();
473                         // Pop the node, because there will be no end tag for it, and it has no children.
474
body._writeByte(Actions.Pop);
475
476                     } else {
477                         body._writeByte(Actions.PushData);
478                         // Leave a two byte space for the PUSH length
479
// Mark where we are, so we can rewrite this with correct length later.
480
int push_bufferpos = body.getPos();
481                         body._writeWord(0); // placeholder 16-bit length field
482

483                         _pushMergedStringData(text, body, dc);
484                         // Set up argsnum and function name
485
// PUSH 2, _tdn
486
body._writeByte(0x07); // INT type
487
body._writeDWord(2); // '2' integer constant ; number of args to function
488
body._writeByte(0x08); // SHORT DICTIONARY LOOKUP
489
body._writeByte(textnode_idx); // push function name: index of "_t" string constant
490

491
492                         // Now go back and fix up the size arg to the PUSH instruction
493
int total_size = body.getPos() - (push_bufferpos + 2);
494                         body.writeWordAt(total_size, push_bufferpos);
495                         body._writeByte(Actions.CallFunction);
496                         // Pop the node, because there will be no end tag for it, and it has no children.
497
body._writeByte(Actions.Pop);
498                     }
499
500                 }
501             }
502             eventType = xpp.next();
503             // invariant is that when we start a frame, stack contains parent node list
504
}
505
506         // return the datacontext, the caller will use it to write the last string pool
507
return dc;
508     }
509
510     /** Takes the string hash table in DataContext dc, and prepends it to the
511         program in FlashBuffer body. Returns a new program with the stringpool
512         at the start.
513     **/

514     private static Program appendStringPool (DataContext dc, FlashBuffer body) {
515         // Collect the string dictionary data
516
byte pooldata[] = makeStringPool(dc);
517
518         // sync up size with buffer
519
body.setSize(body.getPos());
520
521         // 'out' is the main FlashBuffer for composing the output file
522
FlashBuffer out = new FlashBuffer(body.getSize() + pooldata.length + MISC_BUFSIZ);
523         // Write out string constant pool
524
out._writeByte( Actions.ConstantPool );
525         out._writeWord( pooldata.length + 2 ); // number of bytes in pool data + int (# strings)
526
out._writeWord( dc.cpool.size() ); // number of strings in pool
527
out.writeArray( pooldata, 0, pooldata.length); // copy the data
528

529         // Write out the code to build nodes
530
out.writeArray(body.getBuf(), 0, body.getSize());
531         // This is a program that contains the constant pool plus code
532
Program prog = new Program(out);
533         return prog;
534     }
535
536     /** default trimWhitespace=true */
537     public static Program makeProgram(Element data, int flashVersion) throws CompilationError {
538         return makeProgram(data, flashVersion, true, false);
539     }
540
541     /**
542        Called for compile-time data, don't do frame splitting, and don't add any resultset wrapper
543     */

544     public static Program makeProgram(Element data, int flashVersion, boolean trimWhitespace, boolean localdata) throws CompilationError {
545         XMLOutputter outputter = new XMLOutputter();
546         StringWriter sout = new StringWriter();
547         try {
548             outputter.output(data, sout);
549         }
550         catch (IOException ex) {
551             throw new RuntimeException JavaDoc("DataCompiler makeProgram parsing XML: " + ex.getMessage());
552         }
553         String JavaDoc x = sout.toString();
554         // [TODO 02-10-2003 hqm -- Do we need to catch character conversion errors -> ASCII like we did with JDOM?]
555
try {
556             XmlPullParser xpp = getXPPFactory().newPullParser();
557             xpp.setInput( new StringReader(x) );
558             Vector progs = makeProgram(xpp, null, x.length(), flashVersion, false, false, trimWhitespace, localdata);
559             return ((Program) progs.firstElement());
560         } catch (XmlPullParserException ex) {
561             throw new CompilationError("DataCompiler makeProgram parsing XML: " + ex.getMessage());
562         } catch (IOException ex) {
563             throw new RuntimeException JavaDoc("DataCompiler makeProgram parsing XML: " + ex.getMessage());
564         }
565     }
566
567
568
569     /**
570      * Produces a JGenerator Flash Program containing
571      * executable SWF codes to build an XML datasource structure which
572      * represents this XML stream.
573      *
574      * Splits execution across frames when program buffer becomes too large.
575      *
576      *
577      * @param xpp XML XPP parser which is reading from the data content string
578      * @param splitframes if true, split program across multiple frames. In that case, a final stop will be inserted at the last frame. If false, a single frame program will be returned, with no stop or end of frame.
579      * @return Vector of one or more (if frame splitting) Flash Programs */

580     public static Vector makeProgram(XmlPullParser xpp, XmlPullParser xpheaders,
581                                      int xmlsize, int flashVersion,
582                                      boolean splitframes,
583                                      boolean addwrapper,
584                                      boolean trimWhitespace,
585                                      boolean localdata)
586       throws IOException, XmlPullParserException {
587         Vector programs = new Vector();
588
589         // Allocate at least enough room to hold the data nodes and strings, ok to allocate too much;
590
// If we are not splitting frames, allocate enough to hold the whole XML file.
591
FlashBuffer body = new FlashBuffer(splitframes ? FB_INIT_SIZE : (xmlsize * 3) + 128000);
592         Program bodyp = new Program(body);
593         programs.add(bodyp);
594
595         // Bind the node creation functions to some short local names:
596
// element nodes: _level0._m => _m
597
bodyp.push(new Object JavaDoc[]{"_m", "_level0"});
598         bodyp.getVar();
599         bodyp.push("_m");
600         bodyp.getMember();
601         bodyp.setVar();
602
603         // text nodes: _level0._t => _t
604
bodyp.push(new Object JavaDoc[]{"_t", "_level0"});
605         bodyp.getVar();
606         bodyp.push("_t");
607         bodyp.getMember();
608         bodyp.setVar();
609
610         // Build a root node by calling the runtime's root node instantiator
611
// The root node will have $n = 0, and whatever other initial conditions are needed.
612
bodyp.push(0); // Root node creator function takes no args.
613
bodyp.push("_level0");
614         bodyp.getVar();
615         bodyp.push(ROOT_NODE_INSTANTIATOR_FN);
616         bodyp.callMethod();
617         // The root node is now on the stack.
618

619         if (addwrapper) {
620             // dup root node
621
body.writeByte(Actions.PushDuplicate);
622             // Create and push <resultset> node on stack
623
bodyp.push("resultset");
624             bodyp.push(0);
625             body._writeByte(Actions.InitObject);
626             bodyp.push(3); // 3 args : makeElementNode(attrs, name, parent)
627
bodyp.push(NODE_INSTANTIATOR_FN);
628             bodyp.callFunction();
629             // stack = <root> <resultset>
630

631
632             // Push <body> element
633
body.writeByte(Actions.PushDuplicate);
634             bodyp.push("body");
635             bodyp.push(0);
636             body._writeByte(Actions.InitObject); // {}
637
bodyp.push(3); // 3 args : makeElementNode(attrs, name, parent)
638
bodyp.push(NODE_INSTANTIATOR_FN);
639             bodyp.callFunction(); // stack = <root> <resultset> <body>
640
}
641
642         // Build data, which will get stuck under <body> node
643
DataContext dc = makeDataContext(flashVersion);
644         // Always insert the node constructor function names as the
645
// first entries in string pool in this order.
646
addStringConstant(NODE_INSTANTIATOR_FN, dc);
647         addStringConstant(TEXT_INSTANTIATOR_FN, dc);
648         // If it's a big multiframe dataset, a new dc in a new frame may be returned.
649

650         // If we are adding a wrapper, the stack has two extra items on it already, <resultset> and <body>.
651
int stackdepth = (addwrapper ? 3 : 1);
652         dc = writeXMLData(xpp, flashVersion, splitframes, programs, dc, trimWhitespace, stackdepth);
653         // Multiple frames may have been created, so append
654
// finalization code to the last program in the list.
655
bodyp = (Program) programs.lastElement();
656         body = bodyp.body();
657
658         // Finalization Phase
659
if (addwrapper) {
660             // pop the <body> node
661
bodyp.pop();
662             // stack == <root> <resultset>
663
// Add in <headers>, they will get the <resultset> node as parent,
664
// since it's on top of stack
665
dc = writeXMLData(xpheaders, flashVersion, false, programs, dc, trimWhitespace, stackdepth);
666             bodyp = (Program) programs.lastElement();
667             body = bodyp.body();
668             bodyp.pop(); // stack == <root>
669
}
670
671         // Call the node finalize function; takes one arg: the scaffolding root node
672
bodyp.push(1);
673         bodyp.push("_level0");
674         bodyp.getVar();
675         bodyp.push(ROOT_NODE_FINAL_FN);
676         bodyp.callMethod();
677
678         // The <resultset> node is on the stack now
679

680         // This is used by the data compiler to compile inline
681
// datasets into an app swf file.
682
body.writeByte(Actions.PushDuplicate);
683         bodyp.push("__lzdataroot");
684         body.writeByte(Actions.StackSwap);
685         bodyp.setVar();
686
687
688         // If we're runtime-loaded (non-local) data, we need to call
689
// back to our parent movieclip data loader.
690
if (!localdata) {
691             // Call to _parent.loader.returnData(_parent, data)
692
bodyp.push("_parent");
693             bodyp.getVar();
694             bodyp.push(2);
695             bodyp.push("_parent");
696             bodyp.getVar();
697             bodyp.push("loader");
698             bodyp.getMember();
699             bodyp.push("returnData");
700             bodyp.callMethod();
701             bodyp.pop();
702
703             if (splitframes) {
704                 // add a STOP for multiframe programs, otherwise it loops
705
bodyp.stop();
706             }
707         }
708
709         // prepend the string pool to the program,and add it to the frame list
710
Program withpool = appendStringPool(dc, body);
711         // add to the list of frame/programs
712
programs.setElementAt(withpool, programs.size()-1);
713
714         return programs;
715     }
716
717
718     /**
719      * Make SWF
720      *
721      * @param xpp parser which is pointing at XML data
722      * @return FlashFile containing entire SWF with header
723      */

724     private static FlashFile makeSWF(XmlPullParser xpp, XmlPullParser xpheaders, int xmlsize,
725                                      int flashVersion, boolean addwrapper, boolean trimWhitespace)
726       throws IOException, XmlPullParserException {
727         mLogger.debug("makeSWF: addwrapper="+addwrapper+" xpp = "+xpp+" xpheaders="+xpheaders+" xmlsize="+xmlsize);
728         // Create FlashFile object nd include action bytes
729
FlashFile file = FlashFile.newFlashFile();
730         Script s = new Script(1);
731         file.setMainScript(s);
732         file.setVersion(flashVersion);
733         Vector progs = makeProgram(xpp, xpheaders, xmlsize, flashVersion, true, addwrapper, trimWhitespace, false);
734
735         // iterate making a frame for each program
736
while (progs.size() > 0) {
737             Program prog = (Program) progs.firstElement();
738             progs.removeElementAt(0); // pop
739
Frame frame = s.newFrame();
740             frame.addFlashObject(new DoAction(prog));
741         }
742
743         return file;
744     }
745
746     /** default trimWhitespace to true, for back compatibility */
747     public static InputStream getSWF(XmlPullParser xpp, XmlPullParser xpheaders, int xmlsize,
748                                      int flashVersion, boolean addwrapper)
749       throws IOException, XmlPullParserException {
750         return getSWF(xpp, xpheaders, xmlsize, flashVersion, addwrapper, true, false);
751     }
752
753
754     /**
755      * Get XML to output stream SWF
756      *
757      * @param xpp an XPP XML parser which points to the XML data
758      * @param xppheaders an XPP XML parser which points to HTTP or other header XML metadata
759      * @param flashVersion 5 or greater
760      * @param addwrapper Set to true if you pass in a separate HTTP headers string
761      * @param trimWhitespace controls whether whitespace is trimmed in text content
762      * @return swf input stream
763      */

764     public static InputStream getSWF(XmlPullParser xpp, XmlPullParser xpheaders, int xmlsize,
765                                      int flashVersion, boolean addwrapper, boolean trimWhitespace,
766                                      boolean compress)
767       throws IOException, XmlPullParserException {
768         // Get inputstream and write to outputstream
769
InputStream input;
770         int i = 0;
771         try {
772             FlashFile file = makeSWF(xpp, xpheaders, xmlsize, flashVersion, addwrapper, trimWhitespace);
773             if (flashVersion > 5) {
774                 if (compress) {
775                     file.setCompressed(true);
776                 }
777             }
778             return file.generate().getInputStream();
779         } catch (IVException ex) {
780             throw new ChainedException(ex);
781         } catch (IOException e) {
782             mLogger.error("io error getting SWF: " + e.getMessage());
783             throw e;
784         }
785     }
786
787 }
788  
789
Popular Tags