001: /*****************************************************************************
002: * DHTMLWriter.java
003: * ****************************************************************************/package org.openlaszlo.compiler;
004:
005: import org.openlaszlo.sc.ScriptCompiler;
006: import org.openlaszlo.server.LPS;
007: import org.openlaszlo.utils.ChainedException;
008: import org.openlaszlo.utils.FileUtils;
009: import org.openlaszlo.utils.ListFormat;
010: import org.openlaszlo.compiler.CompilationEnvironment;
011: import org.openlaszlo.compiler.ObjectWriter.ImportResourceError;
012: import org.openlaszlo.compiler.ObjectWriter.Resource;
013:
014: import org.openlaszlo.iv.flash.api.FlashDef;
015:
016: import org.openlaszlo.media.*;
017:
018: import java.io.*;
019: import java.util.*;
020: import java.lang.Math;
021: import java.lang.Character;
022:
023: import org.jdom.Element;
024:
025: // jgen 1.4
026: import java.awt.geom.Rectangle2D;
027:
028: import org.apache.log4j.*;
029:
030: /** Accumulates code, XML, and assets to a DHTML object file.
031: *
032: * Properties documented in Compiler.getProperties.
033: */
034: class DHTMLWriter extends ObjectWriter {
035:
036: // Accumulate script here, to pass to script compiler
037: protected PrintWriter scriptWriter = null;
038: protected StringWriter scriptBuffer = null;
039:
040: /** Logger */
041: protected static Logger mLogger = org.apache.log4j.Logger
042: .getLogger(DHTMLWriter.class);
043:
044: DHTMLWriter(Properties props, OutputStream stream,
045: CompilerMediaCache cache, boolean importLibrary,
046: CompilationEnvironment env) {
047:
048: super (props, stream, cache, importLibrary, env);
049:
050: scriptBuffer = new StringWriter();
051: scriptWriter = new PrintWriter(scriptBuffer);
052:
053: }
054:
055: /**
056: * Sets the canvas for the app
057: *
058: * @param canvas
059: *
060: */
061: void setCanvas(Canvas canvas, String canvasConstructor) {
062: scriptWriter.println(canvasConstructor);
063: }
064:
065: void setCanvasDefaults(Canvas canvas, CompilerMediaCache mc) {
066: };
067:
068: public int addScript(String script) {
069: scriptWriter.println(script);
070: return script.length();
071: }
072:
073: public void importPreloadResource(File fileName, String name)
074: throws ImportResourceError {
075: }
076:
077: public void importPreloadResource(String fileName, String name)
078: throws ImportResourceError {
079: }
080:
081: /** Import a multiframe resource into the current movie. Using a
082: * name that already exists clobbers the old resource (for now).
083: */
084: public void importPreloadResource(List sources, String name,
085: File parent) throws ImportResourceError {
086: }
087:
088: /** Import a resource file into the current movie.
089: * Using a name that already exists clobbers the
090: * old resource (for now).
091: *
092: * @param fileName file name of the resource
093: * @param name name of the MovieClip/Sprite
094: * @throws CompilationError
095: */
096: public void importResource(String fileName, String name)
097: throws ImportResourceError {
098: importResource(new File(fileName), name);
099: }
100:
101: public void importResource(File inputFile, String name)
102: throws ImportResourceError {
103: // Moved this conversion from below.
104: try {
105: inputFile = inputFile.getCanonicalFile(); //better be ok if this does not yet exist. changed from getcanonicalPath to getCanonicalFile
106: } catch (java.io.IOException e) {
107: throw new ImportResourceError(inputFile.toString(), e, mEnv);
108: }
109:
110: File[] sources;
111: List outsources = new ArrayList();
112: mLogger.debug("DHTMLWriter: Importing resource: " + name);
113: if (inputFile.isDirectory()) {
114: //mLogger.debug("DHTMLWriter Is directory: " + inputFile.toString());
115: sources = inputFile.listFiles();
116: //mLogger.debug("DHTMLWriter: "+inputFile.toString()+" is a directory containing "+ sources.length +" files.");
117: for (int i = 0; i < sources.length; i++) {
118: // Construct path from directory and file names.
119: /* TODO: In theory, file resolution might get files that come from somewhere on the file system that doesn't match where
120: things will be on the server. Part of the root path may differ, and this File doesn't actually have to correspond
121: to a file on the server disk. Thus the path is reconstructed here.
122: That said, the current code isn't actually abstracting the path here.
123: This will actually come into play when the '2 phase' file resolution currently occuring is
124: compacted to a single phase. To do this, more resource and file descriptor information will have
125: to be maintained, either in a global table or using a more abstract path structure, or both.
126: For now I'll leave this as it is. [pga]
127: */
128: String sFname = inputFile.toString() + File.separator
129: + sources[i].getName();
130: File f = new File(sFname);
131: //mLogger.debug("DHTMLWriter file: " + sFname + " is a file? " + f.isFile());
132: if (f.isFile()) {
133: outsources.add(sFname);
134: }
135: }
136: importResource(outsources, name, null);
137: return;
138: } else if (!inputFile.exists()) {
139: // This case is supposed to handle multiframe resources, as is the one above.
140: sources = FileUtils.matchPlusSuffix(inputFile);
141: for (int i = 0; i < sources.length; i++) {
142: // Construct path from directory and file names. (see comment above [pga])
143: File fDir = inputFile.getParentFile();
144: String sFname = fDir.toString() + File.separator
145: + sources[i].getName();
146: File f = new File(sFname); //sources[i];
147: //mLogger.debug("DHTMLWriter file: " + f.toString() + " is a file? " + f.isFile());
148:
149: if (f.isFile()) {
150: outsources.add(f.toString());
151: }
152: }
153: importResource(outsources, name, null);
154: return;
155: }
156:
157: // Conversion to canonical path was here.
158: org.openlaszlo.iv.flash.api.FlashDef def = null;
159:
160: File dirfile = mEnv.getApplicationFile().getParentFile();
161: File appdir = dirfile != null ? dirfile : new File(".");
162: mLogger.debug("appdir is: " + appdir + ", LPS.HOME() is: "
163: + LPS.HOME());
164: //File appHomeParent = new File(LPS.getHomeParent());
165: String sHome = LPS.HOME();
166: // relativePath will perform canonicalization if needed, placing
167: // the current dir in front of a path fragment.
168: String arPath = FileUtils.relativePath(inputFile, appdir);
169: String srPath = FileUtils.relativePath(inputFile, sHome);
170: String relPath;
171: String pType;
172: if (arPath.length() <= srPath.length()) {
173: pType = "ar";
174: relPath = arPath;
175: } else {
176: pType = "sr";
177: relPath = srPath;
178: }
179: // make it relative and watch out, it comes back canonicalized with forward slashes.
180: // Comparing to file.separator is wrong on the pc.
181: if (relPath.charAt(0) == '/') {
182: relPath = relPath.substring(1);
183: }
184: mLogger.debug("relPath is: " + relPath);
185:
186: StringBuffer sbuf = new StringBuffer("LzResourceLibrary."
187: + name + "={ptype: \"" + pType + "\", frames:[");
188: sbuf.append("'" + relPath + "'");
189:
190: Resource res = (Resource) mResourceMap
191: .get(inputFile.toString());
192: boolean oldRes = (res != null);
193: if (!oldRes) {
194: // Get the resource and put in the map
195: res = getResource(inputFile.toString(), name);
196: mLogger.debug("Building resource map; res: " + res
197: + ", relPath: " + relPath + ", fileName: "
198: + inputFile.toString());
199: mResourceMap.put(relPath, res); //[pga] was fileName
200: def = res.getFlashDef();
201: def.setName(name);
202: } else {
203: def = res.getFlashDef();
204: // Add an element with 0 size, since it's already there.
205: Element elt = new Element("resource");
206: elt.setAttribute("name", name);
207: // elt.setAttribute("mime-type", MimeType.MP3);
208: elt.setAttribute("source", relPath); //[pga] was fileName
209: elt.setAttribute("filesize", "0");
210: mInfo.addContent(elt);
211: }
212:
213: Rectangle2D b = def.getBounds();
214:
215: if (b != null) {
216: sbuf.append("],width:" + (b.getWidth() / 20));
217: sbuf.append(",height:" + (b.getHeight() / 20));
218: } else {
219: // could be an mp3 resource
220: sbuf.append("]");
221: }
222: sbuf.append("};");
223: addScript(sbuf.toString());
224: }
225:
226: public void importResource(List sources, String sResourceName,
227: File parent) {
228: writeResourceLibraryDescriptor(sources, sResourceName, parent);
229: }
230:
231: /* Write resource descriptor library */
232: public void writeResourceLibraryDescriptor(List sources,
233: String sResourceName, File parent) {
234: mLogger
235: .debug("Constructing resource library: "
236: + sResourceName);
237: int width = 0;
238: int height = 0;
239: int fNum = 0;
240: String sep = "";
241: File dirfile = mEnv.getApplicationFile().getParentFile();
242: String appdir = dirfile != null ? dirfile.getPath() : ".";
243: mLogger.debug("appdir is: " + appdir + ", LPS.HOME() is: "
244: + LPS.HOME());
245: //File appHomeParent = new File(LPS.getHomeParent());
246: String sHome = LPS.HOME();
247: boolean first = true;
248: // Initialize the temporary buffer.
249: StringBuffer sbuf = new StringBuffer("");
250: if (sources.isEmpty()) {
251: return;
252: }
253: String pType;
254: String relPath;
255: String arPath;
256: String srPath;
257: for (Iterator e = sources.iterator(); e.hasNext();) {
258: File fFile = new File((String) e.next());
259: arPath = FileUtils.relativePath(fFile, appdir);
260: srPath = FileUtils.relativePath(fFile, sHome);
261: // TODO: Some of this should be rolled out of the loop.
262: if (arPath.length() <= srPath.length()) {
263: pType = "ar";
264: relPath = arPath;
265: } else {
266: pType = "sr";
267: relPath = srPath;
268: }
269: // make it relative
270: if (relPath.charAt(0) == '/') {
271: relPath = relPath.substring(1);
272: }
273:
274: if (first == true) {
275: sbuf.append("LzResourceLibrary." + sResourceName
276: + "={ptype: \"" + pType + "\", frames:[");
277: first = false;
278: }
279:
280: mLogger.debug("relFile is: " + relPath);
281:
282: sbuf.append(sep + "'" + relPath + "'");
283: sep = ",";
284: // Definition to add to the library (without stop)
285: Resource res = getMultiFrameResource(fFile.toString(),
286: sResourceName, fNum);
287: int rw = res.getWidth();
288: int rh = res.getHeight();
289: if (rw > width) {
290: width = rw;
291: }
292: if (rh > height) {
293: height = rh;
294: }
295: fNum++;
296: }
297: mMultiFrameResourceSet.add(new Resource(sResourceName, width,
298: height));
299: sbuf.append("],width:" + width);
300: sbuf.append(",height:" + height);
301: sbuf.append("};");
302: addScript(sbuf.toString());
303: }
304:
305: public void close() throws IOException {
306: //Should we emit javascript or SWF?
307: //boolean emitScript = mEnv.isDHTML();
308:
309: if (mCloseCalled) {
310: throw new IllegalStateException(
311: "DHTMLWriter.close() called twice");
312: }
313:
314: boolean debug = mProperties.getProperty("debug", "false")
315: .equals("true");
316:
317: // This indicates whether the user's source code already manually invoked
318: // <debug> to create a debug window. If they didn't explicitly call for
319: // a debugger window, instantiate one now by passing 'true' to __LzDebug.startDebugWindow()
320: boolean makedebugwindow = !mEnv
321: .getBooleanProperty(mEnv.USER_DEBUG_WINDOW);
322:
323: // Bring up a debug window if needed.
324: if (debug && makedebugwindow) {
325: addScript("__LzDebug.makeDebugWindow()");
326: }
327:
328: // Tell the canvas we're done loading.
329: addScript("canvas.initDone()");
330:
331: try {
332: Properties props = (Properties) mProperties.clone();
333: scriptWriter.close();
334: byte[] objcode = ScriptCompiler.compileToByteArray(
335: scriptBuffer.toString(), props);
336: InputStream input = new ByteArrayInputStream(objcode);
337: mLogger.debug("compiled DHTML code is "
338: + new String(objcode));
339: FileUtils.send(input, mStream);
340: } catch (org.openlaszlo.sc.CompilerException e) {
341: throw new CompilationError(e);
342: } catch (Exception e) {
343: throw new ChainedException(e);
344: }
345:
346: mCloseCalled = true;
347: }
348:
349: public void openSnippet(String url) throws IOException {
350: this .liburl = url;
351: }
352:
353: public void closeSnippet() throws IOException {
354: // Callback to let library know we're done loading
355: addScript("LzLibrary.__LZsnippetLoaded('" + this .liburl + "')");
356:
357: if (mCloseCalled) {
358: throw new IllegalStateException(
359: "DHTMLWriter.close() called twice");
360: }
361:
362: try {
363: Properties props = (Properties) mProperties.clone();
364: scriptWriter.close();
365: byte[] objcode = ScriptCompiler.compileToByteArray(
366: scriptBuffer.toString(), props);
367: InputStream input = new ByteArrayInputStream(objcode);
368: mLogger.debug("compiled DHTML code is "
369: + new String(objcode));
370: FileUtils.send(input, mStream);
371: } catch (org.openlaszlo.sc.CompilerException e) {
372: throw new CompilationError(e);
373: } catch (Exception e) {
374: throw new ChainedException(e);
375: }
376:
377: mCloseCalled = true;
378: }
379:
380: /* [todo 2006-02-09 hqm] These methods are to be compatible with
381: SWF font machinery -- this should get factored away someday so that the FontCompiler
382: doesn't try to do anything with <font> tags in DHTML, (except maybe make aliases for them?)
383: */
384: FontManager getFontManager() {
385: // mEnv.warn("DHTML runtime doesn't support FontManager API");
386: return null;
387: }
388:
389: public boolean isDeviceFont(String face) {
390: return true;
391: }
392:
393: public void setDeviceFont(String face) {
394: }
395:
396: public void setFontManager(FontManager fm) {
397: }
398:
399: public void importFontStyle(String fileName, String face,
400: String style, CompilationEnvironment env)
401: throws FileNotFoundException, CompilationError {
402: env.warn("DHTMLWriter does not support importing fonts");
403: }
404:
405: void addPreloaderScript(String script) {
406: };
407:
408: void addPreloader(CompilationEnvironment env) {
409: };
410:
411: public void importBaseLibrary(String library,
412: CompilationEnvironment env) {
413: env.warn("DHTMLWriter does not implement importBaseLibrary");
414: }
415:
416: public String importClickResource(File file)
417: throws ImportResourceError {
418: mEnv.warn("clickregion not implemented by DHTMLWriter");
419: return ("DHTMLWriter clickregions not implemented");
420: }
421:
422: /** Get a resource descriptor without resource content.
423: *
424: * @param name name of the resource
425: * @param fileName file name of the resource
426: * @param stop include stop action if true
427: *
428: */
429: // TODO: Required for performance improvement. So far not differentiated from ObjectWriter version.
430: protected Resource getResource(String fileName, String name,
431: boolean stop) throws ImportResourceError {
432: try {
433: String inputMimeType = MimeType.fromExtension(fileName);
434: if (!Transcoder.canTranscode(inputMimeType, MimeType.SWF)
435: && !inputMimeType.equals(MimeType.SWF)) {
436: inputMimeType = Transcoder
437: .guessSupportedMimeTypeFromContent(fileName);
438: if (inputMimeType == null || inputMimeType.equals("")) {
439: throw new ImportResourceError(fileName,
440: new Exception(
441: /* (non-Javadoc)
442: * @i18n.test
443: * @org-mes="bad mime type"
444: */
445: org.openlaszlo.i18n.LaszloMessages
446: .getMessage(ObjectWriter.class
447: .getName(), "051018-549")),
448: mEnv);
449: }
450: }
451: // No need to get these from the cache since they don't need to be
452: // transcoded and we usually keep the cmcache on disk.
453: if (inputMimeType.equals(MimeType.SWF)) {
454:
455: long fileSize = FileUtils.getSize(new File(fileName));
456:
457: Element elt = new Element("resource");
458: elt.setAttribute("name", name);
459: elt.setAttribute("mime-type", inputMimeType);
460: elt.setAttribute("source", fileName);
461: elt.setAttribute("filesize", "" + fileSize);
462: mInfo.addContent(elt);
463:
464: return importSWF(fileName, name, false);
465: }
466:
467: // TODO: [2002-12-3 bloch] use cache for mp3s; for now we're skipping it
468: // arguably, this is a fixme
469: if (inputMimeType.equals(MimeType.MP3)
470: || inputMimeType.equals(MimeType.XMP3)) {
471: return importMP3(fileName, name);
472: }
473:
474: File inputFile = new File(fileName);
475: File outputFile = mCache.transcode(inputFile,
476: inputMimeType, MimeType.SWF);
477: mLogger.debug(
478: /* (non-Javadoc)
479: * @i18n.test
480: * @org-mes="importing: " + p[0] + " as " + p[1] + " from cache; size: " + p[2]
481: */
482: org.openlaszlo.i18n.LaszloMessages.getMessage(
483: ObjectWriter.class.getName(), "051018-584",
484: new Object[] { fileName, name,
485: new Long(outputFile.length()) }));
486:
487: long fileSize = FileUtils.getSize(outputFile);
488:
489: Element elt = new Element("resource");
490: elt.setAttribute("name", name);
491: elt.setAttribute("mime-type", inputMimeType);
492: elt.setAttribute("source", fileName);
493: elt.setAttribute("filesize", "" + fileSize);
494: mInfo.addContent(elt);
495:
496: return importSWF(outputFile.getPath(), name, stop);
497: } catch (Exception e) {
498: mLogger.error(
499: /* (non-Javadoc)
500: * @i18n.test
501: * @org-mes="Can't get resource " + p[0]
502: */
503: org.openlaszlo.i18n.LaszloMessages.getMessage(
504: ObjectWriter.class.getName(), "051018-604",
505: new Object[] { fileName }));
506: throw new ImportResourceError(fileName, e, mEnv);
507: }
508:
509: }
510:
511: }
|