KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openlaszlo > compiler > Compiler


1 /* *****************************************************************************
2  * Compiler.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.compiler;
11 import java.lang.Integer JavaDoc;
12 import java.io.*;
13 import java.text.ChoiceFormat JavaDoc;
14 import java.text.MessageFormat JavaDoc;
15 import java.util.*;
16 import org.jdom.*;
17 import org.jdom.input.SAXBuilder;
18 import org.openlaszlo.sc.ScriptCompiler;
19 import org.openlaszlo.utils.ChainedException;
20 import org.openlaszlo.utils.FileUtils;
21 import org.openlaszlo.utils.ListFormat;
22 import org.openlaszlo.xml.internal.*;
23 import org.openlaszlo.server.LPS;
24 import org.apache.log4j.*;
25
26 import org.openlaszlo.iv.flash.util.*;
27 import org.openlaszlo.iv.flash.api.*;
28
29
30
31 /**
32  * Compiles a Laszlo XML source file, and any files that it
33  * references, into an object file that can be executed on the client.
34  *
35  * The compiler parses the file into XML, and then gets an element
36  * compiler for each toplevel element.
37  */

38 public class Compiler {
39     /** Set this to log the modified schema. */
40     public static Logger SchemaLogger = Logger.getLogger("schema");
41     
42     protected static List KNOWN_RUNTIMES =
43         Arrays.asList(new String JavaDoc[] {"swf5", "swf6", "swf7"});
44     
45     /** Called to resolve file references (<code>src</code>
46      * attributes).
47      */

48     protected FileResolver mFileResolver = FileResolver.DEFAULT_FILE_RESOLVER;
49
50     /**
51      * CompilerMediaCache
52      */

53     protected CompilerMediaCache mMediaCache = null;
54
55     /** See getProperties.
56      */

57     protected final Properties mProperties = new Properties();
58
59     /** Logger
60      */

61     private static Logger mLogger = Logger.getLogger(Compiler JavaDoc.class);
62
63
64     /**
65      * A <code>Compiler</code>'s <code>Properties</code> stores
66      * properties that affect the compilation.
67      *
68      * <table><tr><th>Name=default</th><th>Meaning</th></tr>
69      * <tr><td>trace.xml=false</td><td>Display XML that's compiled to
70      * script, and the script that it compiles to.</td></tr>
71      * <tr><td>debug=true</td><td>Add in debug features to the output.</td></tr>
72      * <tr><td>trace.fonts=false</td><td>Additional font traces.</td></tr>
73      * <tr><td>swf.frame.rate=30</td><td>Output SWF frame rate.</td></tr>
74      * <tr><td>default.text.height=12</td><td>Default text height for
75      * fonts.</td></tr>
76      * <tr><td>text.borders=false</td><td>Display text borders in the output SWF
77      * for debugging.</td></tr>
78      * </table>
79      *
80      * Properties that one part of the compiler sets for another part to read:
81      * <table><tr><th>Name=default</th><th>Meaning</th></tr>
82      * <tr><td>lzc_*</td><td>Source location values.</td></tr>
83      * </table>
84      *
85      * @return a <code>Properties</code> value
86      */

87     public String JavaDoc getProperty(String JavaDoc key) {
88         return mProperties.getProperty(key);
89     }
90     
91     public void setProperty(String JavaDoc key, String JavaDoc value) {
92         mProperties.setProperty(key, value);
93     }
94     
95     public FileResolver getFileResolver() {
96         return mFileResolver;
97     }
98     
99     /** Sets the file resolver for this compiler. The file resolver
100      * is used to resolve the names used in include directives to
101      * files.
102      * @param resolver a FileResolver
103      */

104     public void setFileResolver(FileResolver resolver) {
105         this.mFileResolver = resolver;
106     }
107
108     /** Sets the media cache for this compiler.
109      * @param resolver a CompilerMediaCache
110      */

111     public void setMediaCache(CompilerMediaCache cache) {
112         this.mMediaCache = cache;
113     }
114     
115     /** Create a CompilationEnvironment with the properties and
116      * FileResolver of this compiler.
117      */

118     public CompilationEnvironment makeCompilationEnvironment() {
119         CompilationEnvironment env =
120             new CompilationEnvironment(mProperties, mFileResolver, mMediaCache);
121         return env;
122     }
123     
124     /** Compiles <var>sourceFile</var> to <var>objectFile</var>. If
125      * compilation fails, <var>objectFile</var> is deleted.
126      *
127      * @param sourceFile source File
128      * @param objectFile a File to place object code in
129      * @param url request url, ignore if null
130      * @throws CompilationError if an error occurs
131      * @throws IOException if an error occurs
132      */

133     public Canvas compile(File sourceFile, File objectFile, String JavaDoc url)
134         throws CompilationError, IOException
135     {
136         Properties props = new Properties();
137         return compile(sourceFile, objectFile, props);
138     }
139
140
141     /** Compiles <var>sourceFile</var> to <var>objectFile</var>. If
142      * compilation fails, <var>objectFile</var> is deleted.
143      *
144      * @param sourceFile source File
145      * @param objectFile a File to place object code in
146      * @param props parameters for the compile
147      * @throws CompilationError if an error occurs
148      * @throws IOException if an error occurs
149
150      * The PROPS parameters for the compile may include
151      * <ul>
152      * <li> url, optional, ignore if null
153      * <li> debug := "true" | "false" include debugger
154      * <li> logdebug := "true" | "false" makes debug.write() calls get logged to server
155      * </ul>
156      */

157     public Canvas compile(File sourceFile, File objectFile, Properties props)
158         throws CompilationError, IOException
159     {
160         FileUtils.makeFileAndParentDirs(objectFile);
161
162         // Compile to a byte-array, and write out to objectFile, and
163
// write a copy into sourceFile.swf if this is a serverless deployment.
164
ByteArrayOutputStream bstream = new ByteArrayOutputStream();
165         OutputStream ostream = new FileOutputStream(objectFile);
166
167         boolean success = false;
168         try {
169             Canvas canvas = compile(sourceFile, bstream, props);
170             bstream.writeTo(ostream);
171             ostream.close();
172
173             // If the app is serverless, write out a .lzx.swf file when compiling
174
if (!canvas.isProxied()) {
175                 OutputStream fostream = null;
176                 try {
177                     // Create a foo.lzx.swf serverless deployment file for sourcefile foo.lzx
178
File deploymentFile = new File(sourceFile+".swf");
179                     fostream = new FileOutputStream(deploymentFile);
180                     bstream.writeTo(fostream);
181                 } finally {
182                     if (fostream != null) {
183                         fostream.close();
184                     }
185                 }
186             }
187             success = true;
188             return canvas;
189         } catch (java.lang.OutOfMemoryError JavaDoc e) {
190             // The runtime gc is necessary in order for subsequent
191
// compiles to succeed. The System gc is for good luck.
192
System.gc();
193             Runtime.getRuntime().gc();
194             throw new CompilationError("out of memory");
195         } finally {
196             if (!success) {
197                 ostream.close();
198                 objectFile.delete();
199             }
200         }
201     }
202
203     /**
204      * Compiles <var>file</var>, and write the bytes to
205      * a stream.
206      *
207      * @param sourceFile a <code>File</code> value
208      * @param ostr an <code>OutputStream</code> value
209      * @param props parameters for the compilation
210      * @exception CompilationError if an error occurs
211      * @exception IOException if an error occurs
212      *
213      * The parameters currently being looked for in the PROPS arg
214      * are "debug", "logdebug", "profile", "krank"
215      */

216     public Canvas compile(File file, OutputStream ostr, Properties props)
217         throws CompilationError, IOException
218     {
219         mLogger.info("compiling " + file + "...");
220         CompilationEnvironment env = makeCompilationEnvironment();
221         CompilationErrorHandler errors = env.getErrorHandler();
222         env.setApplicationFile(file);
223         
224         // Copy target properties (debug, logdebug, profile, krank,
225
// runtime) from props arg to CompilationEnvironment
226
String JavaDoc swfversion = props.getProperty(env.SWFVERSION_PROPERTY);
227         
228         if (swfversion != null) {
229             mLogger.info("canvas compiler compiling runtime = " + swfversion);
230             env.setProperty(env.SWFVERSION_PROPERTY, swfversion);
231             if (! KNOWN_RUNTIMES.contains(swfversion)) {
232                 List runtimes = new Vector();
233                 for (Iterator iter = KNOWN_RUNTIMES.iterator();
234                      iter.hasNext(); ) {
235                     runtimes.add("\"" + iter.next() + "\"");
236                 }
237                 
238                 throw new CompilationError(
239                     MessageFormat.format(
240                         "Request for unknown runtime: The \"lzr\" query parameter has the value \"{0}\". It must be {1}{2}.",
241                         new String JavaDoc[] {
242                             swfversion,
243                             new ChoiceFormat JavaDoc(
244                                 "1#| 2#either | 2<one of ").
245                             format(runtimes.size()),
246                             new ListFormat("or").format(runtimes)
247                         }));
248             }
249         }
250
251         String JavaDoc proxied = props.getProperty(env.PROXIED_PROPERTY);
252         mLogger.debug("looking for lzproxied, props= " + props.toString());
253         if (proxied != null) {
254             mLogger.debug("setting lzproxied to " + proxied);
255             env.setProperty(env.PROXIED_PROPERTY, proxied);
256         }
257
258
259         String JavaDoc debug = props.getProperty(env.DEBUG_PROPERTY);
260         if (debug != null) {
261             env.setProperty(env.DEBUG_PROPERTY, debug);
262         }
263
264         String JavaDoc profile = props.getProperty(env.PROFILE_PROPERTY);
265         if (profile != null) {
266             env.setProperty(env.PROFILE_PROPERTY, profile);
267         }
268
269         String JavaDoc krank = props.getProperty(env.KRANK_PROPERTY);
270         if (krank != null) {
271             env.setProperty(env.KRANK_PROPERTY, krank);
272         }
273
274         String JavaDoc logdebug = props.getProperty(env.LOGDEBUG_PROPERTY);
275         if (logdebug != null) {
276             env.setProperty(env.LOGDEBUG_PROPERTY, logdebug);
277         }
278
279         String JavaDoc remotedebug = props.getProperty(env.REMOTEDEBUG_PROPERTY);
280         if (remotedebug != null) {
281             env.setProperty(env.REMOTEDEBUG_PROPERTY, remotedebug);
282         }
283
284         String JavaDoc sourcelocators = props.getProperty(env.SOURCELOCATOR_PROPERTY);
285         if (sourcelocators != null) {
286             env.setProperty(env.SOURCELOCATOR_PROPERTY, sourcelocators);
287         }
288
289         try {
290             mLogger.debug("Parsing " + file.getAbsolutePath());
291
292             Document doc = env.getParser().parse(file);
293             Element root = doc.getRootElement();
294             
295             // Override passed in runtime target properties with the
296
// canvas values.
297
if ("true".equals(root.getAttributeValue("debug"))) {
298                 env.setProperty(env.DEBUG_PROPERTY, true);
299             }
300
301             if ("true".equals(root.getAttributeValue("profile"))) {
302                 env.setProperty(env.PROFILE_PROPERTY, true);
303             }
304             if ("false".equals(root.getAttributeValue("validate"))) {
305                 env.setProperty(env.VALIDATE_PROPERTY, false);
306             }
307             // Krank cannot be set in the canvas tag
308

309             mLogger.debug("Making a writer...");
310             
311             // Initialize the schema from the base RELAX file
312
try {
313                 env.getSchema().loadSchema();
314             } catch (org.jdom.JDOMException e) {
315                 throw new ChainedException(e);
316             }
317             Compiler.updateSchema(doc.getRootElement(), env,
318                                   env.getSchema(), new HashSet());
319             if (SchemaLogger.isDebugEnabled()) {
320                 org.jdom.output.XMLOutputter xmloutputter =
321                     new org.jdom.output.XMLOutputter();
322                 try {
323                     SchemaLogger.debug(xmloutputter.outputString(env.getSchema().getSchemaDOM()));
324                 } catch (org.jdom.JDOMException e) {
325                     throw new ChainedException(e);
326                 }
327             }
328             
329             if (env.getBooleanProperty(env.VALIDATE_PROPERTY))
330                 Parser.validate(doc, file.getPath(), env);
331             
332             SWFWriter writer = new SWFWriter(env.getProperties(), ostr,
333                                              mMediaCache, true, env);
334             env.setSWFWriter(writer);
335             mLogger.debug("new env..." + env.getProperties().toString());
336             if (root.getName().intern() != "canvas") {
337                 throw new CompilationError("invalid root element type: " +
338                                            root.getName(), root);
339             }
340             
341             processCompilerInstructions(root, env);
342             compileElement(root, env);
343             ViewCompiler.checkUnresolvedResourceReferences (env);
344             mLogger.debug("done...");
345             // This isn't in a finally clause, because it won't generally
346
// succeed if an error occurs.
347
writer.close();
348             
349             Canvas canvas = env.getCanvas();
350             if (!errors.isEmpty()) {
351                 if (canvas != null) {
352                     canvas.setCompilationWarningText(
353                         errors.toCompilationError().getMessage());
354                     canvas.setCompilationWarningXML(
355                         errors.toXML());
356                 }
357                 System.err.println(errors.toCompilationError().getMessage());
358             }
359             // set file path (relative to webapp) in canvas
360
canvas.setFilePath(FileUtils.relativePath(file, LPS.HOME()));
361             mLogger.info("done");
362             return canvas;
363         } catch (CompilationError e) {
364             // TBD: e.initPathname(file.getPath());
365
e.attachErrors(errors.getErrors());
366             throw e;
367             //errors.addError(new CompilationError(e.getMessage() + "; compilation aborted"));
368
//throw errors.toCompilationError();
369
} catch (org.openlaszlo.xml.internal.MissingAttributeException e) {
370             /* The validator will have caught this, but if we simply
371              * pass the error through, the vaildation warnings will
372              * never be printed. Create a new message that includes
373              * them so that we get the source information. */

374             errors.addError(new CompilationError(e.getMessage() + "; compilation aborted"));
375             throw errors.toCompilationError();
376         }
377     }
378
379     public void compileAndWriteToSWF (String JavaDoc script, String JavaDoc seqnum, OutputStream out, String JavaDoc swfversion) {
380         try {
381             CompilationEnvironment env = makeCompilationEnvironment();
382             env.setProperty(env.DEBUG_PROPERTY, true);
383             Properties props = (Properties) env.getProperties().clone();
384             env.setProperty(env.SWFVERSION_PROPERTY, swfversion);
385             byte[] action;
386
387             // Try compiling as an expression first. If that fails,
388
// compile as sequence of statements. If that fails too,
389
// report the parse error.
390
try {
391                 String JavaDoc prog;
392                 if (seqnum == null) {
393                     prog = "Debug.displayResult(" + script + ");\n";
394                 } else {
395                     // it's a remote debug request, send a response to client
396
prog = "Debug.displayResult(_level0.__LzDebug.sockWriteAsXML("+script+","+seqnum+"));\n";
397                 }
398                 prog += "this._parent.loader.returnData( this._parent );";
399                 action = ScriptCompiler.compileToByteArray(prog, props);
400             } catch (org.openlaszlo.sc.parser.ParseException e) {
401                 try {
402                     action = ScriptCompiler.compileToByteArray(
403                         "function $evalee() {\n" +
404                         "#pragma 'scriptElement'\n" +
405                         CompilerUtils.sourceLocationDirective(null, new Integer JavaDoc(0), new Integer JavaDoc(0)) +
406                         script+"\n"+
407                         "if (Debug.remoteDebug) { _level0.__LzDebug.sockWriteAsXML(true,"+seqnum+");};\n" +
408                         "};\n" +
409                         "$evalee();\n" +
410                         "this._parent.loader.returnData( this._parent )", props);
411                 } catch (org.openlaszlo.sc.parser.ParseException e2) {
412                     //mLogger.info("not stmt: " + e);
413
action = ScriptCompiler.compileToByteArray(
414                         "Debug.__write(" +
415                         ScriptCompiler.quote("Parse error: "+ e2.getMessage()) + ")\n"
416                         + "this._parent.loader.returnData( this._parent )", props);
417                 }
418             }
419
420             ScriptCompiler.writeScriptToStream(action, out, LPS.getSWFVersionNum(swfversion));
421             out.flush();
422             out.close();
423         } catch (IOException e) {
424             mLogger.info("error compiling/writing script: "+script+" :" +e);
425         }
426     }
427
428
429
430     static ElementCompiler getElementCompiler(Element element,
431                                               CompilationEnvironment env) {
432         if (CanvasCompiler.isElement(element)) {
433             return new CanvasCompiler(env);
434         } else if (ImportCompiler.isElement(element)) {
435             return new ImportCompiler(env);
436         } else if (LibraryCompiler.isElement(element)) {
437             return new LibraryCompiler(env);
438         } else if (ScriptElementCompiler.isElement(element)) {
439             return new ScriptElementCompiler(env);
440         } else if (SecurityCompiler.isElement(element)) {
441             return new SecurityCompiler(env);
442         } else if (SplashCompiler.isElement(element)) {
443             return new SplashCompiler(env);
444         } else if (FontCompiler.isElement(element)) {
445             return new FontCompiler(env);
446         } else if (ResourceCompiler.isElement(element)) {
447             return new ResourceCompiler(env);
448         } else if (ClassCompiler.isElement(element)) {
449             return new ClassCompiler(env);
450         } else if (DataCompiler.isElement(element)) {
451             return new DataCompiler(env);
452         } else if (DebugCompiler.isElement(element)) {
453             return new DebugCompiler(env);
454         } else if (SwitchCompiler.isElement(element)) {
455             return new SwitchCompiler(env);
456             // The following test tests true for everything, so call
457
// it last.
458
} else if (ViewCompiler.isElement(element)) {
459             return new ViewCompiler(env);
460         } else {
461             throw new CompilationError("unknown tag: " + element.getName(),
462                                        element);
463         }
464     }
465
466     /**
467      * Compile an XML element within the compilation environment.
468      * Helper function for compile.
469      *
470      * @param element an <code>Element</code> value
471      * @param env a <code>CompilationEnvironment</code> value
472      * @param base a <code>String</code> value
473      * @exception CompilationError if an error occurs
474      */

475     protected static void compileElement(Element element,
476                                          CompilationEnvironment env)
477         throws CompilationError
478     {
479         if (element.getAttributeValue("disabled") != null &&
480             element.getAttributeValue("disabled").intern() == "true") {
481             return;
482         }
483         try {
484             ElementCompiler compiler = getElementCompiler(element, env);
485             mLogger.debug("compiling element with " + compiler.getClass().toString());
486             compiler.compile(element);
487         } catch (CompilationError e) {
488             // todo: wrap instead
489
if (e.getElement() == null && e.getPathname() == null) {
490                 e.initElement(element);
491             }
492             throw e;
493         }
494     }
495
496     static void updateSchema(Element element, CompilationEnvironment env,
497                              ViewSchema schema, Set visited)
498     {
499         getElementCompiler(element, env).updateSchema(element, schema, visited);
500     }
501
502     static void importLibrary(File file, CompilationEnvironment env) {
503         Element root = LibraryCompiler.resolveLibraryElement(
504             file, env, env.getImportedLibraryFiles(), true);
505         if (root != null) {
506             compileElement(root, env);
507         }
508     }
509     
510     static void updateSchemaFromLibrary(File file, CompilationEnvironment env,
511                                         ViewSchema schema, Set visited)
512     {
513         Element root = LibraryCompiler.resolveLibraryElement(
514             file, env, visited, false);
515         if (root != null) {
516             Compiler.updateSchema(root, env, schema, visited);
517         }
518     }
519     
520     protected void processCompilerInstructions(Element element,
521                                                CompilationEnvironment env) {
522         for (Iterator iter = element.getContent().iterator();
523              iter.hasNext(); ) {
524             ProcessingInstruction pi;
525             try {
526                 pi = (ProcessingInstruction) iter.next();
527             } catch (ClassCastException JavaDoc e) {
528                 continue;
529             }
530             if (pi.getTarget().equals("lzc"))
531                 processCompilerInstruction(env, pi);
532         }
533     }
534     
535     protected void processCompilerInstruction(CompilationEnvironment env,
536                                               ProcessingInstruction pi) {
537         if (pi.getValue("class") != null)
538             processClassInstruction(env, pi.getValue("class"), pi);
539         if (pi.getValue("classes") != null) {
540             for (Iterator iter = Arrays.asList(pi.getValue("classes").split("\\s+")).iterator(); iter.hasNext(); ) {
541                 processClassInstruction(env, (String JavaDoc) iter.next(), pi);
542             }
543         }
544     }
545     
546     protected void processClassInstruction(CompilationEnvironment env,
547                                            String JavaDoc className,
548                                            ProcessingInstruction pi) {
549         String JavaDoc inlineString = pi.getValue("inline-only");
550         if (inlineString != null) {
551             boolean inline = Boolean.valueOf(inlineString).booleanValue();
552             ClassModel classModel = env.getSchema().getClassModel(className);
553             if (classModel == null) {
554                 env.warn("A processor instruction refers to a class named \""+
555                          className + "\". No class with this name exists.");
556                 return;
557             }
558             classModel.setInline(inline);
559         }
560     }
561 }
562
Popular Tags