001: // Copyright (c) 2001, 2002, 2003 Per M.A. Bothner and Brainfood Inc.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.xquery.lang;
005:
006: import gnu.mapping.*;
007: import gnu.lists.*;
008: import gnu.expr.*;
009: import gnu.text.Char;
010: import kawa.standard.Scheme;
011: import gnu.bytecode.*;
012: import gnu.kawa.lispexpr.LangPrimType;
013: import gnu.xquery.util.*;
014: import gnu.xml.*;
015: import gnu.text.Lexer;
016: import gnu.text.SourceMessages;
017: import java.io.Reader;
018: import java.util.Vector;
019: import gnu.kawa.functions.ConstantFunction0;
020: import gnu.kawa.reflect.ClassMethods;
021: import gnu.math.IntNum;
022: import gnu.kawa.xml.*;
023:
024: /** The XQuery language. */
025:
026: public class XQuery extends Language {
027: public static final String XQUERY_FUNCTION_NAMESPACE = "http://www.w3.org/2005/xpath-functions";
028: public static final String KAWA_FUNCTION_NAMESPACE = "http://kawa.gnu.org/";
029: public static final String QEXO_FUNCTION_NAMESPACE = "http://qexo.gnu.org/";
030: public static final String LOCAL_NAMESPACE = "http://www.w3.org/2005/xquery-local-functions";
031: public static final String SCHEMA_NAMESPACE = "http://www.w3.org/2001/XMLSchema";
032: public static final String SCHEMA_INSTANCE_NAMESPACE = "http://www.w3.org/2001/XMLSchema-instance";
033: public static final String XHTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
034: public static final Namespace xqueryFunctionNamespace = Namespace
035: .getInstance(XQUERY_FUNCTION_NAMESPACE);
036: public static final Namespace kawaFunctionNamespace = Namespace
037: .getInstance(KAWA_FUNCTION_NAMESPACE);
038: public static final Namespace qexoFunctionNamespace = Namespace
039: .getInstance(QEXO_FUNCTION_NAMESPACE);
040: public static final Namespace[] defaultFunctionNamespacePath = {
041: qexoFunctionNamespace, xqueryFunctionNamespace,
042: Namespace.EmptyNamespace, kawaFunctionNamespace };
043: static boolean charIsInt = false;
044:
045: /** Pseudo-namespace "prefix" for the default element namespace. */
046: public static final String DEFAULT_ELEMENT_PREFIX = null;
047: /** Pseudo-namespace "prefix" for the default function namespace. */
048: public static final String DEFAULT_FUNCTION_PREFIX = "(functions)";
049:
050: Namespace defaultNamespace;
051:
052: public boolean hasSeparateFunctionNamespace() {
053: return true;
054: }
055:
056: public static gnu.math.Numeric asNumber(Object arg) {
057: if (arg instanceof Char)
058: return gnu.math.IntNum.make(((Char) arg).intValue());
059: return (gnu.math.Numeric) arg;
060: }
061:
062: public static char asChar(Object x) {
063: if (x instanceof Char)
064: return ((Char) x).charValue();
065: int i;
066: if (x instanceof gnu.math.Numeric)
067: i = ((gnu.math.Numeric) x).intValue();
068: else
069: i = -1;
070: if (i < 0 || i > 0xffff)
071: throw new ClassCastException("not a character value");
072: return (char) i;
073: }
074:
075: public boolean isTrue(Object value) {
076: return gnu.xquery.util.BooleanValue.booleanValue(value);
077: }
078:
079: public gnu.text.Lexer getLexer(InPort inp, SourceMessages messages) {
080: return new XQParser(inp, messages, this );
081: }
082:
083: public Compilation getCompilation(Lexer lexer,
084: SourceMessages messages) {
085: return new Compilation(this , messages,
086: ((XQParser) lexer).lexical);
087: }
088:
089: /** Special parser flag used by <code>evalToFocusProc</code>. */
090: public static final int PARSE_WITH_FOCUS = 0x10000;
091:
092: public boolean parse(Compilation tr, int options)
093: throws java.io.IOException, gnu.text.SyntaxException {
094: ModuleExp mexp = tr.mainLambda;
095: Compilation.defaultCallConvention = Compilation.CALL_WITH_CONSUMER;
096: tr.mustCompileHere();
097: XQParser parser = (XQParser) tr.lexer;
098: if (parser.isInteractive()) {
099: Expression sexp = parser.parse(tr);
100: if (sexp == null)
101: return false;
102: mexp.body = sexp;
103: } else if ((options & PARSE_WITH_FOCUS) != 0) {
104: LambdaExp lexp = new LambdaExp(3);
105: Declaration dotDecl = lexp
106: .addDeclaration(XQParser.DOT_VARNAME);
107: dotDecl.setFlag(Declaration.IS_SINGLE_VALUE);
108: dotDecl.noteValue(null); // Does not have a known value.
109: lexp.addDeclaration(XQParser.POSITION_VARNAME,
110: Type.int_type);
111: lexp.addDeclaration(XQParser.LAST_VARNAME, Type.int_type);
112: tr.push(lexp);
113: lexp.body = parser.parse(tr);
114: tr.pop(lexp);
115: mexp.body = lexp;
116: } else {
117: Vector exps = new Vector(10);
118: Expression sexp = mexp.body;
119: if (sexp instanceof BeginExp) {
120: BeginExp bexp = (BeginExp) sexp;
121: int blen = bexp.getExpressionCount();
122: Expression[] bexps = bexp.getExpressions();
123: for (int i = 0; i < blen; i++)
124: exps.addElement(bexps[i]);
125: } else if (sexp != null && sexp != QuoteExp.voidExp) {
126: exps.addElement(sexp);
127: }
128: for (;;) {
129: sexp = parser.parse(tr);
130: if (sexp == null) {
131: if (parser.parseCount == 0
132: && !parser.isInteractive()) {
133: parser.error('e', "empty module", "XPST0003");
134: return false;
135: }
136: break;
137: }
138: exps.addElement(sexp);
139: }
140: int nexps = exps.size();
141: if (nexps == 0)
142: mexp.body = QuoteExp.voidExp;
143: else if (nexps == 1)
144: mexp.body = (Expression) exps.elementAt(0);
145: else {
146: Expression[] arr = new Expression[nexps];
147: exps.copyInto(arr);
148: mexp.body = new BeginExp(arr);
149: }
150: }
151: // It seems silly to pop all the declarations, and then push them
152: // in resolveModule. The complication is that imported variable
153: // declarations are pushed eagerly in the first pass.
154: tr.pop(mexp);
155:
156: if (false) {
157: OutPort dout = OutPort.outDefault();
158: dout.println("[Before name-resolving \"" + mexp.getName()
159: + "\":");
160: mexp.print(dout);
161: dout.println(']');
162: dout.flush();
163: }
164:
165: XQResolveNames resolver = new XQResolveNames(tr);
166: resolver.functionNamespacePath = parser.functionNamespacePath;
167: resolver.parser = parser;
168: resolver.resolveModule(mexp); // FIXME should move to resolve(Compilation)
169: tr.setState(Compilation.BODY_PARSED);
170: return true;
171: }
172:
173: public void resolve(Compilation comp) {
174: }
175:
176: public static int namespaceForFunctions(int argCount) {
177: return (argCount << 2) | FUNCTION_NAMESPACE;
178: }
179:
180: public static final int VARIADIC_FUNCTION_NAMESPACE = (-1 << 2)
181: | FUNCTION_NAMESPACE;
182:
183: public int getNamespaceOf(Declaration decl) {
184: if (decl.isProcedureDecl()) {
185: if (decl.getCode() < 0)
186: return VARIADIC_FUNCTION_NAMESPACE;
187: Expression value = decl.getValue();
188: if (value instanceof LambdaExp) {
189: LambdaExp lexp = (LambdaExp) value;
190: if (lexp.min_args == lexp.max_args)
191: return namespaceForFunctions(lexp.min_args);
192: } else if (value instanceof QuoteExp) {
193: Object val = ((QuoteExp) value).getValue();
194: if (val instanceof Procedure) {
195: Procedure proc = (Procedure) val;
196: int min = proc.minArgs();
197: int max = proc.maxArgs();
198: if (min == max)
199: return namespaceForFunctions(min);
200: }
201: } else if (value instanceof ReferenceExp)
202: return getNamespaceOf(((ReferenceExp) value)
203: .getBinding());
204: // I believe we only get here after an error.
205: return VARIADIC_FUNCTION_NAMESPACE;
206: }
207: return VALUE_NAMESPACE;
208: }
209:
210: public boolean hasNamespace(Declaration decl, int namespace) {
211: int dnspace = getNamespaceOf(decl);
212: return (dnspace == namespace
213: || (dnspace == VARIADIC_FUNCTION_NAMESPACE && (namespace & FUNCTION_NAMESPACE) != 0) || (namespace == VARIADIC_FUNCTION_NAMESPACE && (dnspace & FUNCTION_NAMESPACE) != 0));
214: }
215:
216: public Symbol getSymbol(String name) {
217: return Symbol.make(defaultNamespace, name);
218: }
219:
220: public void define(String name, Object value) {
221: Symbol sym = Symbol.make(defaultNamespace, name);
222: Object prop = value instanceof Procedure ? EnvironmentKey.FUNCTION
223: : null;
224: environ.define(sym, prop, value);
225: }
226:
227: protected void define_method(String name, String cname, String mname) {
228: Symbol sym = Symbol.make(defaultNamespace, name);
229: // This does require eager loading of the class, which takes
230: // extra time on startup. FIXME.
231: ClassType ctype = ClassType.make(cname);
232: Procedure proc = ClassMethods.apply(ctype, mname, '\0', this );
233: proc.setSymbol(sym);
234: environ.define(sym, EnvironmentKey.FUNCTION, proc);
235: }
236:
237: public String getName() {
238: return "XQuery";
239: }
240:
241: static int envCounter = 0;
242:
243: /** Environment of pre-defined non-standard Qexo/Kawa functions. */
244: public static Environment extensionsEnvEnv = Environment
245: .getInstance(KAWA_FUNCTION_NAMESPACE);
246:
247: /** Call a procedure with a given focus (context).
248: * @param proc a 3-operand <code>Procedure</code> as returned by
249: * <code>evalToFocusProc</code>
250: * @param item the context item, passed as the first argument to <code>proc</code>
251: * @param position the context position, passed as the second argument
252: * @param size the context size, passed as the second argument
253: * @param out where to send the result of <code>proc</code>
254: */
255: public void applyWithFocus(Procedure proc, Object item,
256: int position, int size, Consumer out) throws Throwable {
257: CallContext ctx = CallContext.getInstance();
258: proc
259: .check3(item, IntNum.make(position), IntNum.make(size),
260: ctx);
261: Consumer save = ctx.consumer;
262: try {
263: ctx.consumer = out;
264: ctx.runUntilDone();
265: } finally {
266: ctx.consumer = save;
267: }
268: }
269:
270: /** Call a procedure with a given focus (context).
271: * @param proc a 3-operand <code>Procedure</code> as returned by
272: * <code>evalToFocusProc</code>
273: * @param item the context item, passed as the first argument to <code>proc</code>
274: * @param position the context position, passed as the second argument
275: * @param size the context size, passed as the second argument
276: * @return the result of applying <code>proc</code>
277: */
278: public Object applyWithFocus(Procedure proc, Object item,
279: int position, int size) throws Throwable {
280: CallContext ctx = CallContext.getInstance();
281: int oldIndex = ctx.startFromContext();
282: try {
283: proc.check3(item, IntNum.make(position), IntNum.make(size),
284: ctx);
285: return ctx.getFromContext(oldIndex);
286: } catch (Throwable ex) {
287: ctx.cleanupFromContext(oldIndex);
288: throw ex;
289: }
290: }
291:
292: /** Call a procedure with each item in a sequence as the context item.
293: * @param proc a 3-operand <code>Procedure</code> as returned by
294: * <code>evalToFocusProc</code>
295: * @param values a sequence. The <code>proc</code> is called once for each
296: * item, with the item as the first argument, a 1-based index as the
297: * second argument, and the sequence size as the third argument.
298: * @param out where to send the result of <code>proc</code>
299: */
300: public void applyWithFocus(Procedure proc, Object values,
301: Consumer out) throws Throwable {
302: CallContext ctx = CallContext.getInstance();
303: Consumer save = ctx.consumer;
304: try {
305: ctx.consumer = out;
306: applyWithFocus$X(proc, values, ctx);
307: } finally {
308: ctx.consumer = save;
309: }
310: }
311:
312: /** Call a procedure with each item in a sequence as the context item.
313: * @param proc a 3-operand <code>Procedure</code> as returned by
314: * <code>evalToFocusProc</code>
315: * @param values a sequence. The <code>proc</code> is called once for each
316: * item, with the item as the first argument, a 1-based index as the
317: * second argument, and the sequence size as the third argument.
318: * @return the result of applying <code>proc</code>
319: */
320: public Object applyWithFocus(Procedure proc, Object values)
321: throws Throwable {
322: CallContext ctx = CallContext.getInstance();
323: int oldIndex = ctx.startFromContext();
324: try {
325: applyWithFocus$X(proc, values, ctx);
326: return ctx.getFromContext(oldIndex);
327: } catch (Throwable ex) {
328: ctx.cleanupFromContext(oldIndex);
329: throw ex;
330: }
331: }
332:
333: /** Call a procedure with each item in a sequence as the context item.
334: * @param proc a 3-operand <code>Procedure</code> as returned by
335: * <code>evalToFocusProc</code>
336: * @param values a sequence. The <code>proc</code> is called once for each
337: * item, with the item as the first argument, a 1-based index as the
338: * second argument, and the sequence size as the third argument.
339: * @param ctx the <code>CallContext</code>. The <code>$X</code> in the
340: * method name tells Kawa that this argument is implicit when invoked
341: * from XQuery.
342: */
343: public void applyWithFocus$X(Procedure proc, Object values,
344: CallContext ctx) throws Throwable {
345: if (values instanceof Values) {
346: Values v = (Values) values;
347: int count = v.size();
348: if (count == 0)
349: return;
350: int ipos = 0;
351: IntNum size = IntNum.make(count);
352: for (int i = 1;; i++) {
353: proc.check3(v.getPosNext(ipos), IntNum.make(i), size,
354: ctx);
355: ctx.runUntilDone();
356: if (i == count)
357: break;
358: ipos = v.nextPos(ipos);
359: }
360: } else {
361: IntNum one = IntNum.one();
362: proc.check3(values, one, one, ctx);
363: ctx.runUntilDone();
364: }
365: }
366:
367: /** Parse an XQuery expression that is the body of a procedure.
368: * Helper method used by <code>evalWithFocus</code> methods.
369: * @param expr an XQuery expression (query) to evaluate
370: * @return a 3-operand Procedure whose arguments become
371: * the context item, position, and size.
372: */
373: public Procedure evalToFocusProc(String expr) throws Throwable {
374: SourceMessages messages = new SourceMessages();
375: Procedure proc = evalToFocusProc(new CharArrayInPort(expr),
376: messages);
377: if (messages.seenErrors())
378: throw new RuntimeException("invalid syntax in eval form:\n"
379: + messages.toString(20));
380: return proc;
381: }
382:
383: /** Parse an XQuery expression from a <code>Reader</code> that is the body of a procedure.
384: * Helper method used by <code>evalWithFocus</code> methods.
385: * @param in where we read the expression from
386: * @param messages where to write syntax errors
387: * @return a 3-operand Procedure whose arguments become
388: * the context item, position, and size.
389: */
390: public Procedure evalToFocusProc(Reader in, SourceMessages messages)
391: throws Throwable {
392: InPort port = in instanceof InPort ? (InPort) in : new InPort(
393: in);
394: Compilation comp = parse(port, messages, PARSE_WITH_FOCUS
395: | PARSE_IMMEDIATE);
396: CallContext ctx = CallContext.getInstance();
397: int oldIndex = ctx.startFromContext();
398: try {
399: ModuleExp.evalModule(Environment.getCurrent(), ctx, comp,
400: null, null);
401: return (Procedure) ctx.getFromContext(oldIndex);
402: } catch (Throwable ex) {
403: ctx.cleanupFromContext(oldIndex);
404: throw ex;
405: }
406: }
407:
408: /** Evaluate an expression with each item in a sequence as the context item.
409: * @param in where we read the expression from
410: * @param messages where to write syntax errors
411: * @param values becomes the context sequence while
412: * evaluating <code>expr</code>.
413: * @param out where to send the result of the expression
414: */
415: public void evalWithFocus(Reader in, SourceMessages messages,
416: Object values, Consumer out) throws Throwable {
417: applyWithFocus(evalToFocusProc(in, messages), values, out);
418: }
419:
420: /** Evaluate an expression with each item in a sequence as the context item.
421: * @param expr an XQuery expression (query) to evaluate
422: * @param values becomes the context sequence while
423: * evaluating the expression
424: * @return the result of evaluating the expression
425: */
426: public Object evalWithFocus(String expr, Object values)
427: throws Throwable {
428: return applyWithFocus(evalToFocusProc(expr), values);
429: }
430:
431: /** Evaluate an expression with a given focus (context).
432: * @param expr an XQuery expression (query) to evaluate
433: * @param item becomes the context item while evaluating <code>expr</code>.
434: * @param position becomes the context position
435: * @param size becomes the context size
436: * @return the result of evaluating <code>expr</code>
437: */
438: public Object evalWithFocus(String expr, Object item, int position,
439: int size) throws Throwable {
440: return applyWithFocus(evalToFocusProc(expr), item, position,
441: size);
442: }
443:
444: /** Evaluate an expression with a given focus (context).
445: * @param in where we read the expression from
446: * @param messages where to write syntax errors
447: * @param item becomes the context item while evaluating the expression
448: * @param position becomes the context position
449: * @param size becomes the context size
450: * @param out where to send the result of the expression
451: */
452: public void evalWithFocus(Reader in, SourceMessages messages,
453: Object item, int position, int size, Consumer out)
454: throws Throwable {
455: applyWithFocus(evalToFocusProc(in, messages), item, position,
456: size, out);
457: }
458:
459: /** Evaluate an expression with a given focus (context).
460: * Similar to <code>evalWithFocus(String, Object, Consumer)</code>.
461: * The "$X" in the method name tells the Kawa compiler that the CallContext
462: * argument is implicit, so it can be invoked from XQuery code thus:
463: * <code>XQuery:eval-with-focus($xquery, "expr", $sequence)</code>
464: */
465: public void eval_with_focus$X(String expr, Object values,
466: CallContext ctx) throws Throwable {
467: applyWithFocus$X(evalToFocusProc(expr), values, ctx);
468: }
469:
470: /** Evaluate an expression with a given focus (context).
471: * Similar to <code>evalWithFocus(String, Object, int, int, Consumer)</code>.
472: * The "$X" in the method name tells the Kawa compiler that the CallContext
473: * argument is implicit, so it can be invoked from XQuery code thus:
474: * <code>XQuery:eval-with-focus($xquery, "expr", $item, $pos, $size)</code>
475: */
476: public void eval_with_focus$X(String expr, Object item,
477: int position, int size, CallContext ctx) throws Throwable {
478: Procedure proc = evalToFocusProc(expr);
479: proc
480: .check3(item, IntNum.make(position), IntNum.make(size),
481: ctx);
482: }
483:
484: public static final Environment xqEnvironment = Environment
485: .make(XQUERY_FUNCTION_NAMESPACE);
486:
487: // This field need to be public so that the findLiteral method in
488: // gnu.expr.LitTable can find it.
489: public static final XQuery instance = new XQuery();
490: static {
491: instance.initXQuery();
492: }
493:
494: public XQuery() {
495: environ = xqEnvironment;
496: defaultNamespace = xqueryFunctionNamespace;
497: }
498:
499: private void initXQuery() {
500: ModuleBody.setMainPrintValues(true);
501:
502: defProcStFld("unescaped-data",
503: "gnu.kawa.xml.MakeUnescapedData", "unescapedData");
504: defProcStFld("item-at", "gnu.xquery.util.ItemAt", "itemAt");
505: defProcStFld("count", "gnu.kawa.functions.CountValues",
506: "countValues");
507: define_method("sum", "gnu.xquery.util.Reduce", "sum"); // Overloaded
508: defProcStFld("avg", "gnu.xquery.util.Average", "avg");
509: defProcStFld("sublist", "gnu.xquery.util.SubList", "subList"); // deprecated
510: defProcStFld("subsequence", "gnu.xquery.util.SubList",
511: "subList");
512: define_method("empty", "gnu.xquery.util.SequenceUtils",
513: "isEmptySequence");
514: define_method("exists", "gnu.xquery.util.SequenceUtils",
515: "exists");
516: define_method("insert-before", "gnu.xquery.util.SequenceUtils",
517: "insertBefore$X");
518: define_method("remove", "gnu.xquery.util.SequenceUtils",
519: "remove$X");
520: define_method("reverse", "gnu.xquery.util.SequenceUtils",
521: "reverse$X");
522: defProcStFld("false", "gnu.xquery.lang.XQuery", "falseFunction");
523: defProcStFld("true", "gnu.xquery.lang.XQuery", "trueFunction");
524: defProcStFld("boolean", "gnu.xquery.util.BooleanValue",
525: "booleanValue");
526:
527: define_method("trace", "gnu.xquery.util.Debug", "trace");
528: define_method("error", "gnu.xquery.util.XQException", "error"); // overloaded
529: defProcStFld("write-to", "gnu.kawa.xml.WriteTo", "writeTo");
530: defProcStFld("write-to-if-changed", "gnu.kawa.xml.WriteTo",
531: "writeToIfChanged");
532: defProcStFld("iterator-items", "gnu.kawa.xml.IteratorItems",
533: "iteratorItems");
534: defProcStFld("list-items", "gnu.kawa.xml.ListItems",
535: "listItems");
536: define_method("node-name", "gnu.xquery.util.NodeUtils",
537: "nodeName");
538: define_method("nilled", "gnu.xquery.util.NodeUtils", "nilled");
539: define_method("data", "gnu.xquery.util.NodeUtils", "data$X");
540: define_method("lower-case", "gnu.xquery.util.StringUtils",
541: "lowerCase");
542: define_method("upper-case", "gnu.xquery.util.StringUtils",
543: "upperCase");
544: define_method("substring", "gnu.xquery.util.StringUtils",
545: "substring");
546: define_method("string-length", "gnu.xquery.util.StringUtils",
547: "stringLength");
548: define_method("substring-before",
549: "gnu.xquery.util.StringUtils", "substringBefore");
550: define_method("substring-after", "gnu.xquery.util.StringUtils",
551: "substringAfter");
552: define_method("translate", "gnu.xquery.util.StringUtils",
553: "translate");
554: define_method("encode-for-uri", "gnu.xquery.util.StringUtils",
555: "encodeForUri");
556: define_method("iri-to-uri", "gnu.xquery.util.StringUtils",
557: "iriToUri");
558: define_method("escape-html-uri", "gnu.xquery.util.StringUtils",
559: "escapeHtmlUri");
560: // Non-standard (in F&O example appendix). Put in qexo namespace?
561: // define_method("string-pad", "gnu.xquery.util.StringUtils", "stringPad");
562: define_method("contains", "gnu.xquery.util.StringUtils",
563: "contains");
564: define_method("starts-with", "gnu.xquery.util.StringUtils",
565: "startsWith");
566: define_method("ends-with", "gnu.xquery.util.StringUtils",
567: "endsWith");
568: define_method("codepoint-equal", "gnu.xquery.util.StringUtils",
569: "codepointEqual");
570: define_method("normalize-unicode",
571: "gnu.xquery.util.StringUtils", "normalizeUnicode");
572: define_method("string-join", "gnu.xquery.util.StringUtils",
573: "stringJoin");
574: define_method("concat", "gnu.xquery.util.StringUtils",
575: "concat$V");
576: define_method("matches", "gnu.xquery.util.StringUtils",
577: "matches");
578: define_method("replace", "gnu.xquery.util.StringUtils",
579: "replace");
580: define_method("tokenize", "gnu.xquery.util.StringUtils",
581: "tokenize$X");
582: define_method("string-to-codepoints",
583: "gnu.xquery.util.StringUtils", "stringToCodepoints$X");
584: define_method("codepoints-to-string",
585: "gnu.xquery.util.StringUtils", "codepointsToString");
586:
587: define_method("abs", "gnu.xquery.util.NumberValue", "abs");
588: define_method("floor", "gnu.xquery.util.NumberValue", "floor");
589: define_method("ceiling", "gnu.xquery.util.NumberValue",
590: "ceiling");
591: define_method("round", "gnu.xquery.util.NumberValue", "round");
592: define_method("round-half-to-even",
593: "gnu.xquery.util.NumberValue", "roundHalfToEven");
594:
595: define_method("QName", "gnu.xquery.util.QNameUtils",
596: "makeQName");
597: define_method("resolve-QName", "gnu.xquery.util.QNameUtils",
598: "resolveQNameUsingElement");
599: define_method("prefix-from-QName",
600: "gnu.xquery.util.QNameUtils", "prefixFromQName");
601: define_method("local-name-from-QName",
602: "gnu.xquery.util.QNameUtils", "localNameFromQName");
603: define_method("namespace-uri-from-QName",
604: "gnu.xquery.util.QNameUtils", "namespaceURIFromQName");
605: define_method("namespace-uri-for-prefix",
606: "gnu.xquery.util.QNameUtils", "namespaceURIForPrefix");
607: define_method("in-scope-prefixes", "gnu.xquery.util.NodeUtils",
608: "inScopePrefixes$X");
609: define_method("document-uri", "gnu.xquery.util.NodeUtils",
610: "documentUri");
611:
612: define_method("years-from-duration",
613: "gnu.xquery.util.TimeUtils", "yearsFromDuration");
614: define_method("months-from-duration",
615: "gnu.xquery.util.TimeUtils", "monthsFromDuration");
616: define_method("days-from-duration",
617: "gnu.xquery.util.TimeUtils", "daysFromDuration");
618: define_method("hours-from-duration",
619: "gnu.xquery.util.TimeUtils", "hoursFromDuration");
620: define_method("minutes-from-duration",
621: "gnu.xquery.util.TimeUtils", "minutesFromDuration");
622: define_method("seconds-from-duration",
623: "gnu.xquery.util.TimeUtils", "secondsFromDuration");
624: define_method("year-from-dateTime",
625: "gnu.xquery.util.TimeUtils", "yearFromDateTime");
626: define_method("month-from-dateTime",
627: "gnu.xquery.util.TimeUtils", "monthFromDateTime");
628: define_method("day-from-dateTime", "gnu.xquery.util.TimeUtils",
629: "dayFromDateTime");
630: define_method("hours-from-dateTime",
631: "gnu.xquery.util.TimeUtils", "hoursFromDateTime");
632: define_method("minutes-from-dateTime",
633: "gnu.xquery.util.TimeUtils", "minutesFromDateTime");
634: define_method("seconds-from-dateTime",
635: "gnu.xquery.util.TimeUtils", "secondsFromDateTime");
636: define_method("timezone-from-dateTime",
637: "gnu.xquery.util.TimeUtils", "timezoneFromDateTime");
638: define_method("year-from-date", "gnu.xquery.util.TimeUtils",
639: "yearFromDate");
640: define_method("month-from-date", "gnu.xquery.util.TimeUtils",
641: "monthFromDate");
642: define_method("day-from-date", "gnu.xquery.util.TimeUtils",
643: "dayFromDate");
644: define_method("timezone-from-date",
645: "gnu.xquery.util.TimeUtils", "timezoneFromDate");
646: define_method("hours-from-time", "gnu.xquery.util.TimeUtils",
647: "hoursFromTime");
648: define_method("minutes-from-time", "gnu.xquery.util.TimeUtils",
649: "minutesFromTime");
650: define_method("seconds-from-time", "gnu.xquery.util.TimeUtils",
651: "secondsFromTime");
652: define_method("timezone-from-time",
653: "gnu.xquery.util.TimeUtils", "timezoneFromTime");
654: define_method("adjust-dateTime-to-timezone",
655: "gnu.xquery.util.TimeUtils", "adjustDateTimeToTimezone"); // overloaded
656: define_method("adjust-date-to-timezone",
657: "gnu.xquery.util.TimeUtils", "adjustDateToTimezone"); // overloaded
658: define_method("adjust-time-to-timezone",
659: "gnu.xquery.util.TimeUtils", "adjustTimeToTimezone"); // overloaded
660: define_method("dateTime", "gnu.xquery.util.TimeUtils",
661: "dateTime");
662: define_method("current-dateTime", "gnu.xquery.util.TimeUtils",
663: "currentDateTime");
664: define_method("current-date", "gnu.xquery.util.TimeUtils",
665: "currentDate");
666: define_method("current-time", "gnu.xquery.util.TimeUtils",
667: "currentTime");
668: define_method("implicit-timezone", "gnu.xquery.util.TimeUtils",
669: "implicitTimezone");
670:
671: define_method("zero-or-one", "gnu.xquery.util.SequenceUtils",
672: "zeroOrOne");
673: define_method("one-or-more", "gnu.xquery.util.SequenceUtils",
674: "oneOrMore");
675: define_method("exactly-one", "gnu.xquery.util.SequenceUtils",
676: "exactlyOne");
677:
678: defProcStFld("distinct-nodes", "gnu.kawa.xml.SortNodes",
679: "sortNodes");
680:
681: // FIXME - should be imported?
682: defProcStFld("children", "gnu.kawa.xml.Children", "children");
683: define_method("not", "gnu.xquery.util.BooleanValue", "not");
684:
685: defaultNamespace = qexoFunctionNamespace;
686: defProcStFld("response-header", "gnu.kawa.servlet.HTTP");
687: defProcStFld("response-content-type", "gnu.kawa.servlet.HTTP");
688: defProcStFld("response-status", "gnu.kawa.servlet.HTTP");
689: defProcStFld("error-response", "gnu.kawa.servlet.HTTP");
690: defProcStFld("current-servlet", "gnu.kawa.servlet.HTTP");
691: defProcStFld("current-servlet-context", "gnu.kawa.servlet.HTTP");
692: defProcStFld("current-servlet-config", "gnu.kawa.servlet.HTTP");
693: defProcStFld("servlet-context-realpath",
694: "gnu.kawa.servlet.HTTP");
695: defProcStFld("get-response", "gnu.kawa.servlet.HTTP");
696: defProcStFld("get-request", "gnu.kawa.servlet.HTTP");
697: defProcStFld("request-method", "gnu.kawa.servlet.HTTP");
698: defProcStFld("request-uri", "gnu.kawa.servlet.HTTP");
699: defProcStFld("request-url", "gnu.kawa.servlet.HTTP");
700: defProcStFld("request-path-info", "gnu.kawa.servlet.HTTP");
701: defProcStFld("request-path-translated", "gnu.kawa.servlet.HTTP");
702: defProcStFld("request-servlet-path", "gnu.kawa.servlet.HTTP");
703: defProcStFld("request-query-string", "gnu.kawa.servlet.HTTP");
704: defProcStFld("request-parameter", "gnu.kawa.servlet.HTTP");
705: defProcStFld("request-parameters", "gnu.kawa.servlet.HTTP");
706: defaultNamespace = xqueryFunctionNamespace;
707: }
708:
709: public static XQuery getInstance() {
710: return instance;
711: }
712:
713: /** The compiler insert calls to this method for applications and applets. */
714: public static void registerEnvironment() {
715: Language.setDefaults(instance);
716: }
717:
718: static public QuoteExp falseExp = new QuoteExp(Boolean.FALSE,
719: XDataType.booleanType);
720: static public QuoteExp trueExp = new QuoteExp(Boolean.TRUE,
721: XDataType.booleanType);
722:
723: public static final ConstantFunction0 falseFunction = new ConstantFunction0(
724: "false", falseExp);
725: public static final ConstantFunction0 trueFunction = new ConstantFunction0(
726: "true", trueExp);
727:
728: public Consumer getOutputConsumer(java.io.Writer out) {
729: return new XMLPrinter(out, false);
730: }
731:
732: static Object[] typeMap = { "string", XDataType.stringType,
733: "untypedAtomic", XDataType.untypedAtomicType, "boolean",
734: XDataType.booleanType, "integer", XIntegerType.integerType,
735: "long", XIntegerType.longType, "int", XIntegerType.intType,
736: "short", XIntegerType.shortType, "byte",
737: XIntegerType.byteType, "unsignedLong",
738: XIntegerType.unsignedLongType, "unsignedInt",
739: XIntegerType.unsignedIntType, "unsignedShort",
740: XIntegerType.unsignedShortType, "unsignedByte",
741: XIntegerType.unsignedByteType, "positiveInteger",
742: XIntegerType.positiveIntegerType, "nonPositiveInteger",
743: XIntegerType.nonPositiveIntegerType, "negativeInteger",
744: XIntegerType.negativeIntegerType, "nonNegativeInteger",
745: XIntegerType.nonNegativeIntegerType, "date",
746: XTimeType.dateType, "dateTime", XTimeType.dateTimeType,
747: "time", XTimeType.timeType, "duration",
748: XTimeType.durationType, "yearMonthDuration",
749: XTimeType.yearMonthDurationType, "dayTimeDuration",
750: XTimeType.dayTimeDurationType, "gYearMonth",
751: XTimeType.gYearMonthType, "gYear", XTimeType.gYearType,
752: "gMonthDay", XTimeType.gMonthDayType, "gDay",
753: XTimeType.gDayType, "gMonth", XTimeType.gMonthType,
754: "decimal", XDataType.decimalType, "float",
755: XDataType.floatType, "double", XDataType.doubleType,
756: "anyURI", XDataType.anyURIType, "hexBinary",
757: XDataType.hexBinaryType, "base64Binary",
758: XDataType.base64BinaryType, "NOTATION",
759: XDataType.NotationType, "QName", "gnu.mapping.Symbol",
760: "normalizedString", XStringType.normalizedStringType,
761: "token", XStringType.tokenType, "language",
762: XStringType.languageType, "NMTOKEN",
763: XStringType.NMTOKENType, "Name", XStringType.NameType,
764: "NCName", XStringType.NCNameType, "ID", XStringType.IDType,
765: "IDREF", XStringType.IDREFType, "ENTITY",
766: XStringType.ENTITYType, "anyAtomicType",
767: XDataType.anyAtomicType };
768:
769: public static Type getStandardType(String name) {
770: for (int i = typeMap.length; (i -= 2) >= 0;) {
771: if (typeMap[i].equals(name)) {
772: Object t = typeMap[i + 1];
773: if (t instanceof String)
774: return Scheme.string2Type((String) t);
775: else
776: return (Type) t;
777: }
778: }
779: return null;
780: }
781:
782: public Type getTypeFor(String name) {
783: String core = name.startsWith("xs:") ? name.substring(3) : name
784: .startsWith("xdt:") ? name.substring(4) : name;
785: Type t = getStandardType(core);
786: return t != null ? t : Scheme.string2Type(name);
787: }
788:
789: public String formatType(Type type) {
790: String tname = type.getName();
791: if ("gnu.math.IntNum".equals(tname))
792: return "xs:integer";
793: if ("java.lang.String".equals(tname)
794: || "java.lang.CharSequence".equals(tname))
795: return "xs:string";
796: return type.toString();
797: }
798:
799: public Type getTypeFor(Class clas) {
800: if (clas.isPrimitive()) {
801: String name = clas.getName();
802: if (name.equals("boolean"))
803: return XDataType.booleanType;
804: return Scheme.getNamedType(name);
805: } else if (!clas.isArray()) {
806: String name = clas.getName();
807: if (name.equals("java.lang.String"))
808: return XDataType.stringStringType;
809: if (name.equals("gnu.kawa.xml.UntypedAtomic"))
810: return XDataType.untypedAtomicType;
811: if (name.equals("java.lang.Boolean"))
812: return XDataType.booleanType;
813: if (name.equals("java.lang.Float"))
814: return XDataType.floatType;
815: if (name.equals("java.lang.Double"))
816: return XDataType.doubleType;
817: if (name.equals("java.math.BigDecimal"))
818: return XDataType.decimalType;
819: if (name.equals("gnu.math.Duration"))
820: return XDataType.durationType;
821: if (name.equals("gnu.text.Path"))
822: return XDataType.anyURIType;
823: }
824: return Type.make(clas);
825: }
826:
827: public Procedure getPrompter() {
828: return new Prompter();
829: }
830:
831: /*
832: static boolean isPunctuation (char ch)
833: {
834: return ch == '-' || ch == '.' || ch == ':' || ch == '_'
835: || (ch >= 0xB7 // To short-circuit rare tests
836: && (ch == '\u00B7' // middle dot
837: || ch == '\u0387' // greek ano teleia
838: || ch == '\u06dd' // arabic end of ayah
839: || ch == '\u06de' // arabic start of rub el hizb
840: ));
841: }
842:
843: static boolean isMark (char ch)
844: {
845: return ! Character.isLetter(ch)
846: && ! Characfter.isDigit(ch)
847: && Character.isJavaIdnteiferiPart(ch);
848: }
849: */
850:
851: /** Mangle an XML name as specified by JAXB. */
852: static void mangle(String name, int start, int length,
853: StringBuffer sbuf, char mode) {
854: // One of 'P' for punctuation; 'D' for digit; 'M' for mark;
855: // 'L' for lower-case; 'U' for upper-case; 'O' other (uncased) letter.
856: char prev = 'P';
857: int outStart = sbuf.length();
858: for (int i = 0; i < length;) {
859: boolean wordStart;
860: char ch = name.charAt(start + i);
861: i++;
862: if (Character.isUpperCase(ch)) {
863: wordStart = prev != 'U'
864: || (i < length && Character.isLowerCase(name
865: .charAt(start + i)));
866: prev = 'U';
867: } else if (Character.isLowerCase(ch)) {
868: wordStart = prev != 'L' || prev != 'U';
869: prev = 'L';
870: } else if (Character.isLetter(ch)) { // uncased letter
871: wordStart = prev != 'O';
872: prev = 'O';
873: } else if (Character.isDigit(ch)) {
874: wordStart = prev != 'D';
875: prev = 'D';
876: } else if (Character.isJavaIdentifierPart(ch)) {
877: wordStart = prev != 'D' && prev != 'M';
878: prev = 'M';
879: } else // if (isPunctuation(ch))
880: {
881: prev = 'P';
882: continue;
883: }
884: if (wordStart || mode == '_') {
885: if (wordStart && mode == '_'
886: && sbuf.length() > outStart)
887: sbuf.append('_');
888: ch = Character.toUpperCase(ch);
889: }
890: sbuf.append(ch);
891: }
892: }
893:
894: public static String mangle(String name) {
895: StringBuffer sbuf = new StringBuffer();
896: mangle(name, 0, name.length(), sbuf, 'U');
897: return sbuf.toString();
898: }
899:
900: public static String makeClassName(String source) {
901: source = source.replace(java.io.File.separatorChar, '/');
902: int sl = source.lastIndexOf('/');
903: if (sl >= 0)
904: source = source.substring(sl + 1);
905: int dot = source.lastIndexOf('.');
906: if (dot >= 0)
907: source = source.substring(0, dot);
908: return Compilation.mangleNameIfNeeded(source);
909: }
910:
911: public static Object getExternal(Symbol name, Object type) {
912: Environment env = Environment.getCurrent();
913: Object value = env.get(name, null, null);
914: if (value == null)
915: value = env.get(Symbol.makeWithUnknownNamespace(name
916: .getLocalName(), name.getPrefix()), null, null);
917: if (value == null)
918: throw new RuntimeException("unbound external " + name);
919: if (type == null)
920: return value;
921: if (type instanceof XDataType)
922: return ((XDataType) type).cast(value);
923: if (type instanceof ClassType) {
924: String cname = ((ClassType) type).getName();
925: // KLUDGE - FIXME
926: if ("gnu.math.IntNum".equals(cname))
927: return IntNum.valueOf(value.toString());
928: if ("gnu.math.RealNum".equals(cname))
929: return gnu.math.DFloNum.make(Double.parseDouble(value
930: .toString()));
931: }
932: try {
933: value = ((Type) type).coerceFromObject(value);
934: } catch (ClassCastException ex) {
935: throw new WrongType(name.toString(), WrongType.ARG_VARNAME,
936: value, type.toString());
937: }
938: return value;
939: }
940: }
941:
942: class Prompter extends Procedure1 {
943: public Object apply1(Object arg) {
944: InPort port = (InPort) arg;
945: int line = port.getLineNumber() + 1;
946: char state = port.readState;
947: if (state == '\n')
948: state = ' ';
949: if (state == '<')
950: return "<!--" + line + "-->";
951: else if (state == ':')
952: return "-(:" + line + "c:) ";
953: else
954: return "(: " + line + state + ":) ";
955: }
956: }
|