001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 /*
019 * $Id: Compiler.java 468655 2006-10-28 07:12:06Z minchau $
020 */
021 package org.apache.xpath.compiler;
022
023 import javax.xml.transform.ErrorListener;
024 import javax.xml.transform.SourceLocator;
025 import javax.xml.transform.TransformerException;
026
027 import org.apache.xalan.res.XSLMessages;
028 import org.apache.xml.dtm.Axis;
029 import org.apache.xml.dtm.DTMFilter;
030 import org.apache.xml.dtm.DTMIterator;
031 import org.apache.xml.utils.PrefixResolver;
032 import org.apache.xml.utils.QName;
033 import org.apache.xml.utils.SAXSourceLocator;
034 import org.apache.xpath.Expression;
035 import org.apache.xpath.axes.UnionPathIterator;
036 import org.apache.xpath.axes.WalkerFactory;
037 import org.apache.xpath.functions.FuncExtFunction;
038 import org.apache.xpath.functions.FuncExtFunctionAvailable;
039 import org.apache.xpath.functions.Function;
040 import org.apache.xpath.functions.WrongNumberArgsException;
041 import org.apache.xpath.objects.XNumber;
042 import org.apache.xpath.objects.XString;
043 import org.apache.xpath.operations.And;
044 import org.apache.xpath.operations.Div;
045 import org.apache.xpath.operations.Equals;
046 import org.apache.xpath.operations.Gt;
047 import org.apache.xpath.operations.Gte;
048 import org.apache.xpath.operations.Lt;
049 import org.apache.xpath.operations.Lte;
050 import org.apache.xpath.operations.Minus;
051 import org.apache.xpath.operations.Mod;
052 import org.apache.xpath.operations.Mult;
053 import org.apache.xpath.operations.Neg;
054 import org.apache.xpath.operations.NotEquals;
055 import org.apache.xpath.operations.Operation;
056 import org.apache.xpath.operations.Or;
057 import org.apache.xpath.operations.Plus;
058 import org.apache.xpath.operations.UnaryOperation;
059 import org.apache.xpath.operations.Variable;
060 import org.apache.xpath.patterns.FunctionPattern;
061 import org.apache.xpath.patterns.NodeTest;
062 import org.apache.xpath.patterns.StepPattern;
063 import org.apache.xpath.patterns.UnionPattern;
064 import org.apache.xpath.res.XPATHErrorResources;
065
066 /**
067 * An instance of this class compiles an XPath string expression into
068 * a Expression object. This class compiles the string into a sequence
069 * of operation codes (op map) and then builds from that into an Expression
070 * tree.
071 * @xsl.usage advanced
072 */
073 public class Compiler extends OpMap
074 {
075
076 /**
077 * Construct a Compiler object with a specific ErrorListener and
078 * SourceLocator where the expression is located.
079 *
080 * @param errorHandler Error listener where messages will be sent, or null
081 * if messages should be sent to System err.
082 * @param locator The location object where the expression lives, which
083 * may be null, but which, if not null, must be valid over
084 * the long haul, in other words, it will not be cloned.
085 * @param fTable The FunctionTable object where the xpath build-in
086 * functions are stored.
087 */
088 public Compiler(ErrorListener errorHandler, SourceLocator locator,
089 FunctionTable fTable)
090 {
091 m_errorHandler = errorHandler;
092 m_locator = locator;
093 m_functionTable = fTable;
094 }
095
096 /**
097 * Construct a Compiler instance that has a null error listener and a
098 * null source locator.
099 */
100 public Compiler()
101 {
102 m_errorHandler = null;
103 m_locator = null;
104 }
105
106 /**
107 * Execute the XPath object from a given opcode position.
108 * @param opPos The current position in the xpath.m_opMap array.
109 * @return The result of the XPath.
110 *
111 * @throws TransformerException if there is a syntax or other error.
112 * @xsl.usage advanced
113 */
114 public Expression compile(int opPos) throws TransformerException
115 {
116
117 int op = getOp(opPos);
118
119 Expression expr = null;
120 // System.out.println(getPatternString()+"op: "+op);
121 switch (op)
122 {
123 case OpCodes.OP_XPATH :
124 expr = compile(opPos + 2); break;
125 case OpCodes.OP_OR :
126 expr = or(opPos); break;
127 case OpCodes.OP_AND :
128 expr = and(opPos); break;
129 case OpCodes.OP_NOTEQUALS :
130 expr = notequals(opPos); break;
131 case OpCodes.OP_EQUALS :
132 expr = equals(opPos); break;
133 case OpCodes.OP_LTE :
134 expr = lte(opPos); break;
135 case OpCodes.OP_LT :
136 expr = lt(opPos); break;
137 case OpCodes.OP_GTE :
138 expr = gte(opPos); break;
139 case OpCodes.OP_GT :
140 expr = gt(opPos); break;
141 case OpCodes.OP_PLUS :
142 expr = plus(opPos); break;
143 case OpCodes.OP_MINUS :
144 expr = minus(opPos); break;
145 case OpCodes.OP_MULT :
146 expr = mult(opPos); break;
147 case OpCodes.OP_DIV :
148 expr = div(opPos); break;
149 case OpCodes.OP_MOD :
150 expr = mod(opPos); break;
151 // case OpCodes.OP_QUO :
152 // expr = quo(opPos); break;
153 case OpCodes.OP_NEG :
154 expr = neg(opPos); break;
155 case OpCodes.OP_STRING :
156 expr = string(opPos); break;
157 case OpCodes.OP_BOOL :
158 expr = bool(opPos); break;
159 case OpCodes.OP_NUMBER :
160 expr = number(opPos); break;
161 case OpCodes.OP_UNION :
162 expr = union(opPos); break;
163 case OpCodes.OP_LITERAL :
164 expr = literal(opPos); break;
165 case OpCodes.OP_VARIABLE :
166 expr = variable(opPos); break;
167 case OpCodes.OP_GROUP :
168 expr = group(opPos); break;
169 case OpCodes.OP_NUMBERLIT :
170 expr = numberlit(opPos); break;
171 case OpCodes.OP_ARGUMENT :
172 expr = arg(opPos); break;
173 case OpCodes.OP_EXTFUNCTION :
174 expr = compileExtension(opPos); break;
175 case OpCodes.OP_FUNCTION :
176 expr = compileFunction(opPos); break;
177 case OpCodes.OP_LOCATIONPATH :
178 expr = locationPath(opPos); break;
179 case OpCodes.OP_PREDICATE :
180 expr = null; break; // should never hit this here.
181 case OpCodes.OP_MATCHPATTERN :
182 expr = matchPattern(opPos + 2); break;
183 case OpCodes.OP_LOCATIONPATHPATTERN :
184 expr = locationPathPattern(opPos); break;
185 case OpCodes.OP_QUO:
186 error(XPATHErrorResources.ER_UNKNOWN_OPCODE,
187 new Object[]{ "quo" }); //"ERROR! Unknown op code: "+m_opMap[opPos]);
188 break;
189 default :
190 error(XPATHErrorResources.ER_UNKNOWN_OPCODE,
191 new Object[]{ Integer.toString(getOp(opPos)) }); //"ERROR! Unknown op code: "+m_opMap[opPos]);
192 }
193 // if(null != expr)
194 // expr.setSourceLocator(m_locator);
195
196 return expr;
197 }
198
199 /**
200 * Bottle-neck compilation of an operation with left and right operands.
201 *
202 * @param operation non-null reference to parent operation.
203 * @param opPos The op map position of the parent operation.
204 *
205 * @return reference to {@link org.apache.xpath.operations.Operation} instance.
206 *
207 * @throws TransformerException if there is a syntax or other error.
208 */
209 private Expression compileOperation(Operation operation, int opPos)
210 throws TransformerException
211 {
212
213 int leftPos = getFirstChildPos(opPos);
214 int rightPos = getNextOpPos(leftPos);
215
216 operation.setLeftRight(compile(leftPos), compile(rightPos));
217
218 return operation;
219 }
220
221 /**
222 * Bottle-neck compilation of a unary operation.
223 *
224 * @param unary The parent unary operation.
225 * @param opPos The position in the op map of the parent operation.
226 *
227 * @return The unary argument.
228 *
229 * @throws TransformerException if syntax or other error occurs.
230 */
231 private Expression compileUnary(UnaryOperation unary, int opPos)
232 throws TransformerException
233 {
234
235 int rightPos = getFirstChildPos(opPos);
236
237 unary.setRight(compile(rightPos));
238
239 return unary;
240 }
241
242 /**
243 * Compile an 'or' operation.
244 *
245 * @param opPos The current position in the m_opMap array.
246 *
247 * @return reference to {@link org.apache.xpath.operations.Or} instance.
248 *
249 * @throws TransformerException if a error occurs creating the Expression.
250 */
251 protected Expression or(int opPos) throws TransformerException
252 {
253 return compileOperation(new Or(), opPos);
254 }
255
256 /**
257 * Compile an 'and' operation.
258 *
259 * @param opPos The current position in the m_opMap array.
260 *
261 * @return reference to {@link org.apache.xpath.operations.And} instance.
262 *
263 * @throws TransformerException if a error occurs creating the Expression.
264 */
265 protected Expression and(int opPos) throws TransformerException
266 {
267 return compileOperation(new And(), opPos);
268 }
269
270 /**
271 * Compile a '!=' operation.
272 *
273 * @param opPos The current position in the m_opMap array.
274 *
275 * @return reference to {@link org.apache.xpath.operations.NotEquals} instance.
276 *
277 * @throws TransformerException if a error occurs creating the Expression.
278 */
279 protected Expression notequals(int opPos) throws TransformerException
280 {
281 return compileOperation(new NotEquals(), opPos);
282 }
283
284 /**
285 * Compile a '=' operation.
286 *
287 * @param opPos The current position in the m_opMap array.
288 *
289 * @return reference to {@link org.apache.xpath.operations.Equals} instance.
290 *
291 * @throws TransformerException if a error occurs creating the Expression.
292 */
293 protected Expression equals(int opPos) throws TransformerException
294 {
295 return compileOperation(new Equals(), opPos);
296 }
297
298 /**
299 * Compile a '<=' operation.
300 *
301 * @param opPos The current position in the m_opMap array.
302 *
303 * @return reference to {@link org.apache.xpath.operations.Lte} instance.
304 *
305 * @throws TransformerException if a error occurs creating the Expression.
306 */
307 protected Expression lte(int opPos) throws TransformerException
308 {
309 return compileOperation(new Lte(), opPos);
310 }
311
312 /**
313 * Compile a '<' operation.
314 *
315 * @param opPos The current position in the m_opMap array.
316 *
317 * @return reference to {@link org.apache.xpath.operations.Lt} instance.
318 *
319 * @throws TransformerException if a error occurs creating the Expression.
320 */
321 protected Expression lt(int opPos) throws TransformerException
322 {
323 return compileOperation(new Lt(), opPos);
324 }
325
326 /**
327 * Compile a '>=' operation.
328 *
329 * @param opPos The current position in the m_opMap array.
330 *
331 * @return reference to {@link org.apache.xpath.operations.Gte} instance.
332 *
333 * @throws TransformerException if a error occurs creating the Expression.
334 */
335 protected Expression gte(int opPos) throws TransformerException
336 {
337 return compileOperation(new Gte(), opPos);
338 }
339
340 /**
341 * Compile a '>' operation.
342 *
343 * @param opPos The current position in the m_opMap array.
344 *
345 * @return reference to {@link org.apache.xpath.operations.Gt} instance.
346 *
347 * @throws TransformerException if a error occurs creating the Expression.
348 */
349 protected Expression gt(int opPos) throws TransformerException
350 {
351 return compileOperation(new Gt(), opPos);
352 }
353
354 /**
355 * Compile a '+' operation.
356 *
357 * @param opPos The current position in the m_opMap array.
358 *
359 * @return reference to {@link org.apache.xpath.operations.Plus} instance.
360 *
361 * @throws TransformerException if a error occurs creating the Expression.
362 */
363 protected Expression plus(int opPos) throws TransformerException
364 {
365 return compileOperation(new Plus(), opPos);
366 }
367
368 /**
369 * Compile a '-' operation.
370 *
371 * @param opPos The current position in the m_opMap array.
372 *
373 * @return reference to {@link org.apache.xpath.operations.Minus} instance.
374 *
375 * @throws TransformerException if a error occurs creating the Expression.
376 */
377 protected Expression minus(int opPos) throws TransformerException
378 {
379 return compileOperation(new Minus(), opPos);
380 }
381
382 /**
383 * Compile a '*' operation.
384 *
385 * @param opPos The current position in the m_opMap array.
386 *
387 * @return reference to {@link org.apache.xpath.operations.Mult} instance.
388 *
389 * @throws TransformerException if a error occurs creating the Expression.
390 */
391 protected Expression mult(int opPos) throws TransformerException
392 {
393 return compileOperation(new Mult(), opPos);
394 }
395
396 /**
397 * Compile a 'div' operation.
398 *
399 * @param opPos The current position in the m_opMap array.
400 *
401 * @return reference to {@link org.apache.xpath.operations.Div} instance.
402 *
403 * @throws TransformerException if a error occurs creating the Expression.
404 */
405 protected Expression div(int opPos) throws TransformerException
406 {
407 return compileOperation(new Div(), opPos);
408 }
409
410 /**
411 * Compile a 'mod' operation.
412 *
413 * @param opPos The current position in the m_opMap array.
414 *
415 * @return reference to {@link org.apache.xpath.operations.Mod} instance.
416 *
417 * @throws TransformerException if a error occurs creating the Expression.
418 */
419 protected Expression mod(int opPos) throws TransformerException
420 {
421 return compileOperation(new Mod(), opPos);
422 }
423
424 /*
425 * Compile a 'quo' operation.
426 *
427 * @param opPos The current position in the m_opMap array.
428 *
429 * @return reference to {@link org.apache.xpath.operations.Quo} instance.
430 *
431 * @throws TransformerException if a error occurs creating the Expression.
432 */
433 // protected Expression quo(int opPos) throws TransformerException
434 // {
435 // return compileOperation(new Quo(), opPos);
436 // }
437
438 /**
439 * Compile a unary '-' operation.
440 *
441 * @param opPos The current position in the m_opMap array.
442 *
443 * @return reference to {@link org.apache.xpath.operations.Neg} instance.
444 *
445 * @throws TransformerException if a error occurs creating the Expression.
446 */
447 protected Expression neg(int opPos) throws TransformerException
448 {
449 return compileUnary(new Neg(), opPos);
450 }
451
452 /**
453 * Compile a 'string(...)' operation.
454 *
455 * @param opPos The current position in the m_opMap array.
456 *
457 * @return reference to {@link org.apache.xpath.operations.String} instance.
458 *
459 * @throws TransformerException if a error occurs creating the Expression.
460 */
461 protected Expression string(int opPos) throws TransformerException
462 {
463 return compileUnary(new org.apache.xpath.operations.String(), opPos);
464 }
465
466 /**
467 * Compile a 'boolean(...)' operation.
468 *
469 * @param opPos The current position in the m_opMap array.
470 *
471 * @return reference to {@link org.apache.xpath.operations.Bool} instance.
472 *
473 * @throws TransformerException if a error occurs creating the Expression.
474 */
475 protected Expression bool(int opPos) throws TransformerException
476 {
477 return compileUnary(new org.apache.xpath.operations.Bool(), opPos);
478 }
479
480 /**
481 * Compile a 'number(...)' operation.
482 *
483 * @param opPos The current position in the m_opMap array.
484 *
485 * @return reference to {@link org.apache.xpath.operations.Number} instance.
486 *
487 * @throws TransformerException if a error occurs creating the Expression.
488 */
489 protected Expression number(int opPos) throws TransformerException
490 {
491 return compileUnary(new org.apache.xpath.operations.Number(), opPos);
492 }
493
494 /**
495 * Compile a literal string value.
496 *
497 * @param opPos The current position in the m_opMap array.
498 *
499 * @return reference to {@link org.apache.xpath.objects.XString} instance.
500 *
501 * @throws TransformerException if a error occurs creating the Expression.
502 */
503 protected Expression literal(int opPos)
504 {
505
506 opPos = getFirstChildPos(opPos);
507
508 return (XString) getTokenQueue().elementAt(getOp(opPos));
509 }
510
511 /**
512 * Compile a literal number value.
513 *
514 * @param opPos The current position in the m_opMap array.
515 *
516 * @return reference to {@link org.apache.xpath.objects.XNumber} instance.
517 *
518 * @throws TransformerException if a error occurs creating the Expression.
519 */
520 protected Expression numberlit(int opPos)
521 {
522
523 opPos = getFirstChildPos(opPos);
524
525 return (XNumber) getTokenQueue().elementAt(getOp(opPos));
526 }
527
528 /**
529 * Compile a variable reference.
530 *
531 * @param opPos The current position in the m_opMap array.
532 *
533 * @return reference to {@link org.apache.xpath.operations.Variable} instance.
534 *
535 * @throws TransformerException if a error occurs creating the Expression.
536 */
537 protected Expression variable(int opPos) throws TransformerException
538 {
539
540 Variable var = new Variable();
541
542 opPos = getFirstChildPos(opPos);
543
544 int nsPos = getOp(opPos);
545 java.lang.String namespace
546 = (OpCodes.EMPTY == nsPos) ? null
547 : (java.lang.String) getTokenQueue().elementAt(nsPos);
548 java.lang.String localname
549 = (java.lang.String) getTokenQueue().elementAt(getOp(opPos+1));
550 QName qname = new QName(namespace, localname);
551
552 var.setQName(qname);
553
554 return var;
555 }
556
557 /**
558 * Compile an expression group.
559 *
560 * @param opPos The current position in the m_opMap array.
561 *
562 * @return reference to the contained expression.
563 *
564 * @throws TransformerException if a error occurs creating the Expression.
565 */
566 protected Expression group(int opPos) throws TransformerException
567 {
568
569 // no-op
570 return compile(opPos + 2);
571 }
572
573 /**
574 * Compile a function argument.
575 *
576 * @param opPos The current position in the m_opMap array.
577 *
578 * @return reference to the argument expression.
579 *
580 * @throws TransformerException if a error occurs creating the Expression.
581 */
582 protected Expression arg(int opPos) throws TransformerException
583 {
584
585 // no-op
586 return compile(opPos + 2);
587 }
588
589 /**
590 * Compile a location path union. The UnionPathIterator itself may create
591 * {@link org.apache.xpath.axes.LocPathIterator} children.
592 *
593 * @param opPos The current position in the m_opMap array.
594 *
595 * @return reference to {@link org.apache.xpath.axes.LocPathIterator} instance.
596 *
597 * @throws TransformerException if a error occurs creating the Expression.
598 */
599 protected Expression union(int opPos) throws TransformerException
600 {
601 locPathDepth++;
602 try
603 {
604 return UnionPathIterator.createUnionIterator(this, opPos);
605 }
606 finally
607 {
608 locPathDepth--;
609 }
610 }
611
612 private int locPathDepth = -1;
613
614 /**
615 * Get the level of the location path or union being constructed.
616 * @return 0 if it is a top-level path.
617 */
618 public int getLocationPathDepth()
619 {
620 return locPathDepth;
621 }
622
623 /**
624 * Get the function table
625 */
626 FunctionTable getFunctionTable()
627 {
628 return m_functionTable;
629 }
630
631 /**
632 * Compile a location path. The LocPathIterator itself may create
633 * {@link org.apache.xpath.axes.AxesWalker} children.
634 *
635 * @param opPos The current position in the m_opMap array.
636 *
637 * @return reference to {@link org.apache.xpath.axes.LocPathIterator} instance.
638 *
639 * @throws TransformerException if a error occurs creating the Expression.
640 */
641 public Expression locationPath(int opPos) throws TransformerException
642 {
643 locPathDepth++;
644 try
645 {
646 DTMIterator iter = WalkerFactory.newDTMIterator(this, opPos, (locPathDepth == 0));
647 return (Expression)iter; // cast OK, I guess.
648 }
649 finally
650 {
651 locPathDepth--;
652 }
653 }
654
655 /**
656 * Compile a location step predicate expression.
657 *
658 * @param opPos The current position in the m_opMap array.
659 *
660 * @return the contained predicate expression.
661 *
662 * @throws TransformerException if a error occurs creating the Expression.
663 */
664 public Expression predicate(int opPos) throws TransformerException
665 {
666 return compile(opPos + 2);
667 }
668
669 /**
670 * Compile an entire match pattern expression.
671 *
672 * @param opPos The current position in the m_opMap array.
673 *
674 * @return reference to {@link org.apache.xpath.patterns.UnionPattern} instance.
675 *
676 * @throws TransformerException if a error occurs creating the Expression.
677 */
678 protected Expression matchPattern(int opPos) throws TransformerException
679 {
680 locPathDepth++;
681 try
682 {
683 // First, count...
684 int nextOpPos = opPos;
685 int i;
686
687 for (i = 0; getOp(nextOpPos) == OpCodes.OP_LOCATIONPATHPATTERN; i++)
688 {
689 nextOpPos = getNextOpPos(nextOpPos);
690 }
691
692 if (i == 1)
693 return compile(opPos);
694
695 UnionPattern up = new UnionPattern();
696 StepPattern[] patterns = new StepPattern[i];
697
698 for (i = 0; getOp(opPos) == OpCodes.OP_LOCATIONPATHPATTERN; i++)
699 {
700 nextOpPos = getNextOpPos(opPos);
701 patterns[i] = (StepPattern) compile(opPos);
702 opPos = nextOpPos;
703 }
704
705 up.setPatterns(patterns);
706
707 return up;
708 }
709 finally
710 {
711 locPathDepth--;
712 }
713 }
714
715 /**
716 * Compile a location match pattern unit expression.
717 *
718 * @param opPos The current position in the m_opMap array.
719 *
720 * @return reference to {@link org.apache.xpath.patterns.StepPattern} instance.
721 *
722 * @throws TransformerException if a error occurs creating the Expression.
723 */
724 public Expression locationPathPattern(int opPos)
725 throws TransformerException
726 {
727
728 opPos = getFirstChildPos(opPos);
729
730 return stepPattern(opPos, 0, null);
731 }
732
733 /**
734 * Get a {@link org.w3c.dom.traversal.NodeFilter} bit set that tells what
735 * to show for a given node test.
736 *
737 * @param opPos the op map position for the location step.
738 *
739 * @return {@link org.w3c.dom.traversal.NodeFilter} bit set that tells what
740 * to show for a given node test.
741 */
742 public int getWhatToShow(int opPos)
743 {
744
745 int axesType = getOp(opPos);
746 int testType = getOp(opPos + 3);
747
748 // System.out.println("testType: "+testType);
749 switch (testType)
750 {
751 case OpCodes.NODETYPE_COMMENT :
752 return DTMFilter.SHOW_COMMENT;
753 case OpCodes.NODETYPE_TEXT :
754 // return DTMFilter.SHOW_TEXT | DTMFilter.SHOW_COMMENT;
755 return DTMFilter.SHOW_TEXT | DTMFilter.SHOW_CDATA_SECTION ;
756 case OpCodes.NODETYPE_PI :
757 return DTMFilter.SHOW_PROCESSING_INSTRUCTION;
758 case OpCodes.NODETYPE_NODE :
759 // return DTMFilter.SHOW_ALL;
760 switch (axesType)
761 {
762 case OpCodes.FROM_NAMESPACE:
763 return DTMFilter.SHOW_NAMESPACE;
764 case OpCodes.FROM_ATTRIBUTES :
765 case OpCodes.MATCH_ATTRIBUTE :
766 return DTMFilter.SHOW_ATTRIBUTE;
767 case OpCodes.FROM_SELF:
768 case OpCodes.FROM_ANCESTORS_OR_SELF:
769 case OpCodes.FROM_DESCENDANTS_OR_SELF:
770 return DTMFilter.SHOW_ALL;
771 default:
772 if (getOp(0) == OpCodes.OP_MATCHPATTERN)
773 return ~DTMFilter.SHOW_ATTRIBUTE
774 & ~DTMFilter.SHOW_DOCUMENT
775 & ~DTMFilter.SHOW_DOCUMENT_FRAGMENT;
776 else
777 return ~DTMFilter.SHOW_ATTRIBUTE;
778 }
779 case OpCodes.NODETYPE_ROOT :
780 return DTMFilter.SHOW_DOCUMENT | DTMFilter.SHOW_DOCUMENT_FRAGMENT;
781 case OpCodes.NODETYPE_FUNCTEST :
782 return NodeTest.SHOW_BYFUNCTION;
783 case OpCodes.NODENAME :
784 switch (axesType)
785 {
786 case OpCodes.FROM_NAMESPACE :
787 return DTMFilter.SHOW_NAMESPACE;
788 case OpCodes.FROM_ATTRIBUTES :
789 case OpCodes.MATCH_ATTRIBUTE :
790 return DTMFilter.SHOW_ATTRIBUTE;
791
792 // break;
793 case OpCodes.MATCH_ANY_ANCESTOR :
794 case OpCodes.MATCH_IMMEDIATE_ANCESTOR :
795 return DTMFilter.SHOW_ELEMENT;
796
797 // break;
798 default :
799 return DTMFilter.SHOW_ELEMENT;
800 }
801 default :
802 // System.err.println("We should never reach here.");
803 return DTMFilter.SHOW_ALL;
804 }
805 }
806
807 private static final boolean DEBUG = false;
808
809 /**
810 * Compile a step pattern unit expression, used for both location paths
811 * and match patterns.
812 *
813 * @param opPos The current position in the m_opMap array.
814 * @param stepCount The number of steps to expect.
815 * @param ancestorPattern The owning StepPattern, which may be null.
816 *
817 * @return reference to {@link org.apache.xpath.patterns.StepPattern} instance.
818 *
819 * @throws TransformerException if a error occurs creating the Expression.
820 */
821 protected StepPattern stepPattern(
822 int opPos, int stepCount, StepPattern ancestorPattern)
823 throws TransformerException
824 {
825
826 int startOpPos = opPos;
827 int stepType = getOp(opPos);
828
829 if (OpCodes.ENDOP == stepType)
830 {
831 return null;
832 }
833
834 boolean addMagicSelf = true;
835
836 int endStep = getNextOpPos(opPos);
837
838 // int nextStepType = getOpMap()[endStep];
839 StepPattern pattern;
840
841 // boolean isSimple = ((OpCodes.ENDOP == nextStepType) && (stepCount == 0));
842 int argLen;
843
844 switch (stepType)
845 {
846 case OpCodes.OP_FUNCTION :
847 if(DEBUG)
848 System.out.println("MATCH_FUNCTION: "+m_currentPattern);
849 addMagicSelf = false;
850 argLen = getOp(opPos + OpMap.MAPINDEX_LENGTH);
851 pattern = new FunctionPattern(compileFunction(opPos), Axis.PARENT, Axis.CHILD);
852 break;
853 case OpCodes.FROM_ROOT :
854 if(DEBUG)
855 System.out.println("FROM_ROOT, "+m_currentPattern);
856 addMagicSelf = false;
857 argLen = getArgLengthOfStep(opPos);
858 opPos = getFirstChildPosOfStep(opPos);
859 pattern = new StepPattern(DTMFilter.SHOW_DOCUMENT |
860 DTMFilter.SHOW_DOCUMENT_FRAGMENT,
861 Axis.PARENT, Axis.CHILD);
862 break;
863 case OpCodes.MATCH_ATTRIBUTE :
864 if(DEBUG)
865 System.out.println("MATCH_ATTRIBUTE: "+getStepLocalName(startOpPos)+", "+m_currentPattern);
866 argLen = getArgLengthOfStep(opPos);
867 opPos = getFirstChildPosOfStep(opPos);
868 pattern = new StepPattern(DTMFilter.SHOW_ATTRIBUTE,
869 getStepNS(startOpPos),
870 getStepLocalName(startOpPos),
871 Axis.PARENT, Axis.ATTRIBUTE);
872 break;
873 case OpCodes.MATCH_ANY_ANCESTOR :
874 if(DEBUG)
875 System.out.println("MATCH_ANY_ANCESTOR: "+getStepLocalName(startOpPos)+", "+m_currentPattern);
876 argLen = getArgLengthOfStep(opPos);
877 opPos = getFirstChildPosOfStep(opPos);
878 int what = getWhatToShow(startOpPos);
879 // bit-o-hackery, but this code is due for the morgue anyway...
880 if(0x00000500 == what)
881 addMagicSelf = false;
882 pattern = new StepPattern(getWhatToShow(startOpPos),
883 getStepNS(startOpPos),
884 getStepLocalName(startOpPos),
885 Axis.ANCESTOR, Axis.CHILD);
886 break;
887 case OpCodes.MATCH_IMMEDIATE_ANCESTOR :
888 if(DEBUG)
889 System.out.println("MATCH_IMMEDIATE_ANCESTOR: "+getStepLocalName(startOpPos)+", "+m_currentPattern);
890 argLen = getArgLengthOfStep(opPos);
891 opPos = getFirstChildPosOfStep(opPos);
892 pattern = new StepPattern(getWhatToShow(startOpPos),
893 getStepNS(startOpPos),
894 getStepLocalName(startOpPos),
895 Axis.PARENT, Axis.CHILD);
896 break;
897 default :
898 error(XPATHErrorResources.ER_UNKNOWN_MATCH_OPERATION, null); //"unknown match operation!");
899
900 return null;
901 }
902
903 pattern.setPredicates(getCompiledPredicates(opPos + argLen));
904 if(null == ancestorPattern)
905 {
906 // This is the magic and invisible "." at the head of every
907 // match pattern, and corresponds to the current node in the context
908 // list, from where predicates are counted.
909 // So, in order to calculate "foo[3]", it has to count from the
910 // current node in the context list, so, from that current node,
911 // the full pattern is really "self::node()/child::foo[3]". If you
912 // translate this to a select pattern from the node being tested,
913 // which is really how we're treating match patterns, it works out to
914 // self::foo/parent::node[child::foo[3]]", or close enough.
915 /* if(addMagicSelf && pattern.getPredicateCount() > 0)
916 {
917 StepPattern selfPattern = new StepPattern(DTMFilter.SHOW_ALL,
918 Axis.PARENT, Axis.CHILD);
919 // We need to keep the new nodetest from affecting the score...
920 XNumber score = pattern.getStaticScore();
921 pattern.setRelativePathPattern(selfPattern);
922 pattern.setStaticScore(score);
923 selfPattern.setStaticScore(score);
924 }*/
925 }
926 else
927 {
928 // System.out.println("Setting "+ancestorPattern+" as relative to "+pattern);
929 pattern.setRelativePathPattern(ancestorPattern);
930 }
931
932 StepPattern relativePathPattern = stepPattern(endStep, stepCount + 1,
933 pattern);
934
935 return (null != relativePathPattern) ? relativePathPattern : pattern;
936 }
937
938 /**
939 * Compile a zero or more predicates for a given match pattern.
940 *
941 * @param opPos The position of the first predicate the m_opMap array.
942 *
943 * @return reference to array of {@link org.apache.xpath.Expression} instances.
944 *
945 * @throws TransformerException if a error occurs creating the Expression.
946 */
947 public Expression[] getCompiledPredicates(int opPos)
948 throws TransformerException
949 {
950
951 int count = countPredicates(opPos);
952
953 if (count > 0)
954 {
955 Expression[] predicates = new Expression[count];
956
957 compilePredicates(opPos, predicates);
958
959 return predicates;
960 }
961
962 return null;
963 }
964
965 /**
966 * Count the number of predicates in the step.
967 *
968 * @param opPos The position of the first predicate the m_opMap array.
969 *
970 * @return The number of predicates for this step.
971 *
972 * @throws TransformerException if a error occurs creating the Expression.
973 */
974 public int countPredicates(int opPos) throws TransformerException
975 {
976
977 int count = 0;
978
979 while (OpCodes.OP_PREDICATE == getOp(opPos))
980 {
981 count++;
982
983 opPos = getNextOpPos(opPos);
984 }
985
986 return count;
987 }
988
989 /**
990 * Compiles predicates in the step.
991 *
992 * @param opPos The position of the first predicate the m_opMap array.
993 * @param predicates An empty pre-determined array of
994 * {@link org.apache.xpath.Expression}s, that will be filled in.
995 *
996 * @throws TransformerException
997 */
998 private void compilePredicates(int opPos, Expression[] predicates)
999 throws TransformerException
1000 {
1001
1002 for (int i = 0; OpCodes.OP_PREDICATE == getOp(opPos); i++)
1003 {
1004 predicates[i] = predicate(opPos);
1005 opPos = getNextOpPos(opPos);
1006 }
1007 }
1008
1009 /**
1010 * Compile a built-in XPath function.
1011 *
1012 * @param opPos The current position in the m_opMap array.
1013 *
1014 * @return reference to {@link org.apache.xpath.functions.Function} instance.
1015 *
1016 * @throws TransformerException if a error occurs creating the Expression.
1017 */
1018 Expression compileFunction(int opPos) throws TransformerException
1019 {
1020
1021 int endFunc = opPos + getOp(opPos + 1) - 1;
1022
1023 opPos = getFirstChildPos(opPos);
1024
1025 int funcID = getOp(opPos);
1026
1027 opPos++;
1028
1029 if (-1 != funcID)
1030 {
1031 Function func = m_functionTable.getFunction(funcID);
1032
1033 /**
1034 * It is a trick for function-available. Since the function table is an
1035 * instance field, insert this table at compilation time for later usage
1036 */
1037
1038 if (func instanceof FuncExtFunctionAvailable)
1039 ((FuncExtFunctionAvailable) func).setFunctionTable(m_functionTable);
1040
1041 func.postCompileStep(this);
1042
1043 try
1044 {
1045 int i = 0;
1046
1047 for (int p = opPos; p < endFunc; p = getNextOpPos(p), i++)
1048 {
1049
1050 // System.out.println("argPos: "+ p);
1051 // System.out.println("argCode: "+ m_opMap[p]);
1052 func.setArg(compile(p), i);
1053 }
1054
1055 func.checkNumberArgs(i);
1056 }
1057 catch (WrongNumberArgsException wnae)
1058 {
1059 java.lang.String name = m_functionTable.getFunctionName(funcID);
1060
1061 m_errorHandler.fatalError( new TransformerException(
1062 XSLMessages.createXPATHMessage(XPATHErrorResources.ER_ONLY_ALLOWS,
1063 new Object[]{name, wnae.getMessage()}), m_locator));
1064 //"name + " only allows " + wnae.getMessage() + " arguments", m_locator));
1065 }
1066
1067 return func;
1068 }
1069 else
1070 {
1071 error(XPATHErrorResources.ER_FUNCTION_TOKEN_NOT_FOUND, null); //"function token not found.");
1072
1073 return null;
1074 }
1075 }
1076
1077 // The current id for extension functions.
1078 private static long s_nextMethodId = 0;
1079
1080 /**
1081 * Get the next available method id
1082 */
1083 synchronized private long getNextMethodId()
1084 {
1085 if (s_nextMethodId == Long.MAX_VALUE)
1086 s_nextMethodId = 0;
1087
1088 return s_nextMethodId++;
1089 }
1090
1091 /**
1092 * Compile an extension function.
1093 *
1094 * @param opPos The current position in the m_opMap array.
1095 *
1096 * @return reference to {@link org.apache.xpath.functions.FuncExtFunction} instance.
1097 *
1098 * @throws TransformerException if a error occurs creating the Expression.
1099 */
1100 private Expression compileExtension(int opPos)
1101 throws TransformerException
1102 {
1103
1104 int endExtFunc = opPos + getOp(opPos + 1) - 1;
1105
1106 opPos = getFirstChildPos(opPos);
1107
1108 java.lang.String ns = (java.lang.String) getTokenQueue().elementAt(getOp(opPos));
1109
1110 opPos++;
1111
1112 java.lang.String funcName =
1113 (java.lang.String) getTokenQueue().elementAt(getOp(opPos));
1114
1115 opPos++;
1116
1117 // We create a method key to uniquely identify this function so that we
1118 // can cache the object needed to invoke it. This way, we only pay the
1119 // reflection overhead on the first call.
1120
1121 Function extension = new FuncExtFunction(ns, funcName, String.valueOf(getNextMethodId()));
1122
1123 try
1124 {
1125 int i = 0;
1126
1127 while (opPos < endExtFunc)
1128 {
1129 int nextOpPos = getNextOpPos(opPos);
1130
1131 extension.setArg(this.compile(opPos), i);
1132
1133 opPos = nextOpPos;
1134
1135 i++;
1136 }
1137 }
1138 catch (WrongNumberArgsException wnae)
1139 {
1140 ; // should never happen
1141 }
1142
1143 return extension;
1144 }
1145
1146 /**
1147 * Warn the user of an problem.
1148 *
1149 * @param msg An error msgkey that corresponds to one of the constants found
1150 * in {@link org.apache.xpath.res.XPATHErrorResources}, which is
1151 * a key for a format string.
1152 * @param args An array of arguments represented in the format string, which
1153 * may be null.
1154 *
1155 * @throws TransformerException if the current ErrorListoner determines to
1156 * throw an exception.
1157 */
1158 public void warn(String msg, Object[] args) throws TransformerException
1159 {
1160
1161 java.lang.String fmsg = XSLMessages.createXPATHWarning(msg, args);
1162
1163 if (null != m_errorHandler)
1164 {
1165 m_errorHandler.warning(new TransformerException(fmsg, m_locator));
1166 }
1167 else
1168 {
1169 System.out.println(fmsg
1170 +"; file "+m_locator.getSystemId()
1171 +"; line "+m_locator.getLineNumber()
1172 +"; column "+m_locator.getColumnNumber());
1173 }
1174 }
1175
1176 /**
1177 * Tell the user of an assertion error, and probably throw an
1178 * exception.
1179 *
1180 * @param b If false, a runtime exception will be thrown.
1181 * @param msg The assertion message, which should be informative.
1182 *
1183 * @throws RuntimeException if the b argument is false.
1184 */
1185 public void assertion(boolean b, java.lang.String msg)
1186 {
1187
1188 if (!b)
1189 {
1190 java.lang.String fMsg = XSLMessages.createXPATHMessage(
1191 XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION,
1192 new Object[]{ msg });
1193
1194 throw new RuntimeException(fMsg);
1195 }
1196 }
1197
1198 /**
1199 * Tell the user of an error, and probably throw an
1200 * exception.
1201 *
1202 * @param msg An error msgkey that corresponds to one of the constants found
1203 * in {@link org.apache.xpath.res.XPATHErrorResources}, which is
1204 * a key for a format string.
1205 * @param args An array of arguments represented in the format string, which
1206 * may be null.
1207 *
1208 * @throws TransformerException if the current ErrorListoner determines to
1209 * throw an exception.
1210 */
1211 public void error(String msg, Object[] args) throws TransformerException
1212 {
1213
1214 java.lang.String fmsg = XSLMessages.createXPATHMessage(msg, args);
1215
1216
1217 if (null != m_errorHandler)
1218 {
1219 m_errorHandler.fatalError(new TransformerException(fmsg, m_locator));
1220 }
1221 else
1222 {
1223
1224 // System.out.println(te.getMessage()
1225 // +"; file "+te.getSystemId()
1226 // +"; line "+te.getLineNumber()
1227 // +"; column "+te.getColumnNumber());
1228 throw new TransformerException(fmsg, (SAXSourceLocator)m_locator);
1229 }
1230 }
1231
1232 /**
1233 * The current prefixResolver for the execution context.
1234 */
1235 private PrefixResolver m_currentPrefixResolver = null;
1236
1237 /**
1238 * Get the current namespace context for the xpath.
1239 *
1240 * @return The current prefix resolver, *may* be null, though hopefully not.
1241 */
1242 public PrefixResolver getNamespaceContext()
1243 {
1244 return m_currentPrefixResolver;
1245 }
1246
1247 /**
1248 * Set the current namespace context for the xpath.
1249 *
1250 * @param pr The resolver for prefixes in the XPath expression.
1251 */
1252 public void setNamespaceContext(PrefixResolver pr)
1253 {
1254 m_currentPrefixResolver = pr;
1255 }
1256
1257 /** The error listener where errors will be sent. If this is null, errors
1258 * and warnings will be sent to System.err. May be null. */
1259 ErrorListener m_errorHandler;
1260
1261 /** The source locator for the expression being compiled. May be null. */
1262 SourceLocator m_locator;
1263
1264 /**
1265 * The FunctionTable for all xpath build-in functions
1266 */
1267 private FunctionTable m_functionTable;
1268 }