1 // Copyright (c) 2003, 2004, 2006  Per M.A. Bothner.
2 // This is free software;  for terms and warranty disclaimer see ./COPYING.
3 
4 package gnu.xquery.lang;
5 import gnu.expr.*;
6 import gnu.kawa.xml.*;
7 import gnu.xml.*;
8 import gnu.mapping.*;
9 import gnu.bytecode.*;
10 import gnu.kawa.reflect.StaticFieldLocation;
11 import gnu.kawa.reflect.SingletonType;
12 import gnu.kawa.functions.CompileNamedPart;
13 import gnu.xquery.util.NamedCollator;
14 import gnu.xquery.util.QNameUtils;
15 import java.util.*;
16 
17 public class XQResolveNames extends ResolveNames
18 {
19   public XQParser parser;
20 
21   /** Code number for the special <code>last</code> function. */
22   public static final int LAST_BUILTIN = -1;
23 
24   /** Code number for the special <code>position</code> function. */
25   public static final int POSITION_BUILTIN = -2;
26 
27   /** Code number for internal function to handle extensions. */
28   public static final int HANDLE_EXTENSION_BUILTIN = -3;
29 
30   /** Code number for the special <code>compare</code> function. */
31   public static final int COMPARE_BUILTIN = -4;
32 
33   /** Code number for the special <code>distinct-values</code> function. */
34   public static final int DISTINCT_VALUES_BUILTIN = -5;
35 
36   /** Code number for the special <code>local-name</code> function. */
37   public static final int LOCAL_NAME_BUILTIN = -6;
38 
39   /** Code number for the special <code>namespace-uri</code> function. */
40   public static final int NAMESPACE_URI_BUILTIN = -7;
41 
42   /** Code number for the special <code>collection</code> function. */
43   public static final int COLLECTION_BUILTIN = -8;
44 
45   /** Code number for the special <code>doc</code> function. */
46   public static final int DOC_BUILTIN = -9;
47 
48   /** Code number for the special <code>doc-available</code> function. */
49   public static final int DOC_AVAILABLE_BUILTIN = -10;
50 
51   /** Code number for the special <code>base-uri</code> function. */
52   public static final int BASE_URI_BUILTIN = -11;
53 
54   /** Code number for the special <code>ressolve-uri</code> function. */
55   public static final int RESOLVE_URI_BUILTIN = -12;
56 
57   /** Code number for internal function that maps prefix to uri. */
58   public static final int RESOLVE_PREFIX_BUILTIN = -13;
59 
60   /** Code number for the special <code>static-base-uri</code> function. */
61   public static final int STATIC_BASE_URI_BUILTIN = -14;
62 
63   /** Code number for the special <code>index-of</code> function. */
64   public static final int INDEX_OF_BUILTIN = -15;
65 
66   /** Code number for the special <code>string</code> function. */
67   public static final int STRING_BUILTIN = -16;
68 
69   /** Code number for the special <code>normalize-space</code> function. */
70   public static final int NORMALIZE_SPACE_BUILTIN = -17;
71 
72   /** Code number for the special <code>unordered</code> function. */
73   public static final int UNORDERED_BUILTIN = -18;
74 
75   /** Code number for the special <code>lang</code> function. */
76   public static final int LANG_BUILTIN = -23;
77 
78   /** Code number for the special <code>name</code> function. */
79   public static final int NAME_BUILTIN = -24;
80 
81   /** Code number for the special <code>deep-equal</code> function. */
82   public static final int DEEP_EQUAL_BUILTIN = -25;
83 
84   /** Code number for the special <code>min</code> function. */
85   public static final int MIN_BUILTIN = -26;
86 
87   /** Code number for the special <code>max</code> function. */
88   public static final int MAX_BUILTIN = -27;
89 
90   /** Code number for the special <code>number</code> function. */
91   public static final int NUMBER_BUILTIN = -28;
92 
93   /** Code number for the special <code>default-collation</code> function. */
94   public static final int DEFAULT_COLLATION_BUILTIN = -29;
95 
96   /** Code number for the special <code>id</code> function. */
97   public static final int ID_BUILTIN = -30;
98 
99   /** Code number for the special <code>idref</code> function. */
100   public static final int IDREF_BUILTIN = -31;
101 
102   /** Code number for the special <code>root</code> function. */
103   public static final int ROOT_BUILTIN = -32;
104 
105   public static final int CAST_AS_BUILTIN = -33;
106   public static final int CASTABLE_AS_BUILTIN = -34;
107 
108   /** Value of {@code xs:QName()} constructor. */
109   public static final int XS_QNAME_BUILTIN = -35;
110 
111   /** Like {@code XS_QNAME_BUILTIN}, but ignore the default
112    * element namespace.  The is appropriate fro resolving atributes. */
113   public static final int XS_QNAME_IGNORE_DEFAULT_BUILTIN = -36;
114 
115   public static final Declaration handleExtensionDecl
116     = makeBuiltin("(extension)", HANDLE_EXTENSION_BUILTIN);
117 
118   public static final Declaration castAsDecl
119     = makeBuiltin("(cast as)", CAST_AS_BUILTIN);
120 
121   public static final Declaration castableAsDecl
122     = makeBuiltin("(castable as)", CASTABLE_AS_BUILTIN);
123 
124   /** Declaration for the <code>fn:last()</code> function. */
125   public static final Declaration lastDecl
126     = makeBuiltin("last", LAST_BUILTIN);
127 
128   public static final Declaration xsQNameDecl
129     = makeBuiltin(Symbol.make(XQuery.SCHEMA_NAMESPACE, "QName"), XS_QNAME_BUILTIN);
130 
131   public static final Declaration xsQNameIgnoreDefaultDecl
132     = makeBuiltin(Symbol.make(XQuery.SCHEMA_NAMESPACE, "(QName-ignore-default)"), XS_QNAME_IGNORE_DEFAULT_BUILTIN);
133 
134   public static final Declaration staticBaseUriDecl
135     = makeBuiltin("static-base-uri", STATIC_BASE_URI_BUILTIN);
136 
137   public static final Declaration resolvePrefixDecl
138     = makeBuiltin(Symbol.make(XQuery.SCHEMA_NAMESPACE, "(resolve-prefix)"),
139                   RESOLVE_PREFIX_BUILTIN);
140 
141   /** Create a <code>Declaration</code> for a builtin function. */
makeBuiltin(String name, int code)142   public static Declaration makeBuiltin (String name, int code)
143   {
144     return makeBuiltin (Symbol.make(XQuery.XQUERY_FUNCTION_NAMESPACE, name, "fn"),
145 			code);
146   }
147 
148   /** Create a <code>Declaration</code> for a builtin function. */
makeBuiltin(Symbol name, int code)149   public static Declaration makeBuiltin (Symbol name, int code)
150   {
151     Declaration decl = new Declaration(name);
152     decl.setProcedureDecl(true);
153     decl.setCode(code);
154     return decl;
155   }
156 
XQResolveNames()157   public XQResolveNames ()
158   {
159     this(null);
160   }
161 
pushBuiltin(String name, int code)162   void pushBuiltin (String name, int code)
163   {
164     lookup.push(makeBuiltin(name, code));
165   }
166 
XQResolveNames(Compilation comp)167   public XQResolveNames (Compilation comp)
168   {
169     super(comp);
170     lookup.push(lastDecl);
171     lookup.push(xsQNameDecl);
172     lookup.push(staticBaseUriDecl);
173     pushBuiltin("position", POSITION_BUILTIN);
174     pushBuiltin("compare", COMPARE_BUILTIN);
175     pushBuiltin("distinct-values", DISTINCT_VALUES_BUILTIN);
176     pushBuiltin("local-name", LOCAL_NAME_BUILTIN);
177     pushBuiltin("name", NAME_BUILTIN);
178     pushBuiltin("namespace-uri", NAMESPACE_URI_BUILTIN);
179     pushBuiltin("root", ROOT_BUILTIN);
180     pushBuiltin("base-uri", BASE_URI_BUILTIN);
181     pushBuiltin("lang", LANG_BUILTIN);
182     pushBuiltin("resolve-uri", RESOLVE_URI_BUILTIN);
183     pushBuiltin("collection", COLLECTION_BUILTIN);
184     pushBuiltin("doc", DOC_BUILTIN);
185     pushBuiltin("document", DOC_BUILTIN); // Obsolete
186     pushBuiltin("doc-available", DOC_AVAILABLE_BUILTIN);
187     pushBuiltin("index-of", INDEX_OF_BUILTIN);
188     pushBuiltin("string", STRING_BUILTIN);
189     pushBuiltin("normalize-space", NORMALIZE_SPACE_BUILTIN);
190     pushBuiltin("unordered", UNORDERED_BUILTIN);
191     pushBuiltin("deep-equal", DEEP_EQUAL_BUILTIN);
192     pushBuiltin("min", MIN_BUILTIN);
193     pushBuiltin("max", MAX_BUILTIN);
194     pushBuiltin("number", NUMBER_BUILTIN);
195     pushBuiltin("default-collation", DEFAULT_COLLATION_BUILTIN);
196     pushBuiltin("id", ID_BUILTIN);
197     pushBuiltin("idref", IDREF_BUILTIN);
198   }
199 
200   public Namespace[] functionNamespacePath
201     = XQuery.defaultFunctionNamespacePath;
202 
push(ScopeExp exp)203   protected void push (ScopeExp exp)
204   {
205     for (Declaration decl = exp.firstDecl();
206          decl != null;  decl = decl.nextDecl())
207       {
208         push(decl);
209       }
210   }
211 
push(Declaration decl)212   void push (Declaration decl)
213   {
214     Compilation comp = getCompilation();
215     Object name = decl.getSymbol();
216     boolean function = decl.isProcedureDecl();
217     if (name instanceof String)
218       {
219         int line = decl.getLineNumber();
220         if (line > 0 && comp != null)
221           {
222             String saveFilename = comp.getFileName();
223             int saveLine = comp.getLineNumber();
224             int saveColumn = comp.getColumnNumber();
225             comp.setLocation(decl);
226             name = parser.namespaceResolve((String) name, function);
227             comp.setLine(saveFilename, saveLine, saveColumn);
228           }
229         else
230           name = parser.namespaceResolve((String) name, function);
231         if (name == null)
232           return;
233         decl.setName(name);
234       }
235 
236     Declaration old = lookup.lookup(name, XQuery.instance.getNamespaceOf(decl));
237     if (old != null)
238       {
239         if (decl.context == old.context)
240           ScopeExp.duplicateDeclarationError(old, decl, comp);
241         else if (XQParser.warnHidePreviousDeclaration
242                  && (! (name instanceof Symbol)
243                      || ((Symbol) name).getNamespace() != null))
244           comp.error('w', decl, "declaration ",
245                      " hides previous declaration");
246       }
247     lookup.push(decl);
248   }
249 
flookup(Symbol sym)250   Declaration flookup (Symbol sym)
251   {
252     Environment env = XQuery.xqEnvironment;
253     gnu.mapping.Location loc = env.lookup(sym, EnvironmentKey.FUNCTION);
254     if (loc == null)
255       return null;
256     loc = loc.getBase();
257     if (loc instanceof StaticFieldLocation)
258       {
259 	Declaration decl = ((StaticFieldLocation) loc).getDeclaration();
260 	if (decl != null)
261 	  return decl;
262       }
263     Object val = loc.get(null);
264     if (val != null)
265       return procToDecl(sym, val);
266     return null;
267   }
268 
visitReferenceExp(ReferenceExp exp, Void ignored)269   protected Expression visitReferenceExp (ReferenceExp exp, Void ignored)
270   {
271     return visitReferenceExp(exp, (ApplyExp) null);
272   }
273 
visitReferenceExp(ReferenceExp exp, ApplyExp call)274   protected Expression visitReferenceExp (ReferenceExp exp, ApplyExp call)
275   {
276     if (exp.getBinding() == null)
277       {
278 	Object symbol = exp.getSymbol();
279         boolean needFunction = exp.isProcedureName();
280         boolean needType = exp.getFlag(ReferenceExp.TYPE_NAME);
281         int namespace = call == null ? XQuery.VALUE_NAMESPACE
282           : XQuery.namespaceForFunctions(call.getArgCount());
283 	Declaration decl = lookup.lookup(symbol, namespace);
284         Symbol sym;
285         if (decl != null)
286           ;
287 	else if (symbol instanceof Symbol
288             && "".equals((sym = (Symbol) symbol).getNamespaceURI()))
289           {
290             // kludge - use xxx_BUILTIN mechanism?  FIXME
291             String name = sym.getLocalName();
292             String mname;
293             if ("request".equals(name))
294               mname = "getCurrentRequest";
295             else if ("response".equals(name))
296               mname = "getCurrentResponse";
297             else
298               mname = null;
299             if (mname != null)
300               {
301                 Method meth =
302                   ClassType.make("gnu.kawa.servlet.KawaServlet")
303                   .getDeclaredMethod(mname, 0);
304                 return new ApplyExp(meth, Expression.noExpressions);
305               }
306           }
307         else if (symbol instanceof Symbol)
308           {
309             // Never happens, I believe.
310             decl = flookup((Symbol) symbol);
311           }
312         else // if (symbol instanceof String)
313           {
314             String name = (String) symbol;
315             if (name.indexOf(':') < 0)
316               {
317                 name = name.intern();
318                 if (needFunction)
319                   {
320                     for (int i = 0;  i < functionNamespacePath.length;  i++)
321                       {
322                         sym = functionNamespacePath[i].getSymbol(name);
323                         decl = lookup.lookup(sym, namespace);
324                         if (decl != null)
325                           break;
326                         decl = flookup(sym);
327                         if (decl != null)
328                           break;
329                       }
330                   }
331               }
332             if (decl == null)
333               {
334                 sym = parser.namespaceResolve(name, needFunction);
335                 if (sym != null)
336                   {
337                     decl = lookup.lookup(sym, namespace);
338                     if (decl == null
339                         && (needFunction || needType))
340                       {
341                         String uri = sym.getNamespaceURI();
342                         Type type = null;
343                         if (XQuery.SCHEMA_NAMESPACE.equals(uri))
344                           {
345                             type = parser.interpreter.getStandardType(sym.getName());
346                           }
347                         else if (needType && uri == ""
348                             && ! getCompilation().isPedantic())
349                           {
350                             type = parser.interpreter.getTypeFor(name);
351                           }
352                         if (type != null)
353                           return new QuoteExp(type).setLine(exp);
354                         if (uri != null && uri.length() > 6 &&
355                             uri.startsWith("class:"))
356                           {
357                             ClassType ctype = ClassType.make(uri.substring(6));
358                             return CompileNamedPart.makeExp(ctype, sym.getName());
359                           }
360                         decl = flookup(sym);
361                       }
362                   }
363               }
364           }
365         if (decl != null)
366           exp.setBinding(decl);
367         else if (needFunction)
368           error('e', "unknown function "+symbol);
369         else if (needType)
370           messages.error('e', exp, "unknown type "+symbol, "XPST0051");
371         else
372           messages.error('e', exp, "unknown variable $"+symbol, "XPST0008");
373       }
374     return exp;
375   }
376 
visitSetExp(SetExp exp, Void ignored)377   protected Expression visitSetExp (SetExp exp, Void ignored)
378   {
379     Expression result = super.visitSetExp(exp, ignored);
380     Declaration decl = exp.getBinding();
381     Object name;
382     Expression new_value;
383     if (decl != null && ! getCompilation().immediate
384 	&& (name = decl.getSymbol()) instanceof Symbol
385 	&& XQuery.LOCAL_NAMESPACE.equals(((Symbol) name).getNamespaceURI())
386         && (! ((new_value = exp.getNewValue()) instanceof ApplyExp)
387             || ((ApplyExp) new_value).getFunction() != XQParser.getExternalFunction))
388       {
389 	decl.setFlag(Declaration.PRIVATE_SPECIFIED);
390 	decl.setPrivate(true);
391       }
392     return result;
393   }
394 
395   private Declaration moduleDecl;
visitStatements(Expression exp)396   private Expression visitStatements (Expression exp)
397   {
398     // The tricky part here is interleaving declarations and statements
399     // so that a variable declaration is only visible *after* we have
400     // visited its initializing expression.
401     if (exp instanceof BeginExp)
402       {
403         BeginExp bbody = (BeginExp) exp;
404         Expression[] exps = bbody.getExpressions();
405         int nexps = bbody.getExpressionCount();
406         for (int i = 0;  i < nexps;  i++)
407           {
408             exps[i] = visitStatements(exps[i]);
409           }
410       }
411     else if (exp instanceof SetExp)
412       {
413         Declaration decl = moduleDecl;
414         SetExp sexp = (SetExp) exp;
415         exp = visitSetExp(sexp, null);
416         if (sexp.isDefining() && sexp.getBinding() == decl)
417           {
418             if (! decl.isProcedureDecl())
419               push(decl);
420             decl = decl.nextDecl();
421           }
422         moduleDecl = decl;
423       }
424     else
425       exp = visit(exp, null);
426     return exp;
427   }
428 
429     static class CycleDetector extends ExpExpVisitor<Void> {
430         Map<Declaration,Integer> depsScanState = new HashMap<Declaration,Integer>();
431         static final Integer SCANNING = 0;
432         static final Integer SCANNED_CYCLE = 1;
433         static final Integer SCANNED_NO_CYCLE = -1;
434 
435         Declaration target;
436         boolean cycleSeen;
437 
visitReferenceExp(ReferenceExp exp, Void ignored)438         protected Expression visitReferenceExp(ReferenceExp exp, Void ignored) {
439             Declaration decl = exp.getBinding();
440             if (decl != null && decl.context instanceof ModuleExp)
441                 scanDependencies(decl);
442             return exp;
443         }
444 
scanDependencies(Declaration decl)445         public void scanDependencies(Declaration decl) {
446             if (target == null)
447                 target = decl;
448             else if (target == decl) {
449                 cycleSeen = true;
450                 return;
451             }
452             Integer state = depsScanState.get(decl);
453             if (state != null) {
454                 if (state == SCANNING)
455                     depsScanState.put(decl, SCANNED_CYCLE);
456                 return;
457             }
458             depsScanState.put(decl, SCANNING);
459             Expression dval = decl.getValue();
460             if (dval != null)
461                 visit(dval, null);
462             state = depsScanState.get(decl);
463             if (state == SCANNING)
464                 depsScanState.put(decl, SCANNED_NO_CYCLE);
465         }
466 
467         /** Return true if cycle detected. */
scanVariable(Declaration decl)468         public static boolean scanVariable(Declaration decl) {
469             CycleDetector cycleDetector = new CycleDetector();
470             cycleDetector.scanDependencies(decl);
471             return cycleDetector.cycleSeen;
472          }
473     }
474 
resolveModule(ModuleExp exp)475   public void resolveModule(ModuleExp exp)
476   {
477     currentLambda = exp;
478     for (Declaration decl = exp.firstDecl();
479          decl != null;  decl = decl.nextDecl())
480       {
481         if (decl.isProcedureDecl())
482 	  push(decl);
483       }
484     moduleDecl = exp.firstDecl();
485     exp.body = visitStatements(exp.body);
486 
487     for (Declaration decl = exp.firstDecl();
488          decl != null;  decl = decl.nextDecl())
489       {
490         if (! decl.isProcedureDecl() && CycleDetector.scanVariable(decl))
491             getCompilation().error('e',
492                 "cycle detected initializing $"+decl.getName(),
493                 "XQST0054", decl);
494 
495         // Remove old hidden declarations, for GC and speed.
496         if (decl.getSymbol() != null)
497           lookup.removeSubsumed(decl);
498       }
499   }
500 
501   /**
502    * Coerce argument to NamedCallator, or return default collator.
503    * @param args argument list
504    * @param argno index in args of collator argument
505    */
getCollator(Expression[] args, int argno)506   Expression getCollator (Expression[] args, int argno)
507   {
508     if (args != null && args.length > argno)
509       return new ApplyExp(ClassType.make("gnu.xquery.util.NamedCollator")
510                             .getDeclaredMethod("find", 1),
511                             new Expression[] { args[argno] });
512     NamedCollator coll = parser.defaultCollator;
513     return coll == null ? QuoteExp.nullExp : new QuoteExp(coll);
514   }
515 
withCollator(Method method, Expression[] args, String name, int minArgs)516   Expression withCollator (Method method, Expression[] args,
517                            String name, int minArgs)
518   {
519     return withCollator(new QuoteExp(new PrimProcedure(method)),
520                         args, name, minArgs);
521   }
522 
523   /** Adjust call to add default collator if collator argument is missing. */
withCollator(Expression function, Expression[] args, String name, int minArgs)524   Expression withCollator (Expression function, Expression[] args,
525                            String name, int minArgs)
526   {
527     String err = WrongArguments.checkArgCount(name, minArgs, minArgs+1, args.length);
528     if (err != null)
529       return getCompilation().syntaxError(err);
530     Expression[] xargs = new Expression[minArgs+1];
531     System.arraycopy(args, 0, xargs, 0, minArgs);
532     xargs[minArgs] = getCollator(args, minArgs);
533     return new ApplyExp(function, xargs);
534   }
535 
536   /** Adjust call to add default contex itemt if that argument is missing. */
withContext(Method method, Expression[] args, String name, int minArgs)537   Expression withContext (Method method, Expression[] args,
538                           String name, int minArgs)
539   {
540     String err = WrongArguments.checkArgCount(name, minArgs, minArgs+1,
541                                               args.length);
542     if (err != null)
543       return getCompilation().syntaxError(err);
544     if (args.length == minArgs)
545       {
546         Expression[] xargs = new Expression[minArgs+1];
547         System.arraycopy(args, 0, xargs, 0, minArgs);
548         Declaration dot = lookup.lookup(XQParser.DOT_VARNAME, false);
549         if (dot == null)
550           {
551             String message = "undefined context for " + name;
552             messages.error('e', message, "XPDY0002");
553             return new ErrorExp(message);
554           }
555         xargs[minArgs] = new ReferenceExp(dot);
556         args = xargs;
557       }
558     return new ApplyExp(method, args);
559   }
560 
checkArgCount(Expression[] args, Declaration decl, int min, int max)561   private Expression checkArgCount (Expression[] args, Declaration decl,
562                                     int min, int max)
563   {
564     String err = WrongArguments.checkArgCount("fn:"+decl.getName(),
565                                               min, max, args.length);
566     if (err == null)
567       return null;
568     else
569       return getCompilation().syntaxError(err);
570   }
571 
visitApplyExp(ApplyExp exp, Void ignored)572   protected Expression visitApplyExp (ApplyExp exp, Void ignored)
573   {
574     Expression func = exp.getFunction();
575     NamespaceBinding namespaceSave = parser.constructorNamespaces;
576     Object proc = exp.getFunctionValue();
577     if (proc instanceof MakeElement)
578       {
579         MakeElement mk = (MakeElement) proc;
580         NamespaceBinding nschain
581           = NamespaceBinding.nconc(mk.getNamespaceNodes(), namespaceSave);
582         mk.setNamespaceNodes(nschain);
583         parser.constructorNamespaces = nschain;
584       }
585     if (func instanceof ReferenceExp)
586       func = visitReferenceExp((ReferenceExp) func, exp);
587     else
588       func = visit(func, ignored);
589     exp.setFunction(func);
590     visitExps(exp.getArgs(), ignored);
591     parser.constructorNamespaces = namespaceSave;
592     func = exp.getFunction();
593     if (func instanceof ReferenceExp)
594       {
595 	Declaration decl = ((ReferenceExp) func).getBinding();
596 	int code;
597         Expression err;
598 	if (decl != null && (code = decl.getCode()) < 0)
599 	  {
600 	    switch (code)
601 	      {
602 	      case POSITION_BUILTIN:
603 	      case LAST_BUILTIN:
604 		Symbol sym = code == LAST_BUILTIN ? XQParser.LAST_VARNAME
605 		  : XQParser.POSITION_VARNAME;
606 		decl = lookup.lookup(sym, false);
607 		if (decl == null)
608 		  error('e', "undefined context for " + sym.getName());
609                 else
610                   // So CompileMisc:validateApplyValuesFilter
611                   // can tell whether last() is used.
612                   decl.setCanRead(true);
613 		return new ReferenceExp(sym, decl);
614               case CAST_AS_BUILTIN:
615               case CASTABLE_AS_BUILTIN:
616                 {
617 		  Expression[] args = exp.getArgs();
618                   Expression texp = args[code == CAST_AS_BUILTIN ? 0 : 1];
619                   Expression qexp = texp;
620                   if (texp instanceof ApplyExp)
621                     {
622                       ApplyExp taexp = (ApplyExp) texp;
623                       if (taexp.getFunction().valueIfConstant()
624                           == XQParser.proc_OccurrenceType_getInstance)
625                         qexp = taexp.getArg(0);
626                     }
627                   Object value = qexp.valueIfConstant();
628                   String msg = null;
629                   if (value == SingletonType.getInstance())
630                     msg = "type to 'cast as' or 'castable as' must be atomic";
631                   else if (value == XDataType.anyAtomicType)
632                     msg = "type to 'cast as' or 'castable as' cannot be anyAtomicType";
633                   else if (value == XDataType.anySimpleType)
634                     msg = "type to 'cast as' or 'castable as' cannot be anySimpleType";
635                   else if (value == XDataType.untypedType)
636                     msg = "type to 'cast as' or 'castable as' cannot be untyped";
637                   else if (value == XDataType.NotationType)
638                     msg = "type to 'cast as' or 'castable as' cannot be NOTATION";
639                   if (msg != null)
640                     messages.error('e', texp, msg, "XPST0080");
641                   boolean toQName = (value == Compilation.typeSymbol
642                                      && ! (texp instanceof ApplyExp));
643                   if (code == CAST_AS_BUILTIN)
644                     {
645                       if (toQName)
646                         return visitApplyExp(XQParser.castQName(args[1], true), ignored);
647                       func
648                         = XQParser.makeFunctionExp("gnu.xquery.util.CastAs", "castAs");
649                     }
650                   else
651                     {
652                       if (toQName && args[0] instanceof QuoteExp)
653                         {
654                           value = ((QuoteExp) args[0]).getValue();
655                           try
656                             {
657                               QNameUtils.resolveQName(value,
658                                                       parser.constructorNamespaces,
659                                                       parser.prologNamespaces);
660                               return XQuery.trueExp;
661                             }
662                           catch (RuntimeException ex)
663                             {
664                               return XQuery.falseExp;
665                             }
666                         }
667                       func = XQParser.makeFunctionExp("gnu.xquery.lang.XQParser",                                       "castableAs");
668                     }
669                   return new ApplyExp(func, args).setLine(exp);
670                 }
671 	      case XS_QNAME_BUILTIN:
672 	      case XS_QNAME_IGNORE_DEFAULT_BUILTIN:
673 		{
674 		  Expression[] args = exp.getArgs();
675                   if ((err = checkArgCount(args, decl, 1, 1)) != null)
676                     return err;
677                   NamespaceBinding constructorNamespaces
678                     = parser.constructorNamespaces;
679                   if (code == XS_QNAME_IGNORE_DEFAULT_BUILTIN)
680                     constructorNamespaces
681                       = new NamespaceBinding(null, "", constructorNamespaces);
682 		  if (args[0] instanceof QuoteExp)
683 		    {
684 		      try
685 			{
686 			  Object val = ((QuoteExp) args[0]).getValue();
687 			  val = QNameUtils.resolveQName(val,
688                                                         constructorNamespaces,
689                                                         parser.prologNamespaces);
690 			  return new QuoteExp(val);
691 			}
692 		      catch (RuntimeException ex)
693 			{
694 			  return getCompilation().syntaxError(ex.getMessage());
695 			}
696 		    }
697 		  Expression[] xargs = {
698 		    args[0],
699 		    new QuoteExp(constructorNamespaces),
700 		    new QuoteExp(parser.prologNamespaces) };
701 		  Method meth
702 		    = (ClassType.make("gnu.xquery.util.QNameUtils")
703 		       .getDeclaredMethod("resolveQName", 3));
704 		  ApplyExp app = new ApplyExp(meth, xargs);
705 		  app.setFlag(ApplyExp.INLINE_IF_CONSTANT);
706 		  return app;
707 		}
708 	      case RESOLVE_PREFIX_BUILTIN:
709 		{
710 		  Expression[] args = exp.getArgs();
711                   if ((err = checkArgCount(args, decl, 1, 1)) != null)
712                     return err;
713 		  if (args[0] instanceof QuoteExp)
714 		    {
715                       Object val = ((QuoteExp) args[0]).getValue();
716                       String prefix = val == null ? null : val.toString();
717                       val = QNameUtils.lookupPrefix(prefix,
718                                                     parser.constructorNamespaces,
719                                                     parser.prologNamespaces);
720                       if (val == null)
721                         return getCompilation()
722                           .syntaxError("unknown namespace prefix '"
723                                        +prefix+"'");
724                       return new QuoteExp(val);
725 		    }
726 		  Expression[] xargs = {
727 		    args[0],
728 		    new QuoteExp(parser.constructorNamespaces),
729 		    new QuoteExp(parser.prologNamespaces) };
730 		  PrimProcedure pproc
731 		    = new PrimProcedure(ClassType.make("gnu.xquery.util.QNameUtils")
732                                         .getDeclaredMethod("resolvePrefix", 3));
733 		  ApplyExp app = new ApplyExp(pproc, xargs);
734 		  app.setFlag(ApplyExp.INLINE_IF_CONSTANT);
735 		  return app;
736 		}
737               case LOCAL_NAME_BUILTIN:
738 		{
739                   Method meth = ClassType.make("gnu.xquery.util.NodeUtils")
740                     .getDeclaredMethod("localName", 1);
741                   return withContext(meth, exp.getArgs(), "fn:local-name", 0);
742                 }
743               case NAME_BUILTIN:
744 		{
745                   Method meth = ClassType.make("gnu.xquery.util.NodeUtils")
746                     .getDeclaredMethod("name", 1);
747                   return withContext(meth, exp.getArgs(), "fn:name", 0);
748                 }
749               case NUMBER_BUILTIN:
750 		{
751                   Method meth = ClassType.make("gnu.xquery.util.NumberValue")
752                     .getDeclaredMethod("numberValue", 1);
753                   return withContext(meth, exp.getArgs(), "fn:number", 0);
754                 }
755               case ROOT_BUILTIN:
756 		{
757                   Method meth = ClassType.make("gnu.xquery.util.NodeUtils")
758                     .getDeclaredMethod("root", 1);
759                   return withContext(meth, exp.getArgs(), "fn:root", 0);
760                 }
761               case BASE_URI_BUILTIN:
762 		{
763                   Method meth = ClassType.make("gnu.xquery.util.NodeUtils")
764                     .getDeclaredMethod("baseUri", 1);
765                   return withContext(meth, exp.getArgs(), "fn:base-uri", 0);
766                 }
767               case LANG_BUILTIN:
768 		{
769                   Method meth = ClassType.make("gnu.xquery.util.NodeUtils")
770                     .getDeclaredMethod("lang", 2);
771                   return withContext(meth, exp.getArgs(), "fn:lang", 1);
772                 }
773               case ID_BUILTIN:
774 		{
775                   Method meth = ClassType.make("gnu.xquery.util.NodeUtils")
776                     .getDeclaredMethod("id$X", 3);
777                   return withContext(meth, exp.getArgs(), "fn:id", 1);
778                 }
779               case IDREF_BUILTIN:
780 		{
781                   Method meth = ClassType.make("gnu.xquery.util.NodeUtils")
782                     .getDeclaredMethod("idref", 2);
783                   return withContext(meth, exp.getArgs(), "fn:idref", 1);
784                 }
785 
786               case STATIC_BASE_URI_BUILTIN:
787                 {
788 		  Expression[] args = exp.getArgs();
789                   if ((err = checkArgCount(args, decl, 0, 0)) != null)
790                     return err;
791                   return getBaseUriExpr();
792                 }
793               case NAMESPACE_URI_BUILTIN:
794 		{
795                   Method meth = ClassType.make("gnu.xquery.util.NodeUtils")
796                     .getDeclaredMethod("namespaceURI", 1);
797                   return withContext(meth, exp.getArgs(),
798                                      "fn:namespace-uri", 0);
799                 }
800 
801               case NORMALIZE_SPACE_BUILTIN:
802 		{
803                   Method meth = ClassType.make("gnu.xquery.util.StringUtils")
804                     .getDeclaredMethod("normalizeSpace", 1);
805                   return withContext(meth, exp.getArgs(),
806                                      "fn:normalize-space", 0);
807                 }
808 
809               case UNORDERED_BUILTIN:
810                 {
811 		  Expression[] args = exp.getArgs();
812                   if ((err = checkArgCount(args, decl, 1, 1)) != null)
813                     return err;
814                   return args[0];
815                 }
816 
817               case COMPARE_BUILTIN:
818 		{
819                   Method meth = ClassType.make("gnu.xquery.util.StringUtils")
820                     .getDeclaredMethod("compare", 3);
821                   return withCollator(meth, exp.getArgs(), "fn:compare", 2);
822                 }
823 
824               case STRING_BUILTIN:
825                 return withContext(ClassType.make("gnu.xml.TextUtils")
826                                    .getDeclaredMethod("asString", 1),
827                                    exp.getArgs(), "fn:string", 0);
828 
829               case INDEX_OF_BUILTIN:
830 		{
831                   Method meth = ClassType.make("gnu.xquery.util.SequenceUtils")
832                     .getDeclaredMethod("indexOf$X", 4);
833                   return withCollator(meth, exp.getArgs(), "fn:index-of", 2);
834                 }
835               case COLLECTION_BUILTIN:
836                 {
837                   Expression[] args = exp.getArgs();
838                   ClassType cl = ClassType.make("gnu.xquery.util.NodeUtils");
839                   Method meth = cl.getDeclaredMethod("collection", 2);
840                   if ((err = checkArgCount(args, decl, 0, 1)) != null)
841                     return err;
842                   Expression base = getBaseUriExpr();
843                   Expression uri = args.length > 0 ? args[0]
844                     : QuoteExp.voidExp;
845                   return new ApplyExp(meth, new Expression[]{ uri, base });
846                 }
847               case DOC_BUILTIN:
848               case DOC_AVAILABLE_BUILTIN:
849                 {
850                   Expression[] args = exp.getArgs();
851                   ClassType cl = ClassType.make("gnu.xquery.util.NodeUtils");
852                   String mname;
853                   if (code == DOC_BUILTIN)
854                     {
855                       mname = "docCached";
856                       if (XQParser.warnOldVersion
857                           && "document".equals(decl.getName()))
858                         getCompilation()
859                           .error('w', "replace 'document' by 'doc'");
860                     }
861                   else
862                     mname = "availableCached";
863                   Method meth = cl.getDeclaredMethod(mname, 2);
864                   if ((err = checkArgCount(args, decl, 1, 1)) != null)
865                     return err;
866                   PrimProcedure pproc = new PrimProcedure(meth);
867                   if (code == DOC_BUILTIN)
868                       pproc.setSideEffectFree();
869                   Expression base = getBaseUriExpr();
870                   ApplyExp aexp
871                     = new ApplyExp(pproc, new Expression[]{args[0], base});
872                   if (code == DOC_BUILTIN)
873                     aexp.setType(NodeType.documentNodeTest);
874                   else
875                     aexp.setType(XDataType.booleanType);
876                   return aexp;
877                 }
878               case RESOLVE_URI_BUILTIN:
879                 {
880                   Expression[] args = exp.getArgs();
881                   if ((err = checkArgCount(args, decl, 1, 2)) != null)
882                     return err;
883                   Expression[] margs = new Expression[2];
884                   margs[0] = args[0];
885                   if (args.length == 1)
886                     margs[1] = getBaseUriExpr();
887                   else
888                     margs[1] = args[1];
889                   Method meth = ClassType.make("gnu.xquery.util.QNameUtils")
890                     .getDeclaredMethod("resolveURI", 2);
891                   return new ApplyExp(meth, margs);
892                 }
893               case DISTINCT_VALUES_BUILTIN:
894                 {
895                   Method meth = ClassType.make("gnu.xquery.util.DistinctValues")
896                     .getDeclaredMethod("distinctValues$X", 3);
897                   return withCollator(meth, exp.getArgs(),
898                                       "fn:distinct-values", 1);
899 
900                 }
901               case DEEP_EQUAL_BUILTIN:
902                 {
903                   Method meth = ClassType.make("gnu.xquery.util.SequenceUtils")
904                     .getDeclaredMethod("deepEqual", 3);
905                   return withCollator(meth, exp.getArgs(),
906                                       "fn:deep-equal", 2);
907                 }
908               case MIN_BUILTIN:
909                 {
910                   Method meth = ClassType.make("gnu.xquery.util.MinMax")
911                     .getDeclaredMethod("min", 2);
912                   return withCollator(meth, exp.getArgs(),
913                                       "fn:min", 1);
914                 }
915               case MAX_BUILTIN:
916                 {
917                   Method meth = ClassType.make("gnu.xquery.util.MinMax")
918                     .getDeclaredMethod("max", 2);
919                   return withCollator(meth, exp.getArgs(),
920                                       "fn:max", 1);
921                 }
922               case DEFAULT_COLLATION_BUILTIN:
923                 if ((err = checkArgCount(exp.getArgs(), decl, 0, 0)) != null)
924                   return err;
925                 NamedCollator coll = parser.defaultCollator;
926                 return QuoteExp.getInstance(coll != null ? coll.getName()
927                           : NamedCollator.UNICODE_CODEPOINT_COLLATION);
928               case HANDLE_EXTENSION_BUILTIN:
929                 {
930                   Compilation comp = getCompilation();
931                   Expression[] args = exp.getArgs();
932                   int i = 0;
933                   for (;  i < args.length - 1;  i += 2)
934                     {
935                       Expression pname = args[i];
936                       String qname = (String) ((QuoteExp) pname).getValue();
937                       Symbol psymbol = parser.namespaceResolve(qname, false);
938                       if (psymbol == null)
939                         ; // error emitted in namespaceResolve
940                       else if (psymbol.getNamespaceURI().length() == 0)
941                         comp.error('e', "pragma name cannot be in the empty namespace");
942                       else
943                         {
944                           Expression replacement
945                             = checkPragma(psymbol, args[i+1]);
946                           if (replacement != null)
947                             return replacement;
948                         }
949                     }
950                   if (i < args.length)
951                     return args[args.length-1];
952                   String msg = "no recognized pragma or default in extension expression";
953                   getMessages().error('e', msg, "XQST0079");
954                   return new ErrorExp(msg);
955                 }
956 	      }
957 	  }
958       }
959     proc = exp.getFunctionValue();
960     if (proc instanceof Type)
961       {
962         Expression[] args = exp.getArgs();
963         if (args.length != 1)
964           {
965             messages.error('e', "type constructor requires a single argument");
966             return exp;
967           }
968         return new ApplyExp(XQParser.makeFunctionExp("gnu.xquery.util.CastAs", "castAs"),
969                             new Expression[] { exp.getFunction(), args[0] });
970       }
971     if (proc instanceof MakeElement)
972       {
973 	MakeElement make = (MakeElement) proc;
974 
975 	// Add namespaces nodes that might be needed.
976 	NamespaceBinding nsBindings = make.getNamespaceNodes();
977         Symbol tag = make.tag;
978         if (tag == null)
979           tag = MakeElement.getTagName(exp);
980 	nsBindings = maybeAddNamespace(tag, false, nsBindings);
981 	Expression[] args = exp.getArgs();
982         Symbol[] attrSyms = new Symbol[args.length];
983         int nattrSyms = 0;
984 	for (int i = 0;  i < args.length;  i++)
985 	  {
986 	    Expression arg = args[i];
987 	    if (arg instanceof ApplyExp)
988 	      {
989 		ApplyExp app = (ApplyExp) arg;
990 		if (app.getFunction() == MakeAttribute.makeAttributeExp)
991                   {
992                     Symbol sym = MakeElement.getTagName(app);
993                     if (sym != null)
994                       {
995                         for (int j = 0;  ;  j++)
996                           {
997                             if (j == nattrSyms)
998                               {
999                                 attrSyms[nattrSyms++] = sym;
1000                                 break;
1001                               }
1002                             if (sym.equals(attrSyms[j]))
1003                               {
1004                                 getCompilation().setLine(app);
1005                                 Symbol elementSym = MakeElement.getTagName(exp);
1006                                 String elementName = elementSym == null ? null
1007                                   : elementSym.toString();
1008                                 messages.error('e', XMLFilter.duplicateAttributeMessage(sym, elementName), "XQST0040");
1009                               }
1010                           }
1011                         nsBindings = maybeAddNamespace(sym, true, nsBindings);
1012                       }
1013                   }
1014 	      }
1015 	  }
1016 	if (nsBindings != null)
1017 	  make.setNamespaceNodes(nsBindings);
1018       }
1019     return exp;
1020   }
1021 
1022   public Expression
checkPragma(Symbol name, Expression contents)1023   checkPragma (Symbol name, Expression contents)
1024   {
1025     return null;
1026   }
1027 
getBaseUriExpr()1028   Expression getBaseUriExpr ()
1029   {
1030     Compilation comp = getCompilation();
1031     String staticBaseUri = parser.getStaticBaseUri();
1032     if (staticBaseUri != null)
1033       return QuoteExp.getInstance(staticBaseUri);
1034     else
1035       return gnu.kawa.functions.GetModuleClass.getModuleClassURI(comp);
1036   }
1037 
maybeAddNamespace(Symbol qname, boolean isAttribute, NamespaceBinding bindings)1038   static NamespaceBinding maybeAddNamespace(Symbol qname, boolean isAttribute,
1039 					    NamespaceBinding bindings)
1040   {
1041     if (qname == null) // Happens if prevously-reported unknown prefix.
1042       return bindings;
1043     String prefix = qname.getPrefix();
1044     String uri = qname.getNamespaceURI();
1045     if (prefix == "")
1046       prefix = null;
1047     if (uri == "")
1048       uri = null;
1049     if (isAttribute && prefix == null && uri == null)
1050       return bindings;
1051     return NamespaceBinding.maybeAdd(prefix, uri, bindings);
1052   }
1053 
1054   /** Wrap a (known) procedure value as a Declaration. */
procToDecl(Object symbol, Object val)1055   static Declaration procToDecl (Object symbol, Object val)
1056   {
1057     Declaration decl = new Declaration(symbol);
1058     decl.setProcedureDecl(true);
1059     decl.noteValue(new QuoteExp(val));
1060     decl.setFlag(Declaration.IS_CONSTANT);
1061     return decl;
1062   }
1063 }
1064