001: /* ****************************************************************************
002: * DataCommon.java
003: *
004: * Compile XML directly to SWF bytecodes.
005: * ****************************************************************************/
006:
007: /* J_LZ_COPYRIGHT_BEGIN *******************************************************
008: * Copyright 2001-2006 Laszlo Systems, Inc. All Rights Reserved. *
009: * Use is subject to license terms. *
010: * J_LZ_COPYRIGHT_END *********************************************************/
011:
012: package org.openlaszlo.xml.internal;
013:
014: import java.io.*;
015: import java.util.*;
016:
017: import org.openlaszlo.iv.flash.util.*;
018: import org.openlaszlo.iv.flash.api.action.*;
019: import org.openlaszlo.iv.flash.api.*;
020: import org.openlaszlo.compiler.CompilationError;
021: import org.openlaszlo.utils.ChainedException;
022: import org.openlaszlo.utils.FileUtils;
023: import org.openlaszlo.utils.HashIntTable;
024:
025: import org.apache.log4j.*;
026:
027: /**
028: * Common data compiler routines between DataCompiler (XML compiler) and DataBuilder (Java API)
029: *
030: * @author Henry Minsky
031: * @version 1.0
032: */
033: public abstract class DataCommon {
034: /* Logger */
035: static public String ROOT_NODE_INSTANTIATOR_FN = "_rootndi";
036: static public String RESULTSET_NODE_INSTANTIATOR_FN = "_resultsetndi";
037: static public String BODY_NODE_INSTANTIATOR_FN = "_bodyndi";
038: static public String NODE_INSTANTIATOR_FN = "_m";
039: static public String TEXT_INSTANTIATOR_FN = "_t";
040: static public String ROOT_NODE_FINAL_FN = "_finishndi";
041:
042: /**
043: * Push a reference to string data on stack. maybeInternString
044: * uses a heuristic to decide whether to add the string constant
045: * pool, so this may push a string literal or a dictionary ref.
046: */
047: static public final void pushStringData(String s, FlashBuffer body,
048: DataContext dc) {
049: int idx = maybeInternString(s, dc);
050: if (idx != -1) {
051: //System.out.println("intern IDX '"+s+"'="+idx);
052: // PUSH idx
053: body.writeByte(Actions.PushData);
054: body.writeWord(2);
055: writeOffset(idx, body);
056: } else {
057: // System.out.println("intern LITERAL '"+s+"'");
058: // PUSH literal attrname
059: // {0x96, lenlo, lenhi, 0x00, char, char, char, ...0x00, }
060: body.writeByte(Actions.PushData);
061: body.writeWord(getByteLength(s, dc.encoding) + 2); // account for type code and zero terminator
062: body.writeByte(0); // type code 0 ; null terminated string type
063: body.writeStringZ(s, dc.encoding);
064: }
065: }
066:
067: static public int getByteLength(String s, String encoding) {
068: try {
069: byte buf[] = s.getBytes(encoding);
070: return buf.length;
071: } catch (UnsupportedEncodingException e) {
072: throw new RuntimeException(
073: "Got UnsupportedEncodingException for charset "
074: + encoding + " on string '" + s + "': "
075: + e.getMessage());
076: }
077: }
078:
079: /**
080: * Push a reference to string data on stack. maybeInternString
081: * uses a heuristic to decide whether to add the string constant
082: * pool, so this may push a string literal or a dictionary ref.
083: *
084: * This version of the method uses the faster _writeXXX() methods,
085: * which do not do bounds checks on the FlashBuffer. So this should only be
086: * used in situations where we know the FlashBuffer is already allocated with
087: * enough space for any data we may push.
088: */
089: static public final void _pushStringData(String s,
090: FlashBuffer body, DataContext dc) {
091: int idx = maybeInternString(s, dc);
092: if (idx != -1) {
093: //System.out.println("intern IDX '"+s+"'="+idx);
094: // PUSH idx
095: body._writeByte(Actions.PushData);
096: body._writeWord(2);
097: writeOffset(idx, body);
098: } else {
099: // System.out.println("intern LITERAL '"+s+"'");
100: // PUSH literal attrname
101: // {0x96, lenlo, lenhi, 0x00, char, char, char, ...0x00, }
102: body._writeByte(Actions.PushData);
103: body._writeWord(getByteLength(s, dc.encoding) + 2); // account for type code and zero terminator
104: body._writeByte(0); // type code 0 ; null terminated string type
105: body._writeStringZ(s, dc.encoding);
106: }
107: }
108:
109: /**
110: * Push a reference to string data on stack. maybeInternString
111: * uses a heuristic to decide whether to add the string constant
112: * pool, so this may push a string literal or a dictionary ref.
113: *
114: *<p>
115: *
116: * This method is used when composing the arguments for a PUSH
117: * merges several args on the stack with a single PUSH
118: * instruction, and writes out only the data or constant-pool
119: * index reference, as opposed to the pushStringData() method
120: * which also writes out the PUSH instruction itsef.
121: *
122: */
123:
124: static public final void pushMergedStringData(String s,
125: FlashBuffer body, DataContext dc) {
126: int idx = maybeInternString(s, dc);
127: if (idx != -1) {
128: //System.out.println("intern IDX '"+s+"'="+idx);
129: writeOffset(idx, body);
130: } else {
131: // System.out.println("intern LITERAL '"+s+"'");
132: body.writeByte(0); // type code 0 ; null terminated
133: // string type
134: body.writeStringZ(s, dc.encoding);
135: }
136: }
137:
138: /**
139: * Push a reference to string data on stack. maybeInternString
140: * uses a heuristic to decide whether to add the string constant
141: * pool, so this may push a string literal or a dictionary ref.
142: *
143: *<p>
144: *
145: *
146: * This version of the method uses the faster _writeXXX() methods,
147: * which do not do bounds checks on the FlashBuffer. So this should only be
148: * used in situations where we know the FlashBuffer is already allocated with
149: * enough space for any data we may push.
150: */
151:
152: static public final void _pushMergedStringData(String s,
153: FlashBuffer body, DataContext dc) {
154: int idx = maybeInternString(s, dc);
155: if (idx != -1) {
156: //System.out.println("intern IDX '"+s+"'="+idx);
157: _writeOffset(idx, body);
158: } else {
159: // System.out.println("intern LITERAL '"+s+"'");
160: body._writeByte(0); // type code 0 ; null terminated
161: // string type
162: body._writeStringZ(s, dc.encoding);
163: }
164: }
165:
166: /**
167: * Push a reference to string data on stack. Always attempts to intern in the string
168: * pool, so this may push a string literal or a dictionary ref.
169: *
170: * <p>
171: *
172: * This method is designed to be called when pushing references to strings
173: * which we are pretty sure will occur again, such as tag names or attribute names.
174: * We try to put them into the string pool immediately, as opposed to using the
175: * heuristic of waiting until the string is seen again before interning it.
176: *
177: */
178: static public final void pushMergedStringDataSymbol(String s,
179: FlashBuffer body, DataContext dc) {
180: int idx = internString(s, dc);
181: if (idx != -1) {
182: //System.out.println("intern IDX '"+s+"'="+idx);
183: writeOffset(idx, body);
184: } else {
185: // System.out.println("intern LITERAL '"+s+"'");
186: body.writeByte(0); // type code 0 ; null terminated
187: // string type
188: body.writeStringZ(s, dc.encoding);
189: }
190: }
191:
192: /**
193: * Push a reference to string data on stack. Attempts to intern in the string pool
194: * pool, so this may push a string literal or a dictionary ref. This routine uses the _writeXXX routines,
195: * which bypass FlashBuffer's ensureCapacity(), so the FlashBuffer must have been allocated with enough
196: * space for these writes, or an ArrayBoundsException may happen.
197: *
198: */
199: static public final void _pushMergedStringDataSymbol(String s,
200: FlashBuffer body, DataContext dc) {
201: int idx = internString(s, dc);
202: if (idx != -1) {
203: //System.out.println("intern IDX '"+s+"'="+idx);
204: writeOffset(idx, body);
205: } else {
206: // System.out.println("intern LITERAL '"+s+"'");
207: body._writeByte(0); // type code 0 ; null terminated
208: // string type
209: body._writeStringZ(s, dc.encoding);
210: }
211: }
212:
213: /**
214: * Copy data from a a byte array into a FlashBuffer
215: */
216: static public final void writeFlashData(FlashBuffer body,
217: byte[] data, int destPos) {
218: //arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
219: byte[] buf = body.getBuf();
220: System.arraycopy(data, 0, buf, destPos, data.length);
221: body.setPos(destPos + data.length);
222: }
223:
224: /**
225: * Return byte array containing the strings from the constant pool, sorted and zero terminated.
226: */
227: final static public byte[] makeStringPool(DataContext dc) {
228: HashIntTable pool = dc.cpool;
229: // Array for sorting
230: int nstrings = pool.size();
231: //System.out.println("makeStringPool: nstrings = "+nstrings);
232: if (nstrings > (1 << 16 - 1)) {
233: throw new RuntimeException(
234: "more than 64k strings in constant pool");
235: }
236: String[] sortArray = new String[nstrings];
237: Enumeration keys = pool.keys();
238: int poolsize = 0;
239: // Sort strings by numerical index
240: while (keys.hasMoreElements()) {
241: String key = (String) keys.nextElement();
242: int v = pool.get(key);
243: //System.out.println("sorting: key="+key+", index="+v);
244: sortArray[v] = key;
245: poolsize += (getByteLength(key, dc.encoding) + 1); // account for zero terminator
246: }
247:
248: byte pooldata[] = new byte[poolsize];
249: int pos = 0;
250: for (int i = 0; i < nstrings; i++) {
251: String s = sortArray[i];
252: //System.out.println("s="+s);
253: // Convert to byte values in the desired charset encoding
254: byte chars[];
255: try {
256: chars = s.getBytes(dc.encoding);
257: } catch (UnsupportedEncodingException e) {
258: chars = s.getBytes();
259: }
260: int len = chars.length;
261: //arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
262: System.arraycopy(chars, 0, pooldata, pos, len);
263: pos += len;
264: pooldata[pos++] = 0; // null terminate the string
265: }
266: return pooldata;
267: }
268:
269: /**
270: * Intern a string in the constant pool.
271: * @param s the string
272: * @param dc
273: * @return offset into string pool
274: */
275: static public final int addStringConstant(String s, DataContext dc) {
276: int size = dc.cpool.size();
277: dc.cpool.put(s, size);
278: dc.pool_data_length += (getByteLength(s, dc.encoding) + 1);
279: return size;
280: }
281:
282: /**
283: * @return the index of the string in the constant pool.
284: */
285: static public final int getStringIndex(String s, DataContext dc) {
286: return dc.cpool.get(s);
287: }
288:
289: /**
290: * Intern a string in the constant pool, but only if it has been seen once before.
291: * @param s the string
292: * @param dc
293: * @return the offset into string pool, or -1 if we decided not to intern it yet
294: */
295: static public final int maybeInternString(String s, DataContext dc) {
296: int idx = dc.cpool.get(s);
297: if (idx >= 0) {
298: return idx;
299: }
300: if (dc.pool_data_length >= 65532) {
301: return -1;
302: }
303: boolean seen = dc.cpool_first.containsKey(s);
304: if (seen) {
305: int size = dc.cpool.size();
306: // We can only have 64k of data (including zero terminators on strings)
307: dc.pool_data_length += (getByteLength(s, dc.encoding) + 1);
308: // 65536 - 4 bytes for # strings
309: if (dc.pool_data_length >= 65532) {
310: return -1;
311: }
312: dc.cpool.put(s, size);
313: return size;
314: } else {
315: dc.cpool_first.put(s, 1);
316: return (-1);
317: }
318: }
319:
320: /**
321: * Intern a string in the constant pool
322: * @param s the string
323: * @param dc
324: * @return the offset into string pool, or -1 if the string pool is full
325: */
326: static public final int internString(String s, DataContext dc) {
327: int idx = dc.cpool.get(s);
328: if (idx >= 0) {
329: return idx;
330: }
331: if (dc.pool_data_length >= 65532) {
332: return -1;
333: }
334: int size = dc.cpool.size();
335: // We can only have 64k of data (including zero terminators on strings)
336: dc.pool_data_length += (getByteLength(s, dc.encoding) + 1);
337: // 65536 - 4 bytes for # strings
338: if (dc.pool_data_length >= 65532) {
339: return -1;
340: }
341: dc.cpool.put(s, size);
342: return size;
343: }
344:
345: /**
346: * Writes a string offset from the constant pool to a flash buffer
347: *
348: * @param o dictionary string index to write to flash buffer
349: */
350: static public final void writeOffset(int o, FlashBuffer fb) {
351: if (o > 255) {
352: fb.writeByte(9);
353: fb.writeWord(o);
354: } else {
355: fb.writeByte(8);
356: fb.writeByte(o);
357: }
358: }
359:
360: /**
361: * Writes a string offset from the constant pool to a flash buffer. This uses _writeXXX
362: * which bypasses the FlashBuffer ensureCapacity() check (for speed) so you must make sure
363: * the FlashBuffer is large enough to write these bytes.
364: *
365: * @param o dictionary string index to write to flash buffer
366: */
367: static public final void _writeOffset(int o, FlashBuffer fb) {
368: if (o > 255) {
369: fb._writeByte(9);
370: fb._writeWord(o);
371: } else {
372: fb._writeByte(8);
373: fb._writeByte(o);
374: }
375: }
376:
377: /**
378: * For debugging, to print out a Program as hex values.
379: */
380:
381: static public void printProgram(Program program) {
382: //program.printContent(System.out, " ");
383: FlashBuffer body = program.body();
384: byte[] buf = body.getBuf();
385: System.out.print("char data[] = {\n ");
386: for (int i = 0; i < body.getSize(); i++) {
387: if (i % 10 == 0) {
388: System.out.print("\n ");
389: }
390: if (i < buf.length - 1) {
391: System.out.print("(byte) 0x"
392: + Integer.toHexString((buf[i] >> 4) & 0xf)
393: + Integer.toHexString(buf[i] & 0xf) + ", ");
394: } else {
395: System.out.print("(byte) 0x"
396: + Integer.toHexString((buf[i] >> 4) & 0xf)
397: + Integer.toHexString(buf[i] & 0xf));
398: }
399: }
400: System.out.println("\n};");
401: }
402: }
|