001: /* ****************************************************************************
002: * XMLCompiler.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.xml.internal;
011:
012: import java.io.File;
013: import java.io.FileReader;
014: import java.io.FileWriter;
015: import java.io.IOException;
016: import java.io.Reader;
017: import java.io.Writer;
018: import java.io.StringBufferInputStream;
019:
020: import java.util.List;
021: import java.util.Stack;
022:
023: import org.jdom.Attribute;
024: import org.jdom.Comment;
025: import org.jdom.Document;
026: import org.jdom.Element;
027: import org.jdom.JDOMException;
028: import org.jdom.input.SAXBuilder;
029:
030: /**
031: * Takes XML in various forms, and serializes it to actionscript, preserving all
032: * heirarchy
033: *
034: * @author Oliver Steele
035: * @author Max Carlson
036: * @version 1.0
037: */
038: public class XMLCompiler {
039:
040: private static Stack counterstack = new Stack();
041: private static Stack basestack = new Stack();
042:
043: private static String bas = "";
044:
045: /**
046: * Compile XML to actionscript
047: *
048: * @param x XML string to compile
049: * @return Actionscript representation of XML
050: */
051: public static String compile(String x) {
052: // Compile a new element from scratch
053: StringBuffer base = new StringBuffer();
054: StringBufferInputStream sis = new StringBufferInputStream(x);
055: SAXBuilder sb = new SAXBuilder();
056: try {
057: Document doc = sb.build(sis);
058: Element e = doc.getRootElement();
059: return compile(e);
060: } catch (Exception ignored) {
061: }
062:
063: return null;
064: }
065:
066: /**
067: * Compile XML to actionscript
068: *
069: * @param e JDOM element to compile
070: * @return Actionscript representation of XML
071: */
072: public static String compile(Element e) {
073: return compile(e, Schema.DEFAULT_SCHEMA);
074: }
075:
076: /**
077: * Compile XML to actionscript
078: *
079: * @param e JDOM element to compile
080: * @param schema Laszlo Schema to follow (what is var, function)
081: * @return Actionscript representation of XML
082: */
083: public static String compile(Element e, Schema schema) {
084: // Compile a new element from scratch
085: StringBuffer base = new StringBuffer();
086: return compile(e, schema, base);
087: }
088:
089: /**
090: * Compile XML to actionscript
091: *
092: * @param e JDOM element to compile
093: * @param schema Laszlo Schema to follow (what is var, function)
094: * @param base Base of current element - used when called
095: * recursively
096: * @return Actionscript representation of XML
097: */
098: public static String compile(Element e, Schema schema,
099: StringBuffer base) {
100: // Default to an empty string for the variable name - use name node
101: // in XML
102: String varname = "";
103: return compile(e, schema, base, varname);
104: }
105:
106: /**
107: * Compile XML to actionscript
108: *
109: * @param e JDOM element to compile
110: * @param schema Laszlo Schema to follow (what is var, function)
111: * @param base Base of current element - used when called
112: * recursively
113: * @param varname Variable name to use instead of element name
114: * @return Actionscript representation of XML
115: */
116: public static String compile(Element e, Schema schema,
117: StringBuffer base, String varname) {
118: StringBuffer out = new StringBuffer();
119:
120: int lastcount = -1;
121:
122: /*
123: // Find shortest available subsring - not necessary because of flash's
124: // name pools
125: String name = (String)shorter.get(nodename);
126: if (name == null) {
127: String chunk = "";
128: for (int i = 1; i < nodename.length(); i++) {
129: chunk = nodename.substring(0, i);
130: if (! shorter.containsKey(nodename) && ! used.containsKey(chunk) ) {
131: shorter.put(nodename, chunk);
132: used.put(chunk, "shorter");
133: break;
134: }
135: }
136: out.append(chunk + " = '" + nodename + "';\n");
137: name = chunk;
138: }
139: */
140:
141: if (base.length() <= 0) {
142: // If we're just starting out, initialize everything
143: counterstack = new Stack();
144: counterstack.push(new Integer(-1));
145: lastcount = counterstack.size();
146:
147: basestack = new Stack();
148: basestack.push(base);
149:
150: bas = "";
151: out
152: .append("function x(n, a, t) {\nvar o = {};\no.n = n;\no.a = a;\no.c = [];\nif (t.length) {o.t = t;}\nreturn o;\n}\n");
153: }
154:
155: //out.append(counterstack.size() + ", " + lastcount + " > ");
156:
157: // Count up one for each unique base name - ensures there is no
158: // namespace collision in Actionscript namespace for elements with same
159: // names
160: Integer in = new Integer(-1);
161: in = (Integer) counterstack.pop();
162: int inum = in.intValue();
163: inum++;
164: in = new Integer(inum);
165:
166: counterstack.push(in);
167:
168: if (base.length() > 0) {
169: base.append("[");
170: // Append the new number to the current base
171: base.append(in);
172: base.append("]");
173: } else {
174: base.append("root");
175: }
176:
177: String name = e.getName();
178: // Create an object to hold sub-values
179: out.append(base + "=x('" + name + "'");
180:
181: // Add each attribute as an element of the 'attrs' object
182: List attributes = e.getAttributes();
183: //if (attributes.size() > 0) {
184: out.append(",{");
185: for (int i = 0; i < attributes.size(); i++) {
186: Attribute a = (Attribute) attributes.get(i);
187:
188: String val = null;
189: Schema.Type type = schema.getAttributeType(e, a.getName());
190: if (type == schema.STRING_TYPE) {
191: val = "'" + escapeQuote(a.getValue()) + "'";
192: } else {
193: // schema.UNKNOWN_TYPE
194: try {
195: float v = a.getFloatValue();
196: val = escapeQuote(a.getValue());
197: } catch (Exception ex) {
198: val = "'" + escapeQuote(a.getValue()) + "'";
199: }
200: }
201:
202: out.append(a.getName() + ":" + val);
203:
204: if (i < attributes.size() - 1) {
205: out.append(",");
206: }
207: }
208: out.append("}");
209: //}
210:
211: // Add textual data to 't' node if available
212: String text = e.getTextTrim();
213: if ((text != null) && (text.length() > 0)) {
214: out.append(",'" + escapeQuote(text) + "');\n");
215: } else {
216: out.append(");\n");
217: }
218:
219: // Add each child element to the children object
220: List nestedElements = e.getChildren();
221:
222: if (nestedElements.size() > 0) {
223: counterstack.push(new Integer(-1));
224:
225: for (int i = 0; i < nestedElements.size(); i++) {
226: Element c = (Element) nestedElements.get(i);
227: if (i > 0) {
228: StringBuffer temp = new StringBuffer(bas);
229: // compile child recursively, add to children object
230: out.append(compile(c, schema, temp));
231: } else {
232: // Set up initial children object to hold child elements
233: basestack.push(bas + "");
234: //out.append("in: " + bas + "\n");
235:
236: base.append(".c");
237: bas = base + "";
238: out.append(compile(c, schema, base));
239: }
240: }
241:
242: if (counterstack.size() != lastcount) {
243: counterstack.pop();
244: bas = (String) basestack.pop();
245: //out.append("out: " + base + " : " + basestack.size() + ", " + lastcount + "\n");
246: lastcount = counterstack.size();
247: }
248: }
249:
250: return out.toString();
251: }
252:
253: /*
254: /**
255: * A utility class to allow execution from the command line
256: *
257: * @param args[] a series of 3 arguments - [XML input document],
258: * [variable name], [AS output document]
259: public static void main(String[] args) {
260: if (args.length != 3) {
261: System.out.println("Usage: XMLCompiler " +
262: "[XML input document] " + "[variable name] " + "[AS output document]");
263: System.exit(0);
264: }
265:
266: try {
267: // Create and load properties
268: System.out.println("Reading XML from " + args[0]);
269:
270: // Load XML into JDOM Document
271: SAXBuilder builder = new SAXBuilder();
272: FileReader r = new FileReader(args[0]);
273: Document doc = builder.build( r );
274:
275: System.out.println("\n\n---- Converting to Actionscript ----");
276:
277: Schema schema = Schema.DEFAULT_SCHEMA;
278: StringBuffer out = new StringBuffer();
279:
280: // recurse on children - should be only one root element...
281: List nestedElements = doc.getRootElement().getChildren();
282: for (int i = 0; i < nestedElements.size(); i++) {
283: Element c = (Element)nestedElements.get(i);
284: out.append( compile(c, schema, new StringBuffer(), args[1]) );
285: }
286:
287:
288: System.out.println("\n\n---- Writing to file ----");
289: FileWriter w = new FileWriter(args[2]);
290: w.write(out + "");
291: w.flush();
292: w.close();
293:
294: System.out.println(out);
295:
296: } catch (Exception e) {
297: e.printStackTrace();
298: }
299: }
300: */
301:
302: /**
303: * Escapes strings for inclusion inside actionscript strings,
304: * ex: ' to \'
305: *
306: * @param s string to escape
307: * @return an escaped string
308: */
309: private static String escapeQuote(String s) {
310: String retvalue = s;
311: if (s.indexOf("'") != -1 || s.indexOf("\"") != -1
312: || s.indexOf("\\") != -1) {
313: StringBuffer hold = new StringBuffer();
314: char c;
315: for (int i = 0; i < s.length(); i++) {
316: if ((c = s.charAt(i)) == '\'') {
317: hold.append("\\'");
318: } else if ((c = s.charAt(i)) == '\"') {
319: hold.append("\\\"");
320: } else if ((c = s.charAt(i)) == '\\') {
321: hold.append("\\\\");
322: } else {
323: hold.append(c);
324: }
325: }
326: retvalue = hold.toString();
327: }
328: return retvalue;
329: }
330: }
|