001: // Copyright (c) 2002, 2003 Per M.A. Bothner.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.kawa.xslt;
005:
006: import gnu.lists.*;
007: import gnu.math.*;
008: import gnu.bytecode.*;
009: import gnu.expr.*;
010: import gnu.text.*;
011: import gnu.mapping.*;
012: import java.util.Stack;
013: import gnu.xml.*;
014: import gnu.kawa.xml.*;
015: import gnu.xquery.lang.*;
016: import gnu.kawa.functions.AppendValues;
017:
018: /** Translate an XSLT stylesheet to a Kawa Expression tree. */
019:
020: public class XslTranslator extends Lexer implements Consumer {
021: boolean inTemplate;
022: Declaration consumerDecl;
023: StringBuffer nesting = new StringBuffer(100);
024: ModuleExp mexp;
025: Compilation comp;
026:
027: /** We seen a startAttribute but not the closing endAttribute. */
028: boolean inAttribute;
029: /** The 'attribute type' from the most recent startAttribute. */
030: Object attributeType;
031: /** Buffer to acumulate the value of the current attribute. */
032: StringBuffer attributeValue = new StringBuffer(100);
033:
034: XSLT interpreter;
035: InPort in;
036:
037: /** Non-null if we're inside an xsl:template. */
038: LambdaExp templateLambda;
039:
040: XslTranslator(InPort inp, gnu.text.SourceMessages messages,
041: XSLT interpreter) {
042: super (inp, messages);
043: this .interpreter = interpreter;
044: this .in = inp;
045: }
046:
047: static final String XSL_TRANSFORM_URI = "http://www.w3.org/1999/XSL/Transform";
048:
049: public String popMatchingAttribute(String ns, String name, int start) {
050: int size = comp.exprStack.size();
051: for (int i = start; i < size; i++) {
052: Object el = comp.exprStack.elementAt(start);
053: if (!(el instanceof ApplyExp))
054: return null;
055: ApplyExp aexp = (ApplyExp) el;
056: Expression function = aexp.getFunction();
057: if (aexp.getFunction() != MakeAttribute.makeAttributeExp)
058: return null;
059: Expression[] args = aexp.getArgs();
060: if (args.length != 2)
061: return null;
062: Expression arg0 = args[0];
063: if (!(arg0 instanceof QuoteExp))
064: return null;
065: Object tag = ((QuoteExp) arg0).getValue();
066: if (!(tag instanceof Symbol))
067: return null;
068: Symbol stag = (Symbol) tag;
069: if (stag.getLocalPart() == name
070: && stag.getNamespaceURI() == ns) {
071: comp.exprStack.removeElementAt(i);
072: return (String) ((QuoteExp) args[1]).getValue();
073: }
074: }
075: return null;
076: }
077:
078: Expression popTemplateBody(int start) {
079: // should strip off attributes?
080: int i = comp.exprStack.size() - start;
081: Expression exp;
082: Expression[] args = new Expression[i];
083: while (--i >= 0)
084: args[i] = (Expression) comp.exprStack.pop();
085: return new ApplyExp(AppendValues.appendValues, args);
086: }
087:
088: public static String isXslTag(Object type) {
089: if (type instanceof QuoteExp)
090: type = ((QuoteExp) type).getValue();
091: if (!(type instanceof Symbol))
092: return null;
093: Symbol qname = (Symbol) type;
094: if (qname.getNamespaceURI() != XSL_TRANSFORM_URI)
095: return null;
096: return qname.getLocalName();
097: }
098:
099: void append(Expression expr) {
100: // FIXME
101: }
102:
103: public void startElement(Object type) {
104: String xslTag = isXslTag(type);
105: if (xslTag == "template") {
106: if (templateLambda != null)
107: error("nested xsl:template");
108: templateLambda = new LambdaExp();
109: //templateLambda.setFile(getName());
110: //templateLambda.setLine(declLine, declColumn);
111: }
112: if (type instanceof XName) {
113: // This gets rid of namespace "nodes". That's not really right.
114: // We do want to get rid of xmlns:xsl, though, at least. FIXME.
115: XName xn = (XName) type;
116: type = Symbol.make(xn.getNamespaceURI(), xn.getLocalPart(),
117: xn.getPrefix());
118: }
119: nesting.append((char) comp.exprStack.size());
120: push(type);
121: }
122:
123: public void startAttribute(Object attrType) {
124: if (inAttribute)
125: error('f', "internal error - attribute inside attribute");
126: attributeType = attrType;
127: attributeValue.setLength(0);
128: nesting.append((char) comp.exprStack.size());
129: inAttribute = true;
130: }
131:
132: public void endAttribute() {
133: Expression[] args = new Expression[2];
134: args[0] = new QuoteExp(attributeType);
135: args[1] = new QuoteExp(attributeValue.toString());
136: push(new ApplyExp(MakeAttribute.makeAttributeExp, args));
137: nesting.setLength(nesting.length() - 1);
138: inAttribute = false;
139: }
140:
141: public void endElement() {
142: int nlen = nesting.length() - 1;
143: int start = nesting.charAt(nlen);
144: nesting.setLength(nlen);
145: Expression startTag = (Expression) comp.exprStack
146: .elementAt(start);
147: String xslTag = isXslTag(startTag);
148: if (xslTag == "value-of") {
149: String select = popMatchingAttribute("", "select",
150: start + 1);
151: if (select != null) {
152: Expression exp = interpreter.parseXPath(select,
153: getMessages());
154: exp = new ApplyExp(ClassType.make("gnu.xml.TextUtils")
155: .getDeclaredMethod("stringValue", 1),
156: new Expression[] { exp });
157: comp.exprStack.pop();
158: push(exp);
159: return;
160: }
161: } else if (xslTag == "apply-templates") {
162: String select = popMatchingAttribute("", "select",
163: start + 1);
164: String mode = popMatchingAttribute("", "mode", start + 1);
165: Expression[] args = { new QuoteExp(select),
166: resolveQNameExpression(mode) };
167: comp.exprStack.pop();
168: push(new ApplyExp(new QuoteExp(applyTemplatesProc), args));
169: } else if (xslTag == "if") {
170: String select = popMatchingAttribute("", "test", start + 1);
171: Expression test = interpreter.parseXPath(select,
172: getMessages());
173: test = XQParser.booleanValue(test);
174: Expression clause = popTemplateBody(start + 1);
175: comp.exprStack.pop();
176: push(new IfExp(test, clause, QuoteExp.voidExp));
177: } else if (xslTag == "stylesheet" || xslTag == "transform") {
178: push(new ApplyExp(new QuoteExp(runStylesheetProc),
179: Expression.noExpressions));
180: Expression body = popTemplateBody(start + 1);
181: push(body);
182: mexp.body = body;
183: } else if (xslTag == "template") {
184: String match = popMatchingAttribute("", "match", start + 1);
185: String name = popMatchingAttribute("", "name", start + 1);
186: String priority = popMatchingAttribute("", "priority",
187: start + 1);
188: String mode = popMatchingAttribute("", "mode", start + 1);
189: templateLambda.body = popTemplateBody(start + 1);
190: comp.exprStack.pop();
191: Expression[] args = new Expression[5];
192: double prio = 0.0; // FIXME
193: args[0] = resolveQNameExpression(name);
194: args[1] = new QuoteExp(match);
195: args[2] = new QuoteExp(DFloNum.make(prio));
196: args[3] = resolveQNameExpression(mode);
197: args[4] = templateLambda;
198: push(new ApplyExp(new QuoteExp(defineTemplateProc), args));
199: templateLambda = null;
200: } else {
201: Expression[] args = new Expression[comp.exprStack.size()
202: - start];
203: for (int i = args.length; --i >= 0;)
204: args[i] = (Expression) comp.exprStack.pop();
205: // FIXME does not preserve namespace attributes.
206: Expression exp = new ApplyExp(MakeElement.makeElement, args);
207: push(exp);
208: mexp.body = exp;
209: }
210: }
211:
212: public void write(int v) {
213: if (inAttribute) {
214: /* #ifdef JAVA5 */
215: // attributeValue.appendCodePoint(v);
216: /* #else */
217: attributeValue.append((char) v);
218: /* #endif */
219: } else {
220: String str;
221: if (v < 0x10000)
222: str = String.valueOf(v);
223: else { // Use surrogates.
224: char[] c2 = { (char) (((v - 0x10000) >> 10) + 0xD800),
225: (char) ((v & 0x3FF) + 0xDC00) };
226: str = new String(c2);
227: }
228: push(str);
229: }
230: }
231:
232: /* #ifdef JAVA5 */
233: // public Consumer append (char v)
234: // {
235: // if (inAttribute)
236: // attributeValue.append(v);
237: // else
238: // push(String.valueOf(v));
239: // return this;
240: // }
241: // public Consumer append (CharSequence csq)
242: // {
243: // if (inAttribute)
244: // attributeValue.append(csq);
245: // else
246: // push(csq.toString());
247: // return this;
248: // }
249: // public Consumer append (CharSequence csq, int start, int end)
250: // {
251: // return append(csq.subSequence(start, end));
252: // }
253: /* #else */
254: public Consumer append(String str) {
255: if (inAttribute)
256: attributeValue.append(str);
257: else
258: push(str);
259: return this ;
260: }
261:
262: /* #endif */
263:
264: void push(Expression exp) {
265: comp.exprStack.push(exp);
266: }
267:
268: void push(Object value) {
269: push(new QuoteExp(value));
270: }
271:
272: public void writeBoolean(boolean v) {
273: if (inAttribute)
274: attributeValue.append(v);
275: else
276: push(v ? QuoteExp.trueExp : QuoteExp.falseExp);
277: }
278:
279: public void writeFloat(float v) {
280: if (inAttribute)
281: attributeValue.append(v);
282: else
283: push(DFloNum.make(v));
284: }
285:
286: public void writeDouble(double v) {
287: if (inAttribute)
288: attributeValue.append(v);
289: else
290: push(DFloNum.make(v));
291: }
292:
293: public void writeInt(int v) {
294: if (inAttribute)
295: attributeValue.append(v);
296: else
297: push(IntNum.make(v));
298: }
299:
300: public void writeLong(long v) {
301: if (inAttribute)
302: attributeValue.append(v);
303: else
304: push(IntNum.make(v));
305: }
306:
307: public void startDocument() {
308:
309: }
310:
311: public void startDocument(ModuleExp mexp) {
312: this .mexp = mexp;
313: startDocument();
314: }
315:
316: public void endDocument() {
317: }
318:
319: public void writeObject(Object v) {
320: if (inAttribute)
321: attributeValue.append(v);
322: else
323: push(v);
324: }
325:
326: public void write(char[] buf, int off, int len) {
327: if (inAttribute)
328: attributeValue.append(buf, off, len);
329: else
330: push(new String(buf, off, len));
331: }
332:
333: public void write(String str) {
334: if (inAttribute)
335: attributeValue.append(str);
336: else
337: push(str);
338: }
339:
340: /* #ifdef use:java.lang.CharSequence */
341: public void write(CharSequence str, int start, int length) {
342: write(str.subSequence(start, length).toString());
343: }
344:
345: /* #else */
346: // public void write (String str, int start, int length)
347: // {
348: // write(str.substring(start, length));
349: // }
350: /* #endif */
351:
352: public boolean ignoring() {
353: return false;
354: }
355:
356: public Expression getExpression() {
357: return (Expression) comp.exprStack.pop();
358: }
359:
360: public void error(char kind, String message) {
361: getMessages().error(kind, message);
362: }
363:
364: /*
365: public void fatal(String message) throws SyntaxException
366: {
367: getMessages().error('f', message);
368: throw new SyntaxException(getMessages());
369: }
370: */
371:
372: Expression resolveQNameExpression(String name) {
373: if (name == null)
374: return QuoteExp.nullExp;
375: else
376: return new QuoteExp(Symbol.make(null, name)); // FIXME
377: }
378:
379: public void parse(Compilation comp) throws java.io.IOException {
380: this .comp = comp;
381: if (comp.exprStack == null)
382: comp.exprStack = new Stack();
383: ModuleExp mexp = comp.pushNewModule(this );
384: comp.mustCompileHere();
385: startDocument(mexp);
386: XMLParser.parse(in, getMessages(), this );
387: endDocument();
388: comp.pop(mexp);
389: }
390:
391: static final ClassType typeXSLT = gnu.bytecode.ClassType
392: .make("gnu.kawa.xslt.XSLT");
393: static final ClassType typeTemplateTable = gnu.bytecode.ClassType
394: .make("gnu.kawa.xslt.TemplateTable");
395: static final Method defineTemplateMethod = typeXSLT
396: .getDeclaredMethod("defineTemplate", 5);
397: static final Method runStylesheetMethod = typeXSLT
398: .getDeclaredMethod("runStylesheet", 0);
399: static final PrimProcedure defineTemplateProc = new PrimProcedure(
400: defineTemplateMethod);
401: static final PrimProcedure runStylesheetProc = new PrimProcedure(
402: runStylesheetMethod);
403: static final Method applyTemplatesMethod = typeXSLT
404: .getDeclaredMethod("applyTemplates", 2);
405: static final PrimProcedure applyTemplatesProc = new PrimProcedure(
406: applyTemplatesMethod);
407: }
|