KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > trans > Mode


1 package net.sf.saxon.trans;
2
3 import net.sf.saxon.Configuration;
4 import net.sf.saxon.expr.XPathContext;
5 import net.sf.saxon.expr.XPathContextMajor;
6 import net.sf.saxon.om.Navigator;
7 import net.sf.saxon.om.NodeInfo;
8 import net.sf.saxon.pattern.NoNodeTest;
9 import net.sf.saxon.pattern.Pattern;
10 import net.sf.saxon.type.Type;
11
12 import java.io.Serializable JavaDoc;
13
14 /**
15  * A Mode is a collection of rules; the selection of a rule to apply to a given element
16  * is determined by a Pattern.
17  *
18  * @author Michael H. Kay
19  */

20
21 public class Mode implements Serializable JavaDoc {
22
23     public static final int DEFAULT_MODE = -1;
24     public static final int ALL_MODES = -2;
25     public static final int NAMED_MODE = -3;
26     public static final int STRIPPER_MODE = -4;
27
28
29     private Rule[] ruleDict = new Rule[101 + Type.MAX_NODE_TYPE];
30     private int sequence = 0; // sequence number for the rules in this Mode
31
private boolean isDefault;
32     private boolean isStripper;
33
34     /**
35      * Default constructor - creates a Mode containing no rules
36      * @param usage one of {@link #DEFAULT_MODE}, {@link #NAMED_MODE}, {@link #STRIPPER_MODE}
37      */

38
39     public Mode(int usage) {
40         this.isDefault = (usage == DEFAULT_MODE);
41         this.isStripper = (usage == STRIPPER_MODE);
42     }
43
44
45
46     /**
47      * Construct a new Mode, copying the contents of an existing Mode
48      *
49      * @param omniMode the existing mode. May be null, in which case it is not copied
50      */

51
52     public Mode(Mode omniMode) {
53         isDefault = false;
54         isStripper = false;
55         if (omniMode != null) {
56             for (int i = 0; i < this.ruleDict.length; i++) {
57                 if (omniMode.ruleDict[i] != null) {
58                     this.ruleDict[i] = new Rule(omniMode.ruleDict[i]);
59                 }
60             }
61             this.sequence = omniMode.sequence;
62         }
63     }
64
65     /**
66      * Determine if this is the default mode
67      */

68
69     public boolean isDefaultMode() {
70         return isDefault;
71     }
72
73     /**
74      * Add a rule to the Mode. <br>
75      * The rule effectively replaces any other rule for the same pattern/mode at the same or a lower
76      * priority.
77      *
78      * @param p a Pattern
79      * @param obj the Object to return from getRule() when the supplied node matches this Pattern
80      * @param precedence the import precedence of the rule
81      * @param priority the explicit or implicit priority of the rule
82      */

83
84     public void addRule(Pattern p, Object JavaDoc obj, int precedence, double priority) {
85
86         // System.err.println("Add rule, pattern = " + p.toString() + " class " + p.getClass() + ", priority=" + priority);
87

88         // Ignore a pattern that will never match, e.g. "@comment"
89

90         if (p.getNodeTest() instanceof NoNodeTest) {
91             return;
92         }
93
94         // for fast lookup, we maintain one list for each element name for patterns that can only
95
// match elements of a given name, one list for each node type for patterns that can only
96
// match one kind of non-element node, and one generic list.
97
// Each list is sorted in precedence/priority order so we find the highest-priority rule first
98

99         int fingerprint = p.getFingerprint();
100         int type = p.getNodeKind();
101
102         int key = getList(fingerprint, type);
103         // System.err.println("Fingerprint " + fingerprint + " key " + key + " type " + type);
104

105         Rule newRule = new Rule(p, obj, precedence, priority, sequence++);
106
107         Rule rule = ruleDict[key];
108         if (rule == null) {
109             ruleDict[key] = newRule;
110             return;
111         }
112
113         // insert the new rule into this list before others of the same precedence/priority
114

115         Rule prev = null;
116         while (rule != null) {
117             if ((rule.precedence < precedence) ||
118                     (rule.precedence == precedence && rule.priority <= priority)) {
119                 newRule.next = rule;
120                 if (prev == null) {
121                     ruleDict[key] = newRule;
122                 } else {
123                     prev.next = newRule;
124                 }
125                 break;
126             } else {
127                 prev = rule;
128                 rule = rule.next;
129             }
130         }
131         if (rule == null) {
132             prev.next = newRule;
133             newRule.next = null;
134         }
135     }
136
137     /**
138      * Determine which list to use for a given pattern (we must also search the generic list)
139      */

140
141     public int getList(int fingerprint, int type) {
142
143         if (type == Type.ELEMENT) {
144             if (fingerprint == -1) {
145                 return Type.NODE; // the generic list
146
} else {
147                 return Type.MAX_NODE_TYPE +
148                         (fingerprint % 101);
149             }
150         } else {
151             return type;
152         }
153     }
154
155     /**
156      * Get the rule corresponding to a given Node, by finding the best Pattern match.
157      *
158      * @param node the NodeInfo referring to the node to be matched
159      * @return the object (e.g. a NodeHandler) registered for that element, if any (otherwise null).
160      */

161
162     public Object JavaDoc getRule(NodeInfo node, XPathContext context) throws XPathException {
163         // System.err.println("Get rule for " + Navigator.getPath(node));
164
int fingerprint = node.getFingerprint();
165         // TODO: the above is inefficient with wrapped object models (DOM, XOM, JDOM)
166
int type = node.getNodeKind();
167         int key = getList(fingerprint, type);
168         int policy = context.getController().getRecoveryPolicy();
169
170         // If there are match patterns in the stylesheet that use local variables, we need to allocate
171
// a new stack frame for evaluating the match patterns. We base this on the match pattern with
172
// the highest number of range variables, so we can reuse the same stack frame for all rules
173
// that we test against. If no patterns use range variables, we don't bother allocating a new
174
// stack frame.
175

176         context = perhapsMakeNewContext(context);
177
178         Rule specificRule = null;
179         Rule generalRule = null;
180         int specificPrecedence = -1;
181         double specificPriority = Double.NEGATIVE_INFINITY;
182
183         // search the specific list for this node type / node name
184

185         // System.err.println("Hash key = " + key);
186

187         if (key != Type.NODE) {
188             Rule r = ruleDict[key];
189             while (r != null) {
190                 // if we already have a match, and the precedence or priority of this
191
// rule is lower, quit the search for a second match
192
if (specificRule != null) {
193                     if (r.precedence < specificPrecedence ||
194                             (r.precedence == specificPrecedence && r.priority < specificPriority)) {
195                         break;
196                     }
197                 }
198                 //System.err.println("Testing " + Navigator.getPath(node) + " against " + r.pattern);
199
if (r.pattern.matches(node, context)) {
200                     //System.err.println("Matches");
201

202                     // is this a second match?
203
if (specificRule != null) {
204                         if (r.precedence == specificPrecedence && r.priority == specificPriority) {
205                             reportAmbiguity(node, specificRule, r, context);
206                         }
207                         break;
208                     }
209                     specificRule = r;
210                     specificPrecedence = r.precedence;
211                     specificPriority = r.priority;
212                     if (policy == Configuration.RECOVER_SILENTLY) {
213                         break; // find the first; they are in priority order
214
}
215                 }
216                 r = r.next;
217             }
218         }
219
220         // search the general list
221

222         Rule r2 = ruleDict[Type.NODE];
223         while (r2 != null) {
224             if (r2.precedence < specificPrecedence ||
225                     (r2.precedence == specificPrecedence && r2.priority < specificPriority)) {
226                 break; // no point in looking at a lower priority rule than the one we've got
227
}
228             if (r2.pattern.matches(node, context)) {
229                 // is it a second match?
230
if (generalRule != null) {
231                     if (r2.precedence == generalRule.precedence && r2.priority == generalRule.priority) {
232                         reportAmbiguity(node, r2, generalRule, context);
233                     }
234                     break;
235                 } else {
236                     generalRule = r2;
237                     if (policy == Configuration.RECOVER_SILENTLY) {
238                         break; // find only the first; they are in priority order
239
}
240                 }
241             }
242             r2 = r2.next;
243         }
244
245         if (specificRule != null && generalRule == null) {
246             return specificRule.object;
247         }
248         if (specificRule == null && generalRule != null) {
249             return generalRule.object;
250         }
251         if (specificRule != null && generalRule != null) {
252             if (specificRule.precedence == generalRule.precedence &&
253                     specificRule.priority == generalRule.priority) {
254                 // This situation is exceptional: we have a "specific" pattern and
255
// a "general" pattern with the same priority. We have to select
256
// the one that was added last
257
// (Bug reported by Norman Walsh Jan 2002)
258
Object JavaDoc result = (specificRule.sequence > generalRule.sequence ?
259                         specificRule.object :
260                         generalRule.object);
261
262                 if (policy != Configuration.RECOVER_SILENTLY) {
263                     reportAmbiguity(node, specificRule, generalRule, context);
264                 }
265                 return result;
266             }
267             if (specificRule.precedence > generalRule.precedence ||
268                     (specificRule.precedence == generalRule.precedence &&
269                     specificRule.priority >= generalRule.priority)) {
270                 return specificRule.object;
271             } else {
272                 return generalRule.object;
273             }
274         }
275         return null;
276     }
277
278     /**
279      * Make a new XPath context for evaluating patterns if there is any possibility that the
280      * pattern uses local variables
281      *
282      * @param context The existing XPath context
283      * @return a new XPath context (or the existing context if no new context was created)
284      */

285
286     private XPathContext perhapsMakeNewContext(XPathContext context) {
287         int patternLocals = context.getController().getExecutable().getLargestPatternStackFrame();
288         if (patternLocals > 0) {
289             context = context.newContext();
290             context.setOrigin(context.getController());
291             ((XPathContextMajor)context).openStackFrame(patternLocals);
292         }
293         return context;
294     }
295
296     /**
297      * Get the rule corresponding to a given Node, by finding the best Pattern match, subject to a minimum
298      * and maximum precedence. (This supports xsl:apply-imports)
299      *
300      * @param node the NodeInfo referring to the node to be matched
301      * @return the object (e.g. a NodeHandler) registered for that element, if any (otherwise null).
302      */

303
304     public Object JavaDoc getRule(NodeInfo node, int min, int max, XPathContext context) throws XPathException {
305         int fing = node.getFingerprint();
306         int type = node.getNodeKind();
307         int key = getList(fing, type);
308
309         Rule specificRule = null;
310         Rule generalRule = null;
311
312         context = perhapsMakeNewContext(context);
313
314         // search the the specific list for this node type / name
315

316         if (key != Type.NODE) {
317             Rule r = ruleDict[key];
318             while (r != null) {
319                 if (r.precedence >= min && r.precedence <= max &&
320                         r.pattern.matches(node, context)) {
321                     specificRule = r;
322                     break; // find the first; they are in priority order
323
}
324                 r = r.next;
325             }
326         }
327
328         // search the generic list
329

330         Rule r2 = ruleDict[Type.NODE];
331         while (r2 != null) {
332             if (r2.precedence >= min && r2.precedence <= max && r2.pattern.matches(node, context)) {
333                 generalRule = r2;
334                 break; // find only the first; they are in priority order
335
}
336             r2 = r2.next;
337         }
338         if (specificRule != null && generalRule == null) {
339             return specificRule.object;
340         }
341         if (specificRule == null && generalRule != null) {
342             return generalRule.object;
343         }
344         if (specificRule != null && generalRule != null) {
345             if (specificRule.precedence > generalRule.precedence ||
346                     (specificRule.precedence == generalRule.precedence &&
347                     specificRule.priority >= generalRule.priority)) {
348                 return specificRule.object;
349             } else {
350                 return generalRule.object;
351             }
352         }
353         return null;
354     }
355
356     /**
357      * Get the rule corresponding to a given Node, by finding the next-best Pattern match
358      * after the specified object.
359      *
360      * @param node the NodeInfo referring to the node to be matched
361      * @return the object (e.g. a NodeHandler) registered for that element, if any (otherwise null).
362      */

363
364     public Object JavaDoc getNextMatchRule(NodeInfo node, Object JavaDoc currentHandler, XPathContext context) throws XPathException {
365         int fingerprint = node.getFingerprint();
366         int type = node.getNodeKind();
367         int key = getList(fingerprint, type);
368         int policy = context.getController().getRecoveryPolicy();
369
370         int currentPrecedence = -1;
371         double currentPriority = -1.0;
372         int currentSequence = -1;
373
374         context = perhapsMakeNewContext(context);
375
376         // First find the Rule object corresponding to the current handler
377

378         Rule r = ruleDict[key];
379         while (r != null) {
380             if (r.object == currentHandler) {
381                 currentPrecedence = r.precedence;
382                 currentPriority = r.priority;
383                 currentSequence = r.sequence;
384                 break;
385             } else {
386                 r = r.next;
387             }
388         }
389         if (r == null) {
390             r = ruleDict[Type.NODE];
391             while (r != null) {
392                 if (r.object == currentHandler) {
393                     currentPrecedence = r.precedence;
394                     currentPriority = r.priority;
395                     currentSequence = r.sequence;
396                     break;
397                 } else {
398                     r = r.next;
399                 }
400             }
401             if (r == null) {
402                 DynamicError de = new DynamicError("Internal error: current template doesn't match current node");
403                 de.setXPathContext(context);
404                 de.setErrorCode("SAXON:0000");
405                 throw de;
406             }
407         }
408
409         Rule specificRule = null;
410         Rule generalRule = null;
411         int specificPrecedence = -1;
412         double specificPriority = Double.NEGATIVE_INFINITY;
413
414         // search the specific list for this node type / node name
415

416         // System.err.println("Hash key = " + key);
417

418         if (key != Type.NODE) {
419             r = ruleDict[key];
420             while (r != null) {
421                 // skip this rule if its template is the current template. (There can be more than
422
// one rule for the same template in the case of a union pattern.)
423
if (r.object == currentHandler) {
424                     // skip this rule
425
} else
426                 // skip this rule unless it's "below" the current rule in search order
427
if ((r.precedence > currentPrecedence) ||
428                             (r.precedence == currentPrecedence &&
429                             (r.priority > currentPriority ||
430                             (r.priority == currentPriority && r.sequence >= currentSequence)))) {
431                         // skip this rule
432
} else {
433                         // quit the search on finding the second (recoverable error) match
434
if (specificRule != null) {
435                             if (r.precedence < specificPrecedence ||
436                                     (r.precedence == specificPrecedence && r.priority < specificPriority)) {
437                                 break;
438                             }
439                         }
440                         //System.err.println("Testing " + Navigator.getPath(node) + " against " + r.pattern);
441
if (r.pattern.matches(node, context)) {
442                             //System.err.println("Matches");
443

444                             // is this a second match?
445
if (specificRule != null) {
446                                 if (r.precedence == specificPrecedence && r.priority == specificPriority) {
447                                     reportAmbiguity(node, specificRule, r, context);
448                                 }
449                                 break;
450                             }
451                             specificRule = r;
452                             specificPrecedence = r.precedence;
453                             specificPriority = r.priority;
454                             if (policy == Configuration.RECOVER_SILENTLY) {
455                                 break; // find the first; they are in priority order
456
}
457                         }
458                     }
459                 r = r.next;
460             }
461         }
462
463         // search the general list
464

465         Rule r2 = ruleDict[Type.NODE];
466         while (r2 != null) {
467             // skip this rule if the template is the current template
468
if (r2.object == currentHandler) {
469                 // skip this rule
470
} else
471             // skip this rule unless it's "after" the current rule in search order
472
if ((r2.precedence > currentPrecedence) ||
473                         (r2.precedence == currentPrecedence &&
474                         (r2.priority > currentPriority ||
475                         (r2.priority == currentPriority && r2.sequence >= currentSequence)))) {
476                     // skip this rule
477
} else {
478                     if (r2.precedence < specificPrecedence ||
479                             (r2.precedence == specificPrecedence && r2.priority < specificPriority)) {
480                         break; // no point in looking at a lower priority rule than the one we've got
481
}
482                     if (r2.pattern.matches(node, context)) {
483                         // is it a second match?
484
if (generalRule != null) {
485                             if (r2.precedence == generalRule.precedence && r2.priority == generalRule.priority) {
486                                 reportAmbiguity(node, r2, generalRule, context);
487                             }
488                             break;
489                         } else {
490                             generalRule = r2;
491                             if (policy == Configuration.RECOVER_SILENTLY) {
492                                 break; // find only the first; they are in priority order
493
}
494                         }
495                     }
496                 }
497             r2 = r2.next;
498         }
499
500         if (specificRule != null && generalRule == null) {
501             return specificRule.object;
502         }
503         if (specificRule == null && generalRule != null) {
504             return generalRule.object;
505         }
506         if (specificRule != null && generalRule != null) {
507             if (specificRule.precedence == generalRule.precedence &&
508                     specificRule.priority == generalRule.priority) {
509                 // This situation is exceptional: we have a "specific" pattern and
510
// a "general" pattern with the same priority. We have to select
511
// the one that was added last
512
// (Bug reported by Norman Walsh Jan 2002)
513
Object JavaDoc result = (specificRule.sequence > generalRule.sequence ?
514                         specificRule.object :
515                         generalRule.object);
516
517                 if (policy != Configuration.RECOVER_SILENTLY) {
518                     reportAmbiguity(node, specificRule, generalRule, context);
519                 }
520                 return result;
521             }
522             if (specificRule.precedence > generalRule.precedence ||
523                     (specificRule.precedence == generalRule.precedence &&
524                     specificRule.priority >= generalRule.priority)) {
525                 return specificRule.object;
526             } else {
527                 return generalRule.object;
528             }
529         }
530         return null;
531     }
532
533     /**
534      * Report an ambiguity, that is, the situation where two rules of the same
535      * precedence and priority match the same node
536      *
537      * @param node The node that matches two or more rules
538      * @param r1 The first rule that the node matches
539      * @param r2 The second rule that the node matches
540      * @param c The controller for the transformation
541      */

542
543     private void reportAmbiguity(NodeInfo node, Rule r1, Rule r2, XPathContext c)
544             throws XPathException {
545         // don't report an error if the conflict is between two branches of the same
546
// Union pattern
547
if (r1.object == r2.object) {
548             return;
549         }
550         String JavaDoc path;
551         String JavaDoc errorCode = "XTRE0540";
552
553         if (isStripper) {
554             // don't report an error if the conflict is between strip-space and strip-space, or
555
// preserve-space and preserve-space
556
if (r1.object.equals(r2.object)) {
557                 return;
558             }
559             errorCode = "XTRE0270";
560             path = "xsl:strip-space";
561         } else {
562             path = Navigator.getPath(node);
563         }
564
565         Pattern pat1 = r1.pattern;
566         Pattern pat2 = r2.pattern;
567
568         DynamicError err = new DynamicError("Ambiguous rule match for " + path + '\n' +
569                 "Matches both \"" + pat1 + "\" on line " + pat1.getLineNumber() + " of " + pat1.getSystemId() +
570                 "\nand \"" + pat2 + "\" on line " + pat2.getLineNumber() + " of " + pat2.getSystemId());
571         err.setErrorCode(errorCode);
572         err.setLocator(c.getOrigin().getInstructionInfo());
573         c.getController().recoverableError(err);
574     }
575
576
577     /**
578      * Inner class Rule used to support the implementation
579      */

580
581     public static class Rule implements Serializable JavaDoc {
582         public Pattern pattern;
583         public Object JavaDoc object;
584         public int precedence;
585         public double priority;
586         public int sequence;
587         public Rule next;
588
589         /**
590          * Create a Rule
591          *
592          * @param p the pattern that this rule matches
593          * @param o the object invoked by this rule (usually a Template)
594          * @param prec the precedence of the rule
595          * @param prio the priority of the rule
596          * @param seq a sequence number for ordering of rules
597          */

598
599         public Rule(Pattern p, Object JavaDoc o, int prec, double prio, int seq) {
600             pattern = p;
601             object = o;
602             precedence = prec;
603             priority = prio;
604             next = null;
605             sequence = seq;
606         }
607
608         /**
609          * Copy a rule, including the chain of rules linked to it
610          *
611          * @param r
612          */

613
614         public Rule(Rule r) {
615             this.pattern = r.pattern;
616             this.object = r.object;
617             this.precedence = r.precedence;
618             this.priority = r.priority;
619             this.sequence = r.sequence;
620             if (r.next == null) {
621                 this.next = null;
622             } else {
623                 this.next = new Rule(r.next);
624             }
625         }
626
627     }
628
629 }
630
631 //
632
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
633
// you may not use this file except in compliance with the License. You may obtain a copy of the
634
// License at http://www.mozilla.org/MPL/
635
//
636
// Software distributed under the License is distributed on an "AS IS" basis,
637
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
638
// See the License for the specific language governing rights and limitations under the License.
639
//
640
// The Original Code is: all this file.
641
//
642
// The Initial Developer of the Original Code is Michael H. Kay.
643
//
644
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
645
//
646
// Contributor(s): none.
647
//
648
Popular Tags