001: /* ****************************************************************************
002: * CompilerUtils.java
003: * ****************************************************************************/
004:
005: /* J_LZ_COPYRIGHT_BEGIN *******************************************************
006: * Copyright 2001-2006 Laszlo Systems, Inc. All Rights Reserved. *
007: * Use is subject to license terms. *
008: * J_LZ_COPYRIGHT_END *********************************************************/
009:
010: package org.openlaszlo.compiler;
011:
012: import org.openlaszlo.css.CSSParser;
013: import org.openlaszlo.sc.Function;
014: import org.openlaszlo.sc.ScriptCompiler;
015: import org.openlaszlo.utils.ChainedException;
016:
017: import java.io.*;
018: import java.util.*;
019: import org.jdom.Element;
020: import org.apache.log4j.*;
021: import java.text.DecimalFormat;
022: import java.text.FieldPosition;
023:
024: public class CompilerUtils {
025: /** Return a string that can be included in a script to tell the
026: * script compiler that subsequent lines should be numbered
027: * relative to the beginning or end position of the source text
028: * for this element.
029: *
030: * @param start true if the location should be relative to the
031: * start of the element (otherwise it is relative to the end)
032: */
033: public static String sourceLocationDirective(Element elt,
034: boolean start) {
035: // Parser adds these attributes.
036: String pathname = Parser.getSourceMessagePathname(elt);
037: Integer lineno = Parser.getSourceLocation(elt, Parser.LINENO,
038: start);
039: Integer colno = Parser.getSourceLocation(elt, Parser.COLNO,
040: start);
041: return sourceLocationDirective(pathname, lineno, colno);
042: }
043:
044: /** Return a string that can be used as a unique name for the
045: * element for compiler generated function names.
046: *
047: * @param start true if the location should be relative to the
048: * start of the element (otherwise it is relative to the end)
049: */
050: public static String sourceUniqueName(Element elt, boolean start) {
051: // Parser adds these attributes.
052: String pathname = Parser.getSourceMessagePathname(elt);
053: Integer lineno = Parser.getSourceLocation(elt, Parser.LINENO,
054: start);
055: Integer colno = Parser.getSourceLocation(elt, Parser.COLNO,
056: start);
057: // When parsing with crimson, the column number isn't defined.
058: // TODO: [2004-11-10] This won't generate unique names, so it
059: // will fail with krank. Krank isn't used in this environment
060: // so that's okay for now.
061: if (colno.intValue() < 0)
062: colno = new Integer(0);
063:
064: if (pathname == null) {
065: pathname = "unknown_file";
066: } else {
067: pathname = encodeJavaScriptIdentifier(pathname);
068: }
069: return "$" + pathname + '_' + lineno + '_' + colno;
070: }
071:
072: /** Returns a string that is a valid JavaScript identifier.
073: * Characters in the input string that are not valid in a
074: * JavaScript identifier are replaced by "$xx", where xx is the
075: * hex code of the character. '$' is also replaced. This is
076: * similar to URL encoding, except '$' is used as the quote
077: * character. */
078: public static String encodeJavaScriptIdentifier(String s) {
079: StringBuffer buffer = new StringBuffer();
080: for (int i = 0; i < s.length(); i++) {
081: char c = s.charAt(i);
082: // Java and JavaScript identifiers have the same lexical
083: // specification, so the Java methods work for this
084: if (!(Character.isJavaIdentifierPart(c) || (i == 0 && Character
085: .isJavaIdentifierStart(c)))
086: || c == '$') {
087: String hex = Integer.toHexString((int) c);
088: if (hex.length() < 2)
089: hex = "0" + hex;
090: buffer.append('$');
091: buffer.append(hex.toUpperCase());
092: } else {
093: buffer.append(c);
094: }
095: }
096: return buffer.toString();
097: }
098:
099: /* TODO: [2002-12-20 hqm] we need a better way to locate where
100: * an attribute occurs in a source file.
101: */
102: public static String attributeLocationDirective(Element elt,
103: String attrname) {
104: return sourceLocationDirective(elt, true);
105: }
106:
107: /** TODO: [2003-03-14 ptw] ditto */
108: public static String attributeUniqueName(Element elt,
109: String attrname) {
110: return sourceUniqueName(elt, true);
111: }
112:
113: /** Return a string that can be included in a script to tell the
114: * script compiler that subsequent lines should be numbered
115: * relative to the beginning or end position of the source text
116: * for this element.
117: *
118: */
119: public static String sourceLocationDirective(String pathname,
120: Integer lineno, Integer colno) {
121: StringBuffer buffer = new StringBuffer();
122: if (pathname != null) {
123: buffer.append("\n#file " + pathname + "\n");
124: }
125: if (lineno != null) {
126: // Set the line number of the following line. Prepend a
127: // line feed, in case this directive is being added after
128: // other text.
129: if (buffer.length() == 0) {
130: buffer.append('\n');
131: }
132: buffer.append("#line " + lineno + "\n");
133: // Add enough spaces to line the column of the following
134: // text up to the same position it had within the source
135: // text, so that the column numbers in source reporting
136: // are correct.
137: if (colno != null) {
138: for (int i = colno.intValue(); i > 0; --i) {
139: buffer.append(' ');
140: }
141: }
142: }
143: return buffer.toString();
144: }
145:
146: public static String sourceLocationPrettyString(Element elt) {
147: // Parser adds these attributes.
148: String pathname = Parser.getSourceMessagePathname(elt);
149: Integer lineno = Parser.getSourceLocation(elt, Parser.LINENO,
150: true);
151: Integer colno = Parser.getSourceLocation(elt, Parser.COLNO,
152: true);
153: return pathname + ":" + lineno + ":" + colno;
154: }
155:
156: /** Returns true if the argument is at the top level of an lzx
157: * program: it is immediately within a canvas element, or within a
158: * library element that is itself at the top level. */
159: static boolean isAtToplevel(Element element) {
160: // To simplify the implementation, root canvas and library
161: // elements are also considered to be at the top level, as is
162: // a canvas inside a top level element.
163: Element parent = element.getParentElement();
164: return parent == null
165: || (ToplevelCompiler.isElement(parent) && isAtToplevel(parent));
166: }
167: }
|