KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > icl > saxon > Mode


1 package com.icl.saxon;
2 import com.icl.saxon.pattern.Pattern;
3 import com.icl.saxon.pattern.NoNodeTest;
4 import com.icl.saxon.om.NodeInfo;
5 import com.icl.saxon.om.Navigator;
6 import java.util.*;
7 import javax.xml.transform.TransformerException JavaDoc;
8 import com.icl.saxon.expr.XPathException;
9
10     /**
11     * A Mode is a collection of rules; the selection of a rule to apply to a given element
12     * is determined by a Pattern.
13     *
14     * @author <A HREF="mhkay@iclway.co.uk>Michael H. Kay</A>
15     */

16   
17 public class Mode {
18     private Rule[] ruleDict = new Rule[101 + NodeInfo.NUMBER_OF_TYPES];
19     private int nameCode = -1; // identifies the name of this mode
20
private int sequence = 0; // records sequence in which rules were added
21

22
23     public Mode() {
24         //for (int i=0; i<ruleDict.length; i++) {
25
// ruleDict[i] = null;
26
//}
27
}
28
29     /**
30     * Set the name of this mode (for tracing output)
31     */

32
33     public void setNameCode(int nameCode) {
34         this.nameCode = nameCode;
35     }
36
37     /**
38     * Get the name of this mode (for tracing output)
39     */

40     
41     public int getNameCode() {
42         return nameCode;
43     }
44     
45
46     /**
47     * Add a rule to the Mode. <br>
48     * The rule effectively replaces any other rule for the same pattern/mode at the same or a lower
49     * priority.
50     * @param p a Pattern
51     * @param obj the Object to return from getRule() when the supplied element matches this Pattern
52     */

53
54     public void addRule(Pattern p, Object JavaDoc obj, int precedence, double priority) {
55
56         // System.err.println("Add rule, pattern = " + p.toString() + " class " + p.getClass() + ", priority=" + priority);
57

58         // Ignore a pattern that will never match, e.g. "@comment"
59

60         if (p instanceof NoNodeTest) {
61             return;
62         }
63
64         // for fast lookup, we maintain one list for each element name for patterns that can only
65
// match elements of a given name, one list for each node type for patterns that can only
66
// match one kind of non-element node, and one generic list.
67
// Each list is sorted in precedence/priority order so we find the highest-priority rule first
68

69         int fingerprint = p.getFingerprint();
70         short type = p.getNodeType();
71         
72         int key = getList(fingerprint, type);
73         // System.err.println("Fingerprint " + fingerprint + " key " + key + " type " + type);
74

75         Rule newRule = new Rule(p, obj, precedence, priority, sequence++);
76
77         Rule rule = ruleDict[key];
78         if (rule==null) {
79             ruleDict[key] = newRule;
80             return;
81         }
82
83         // insert the new rule into this list before others of the same precedence/priority
84

85         Rule prev = null;
86         while (rule != null) {
87             if ((rule.precedence < precedence) ||
88                     ( rule.precedence == precedence && rule.priority <= priority)) {
89                 newRule.next = rule;
90                 if (prev==null) {
91                     ruleDict[key] = newRule;
92                 } else {
93                     prev.next = newRule;
94                 }
95                 break;
96             } else {
97                 prev = rule;
98                 rule = rule.next;
99             }
100         }
101         if (rule==null) {
102             prev.next = newRule;
103             newRule.next = null;
104         }
105     }
106
107     /**
108     * Determine which list to use for a given pattern (we must also search the generic list)
109     */

110
111     public int getList(int fingerprint, int type) {
112     
113         if (type==NodeInfo.ELEMENT) {
114             if (fingerprint==-1) {
115                 return NodeInfo.NODE; // the generic list
116
} else {
117                 return NodeInfo.NUMBER_OF_TYPES +
118                     (fingerprint % 101);
119             }
120         } else {
121             return type;
122         }
123     }
124
125     /**
126     * Get the rule corresponding to a given Node, by finding the best Pattern match.
127     * @param node the NodeInfo referring to the node to be matched
128     * @return the object (e.g. a NodeHandler) registered for that element, if any (otherwise null).
129     */

130
131     public Object JavaDoc getRule(NodeInfo node, Context context) throws TransformerException JavaDoc {
132         // System.err.println("Get rule for " + Navigator.getPath(node));
133
int fingerprint = node.getFingerprint();
134         int type = node.getNodeType();
135         int key = getList(fingerprint, type);
136         int policy = context.getController().getRecoveryPolicy();
137
138         Rule specificRule = null;
139         Rule generalRule = null;
140         int specificPrecedence = -1;
141         double specificPriority = Double.NEGATIVE_INFINITY;
142
143         // search the specific list for this node type / node name
144

145         // System.err.println("Hash key = " + key);
146

147         if (key!=NodeInfo.NODE) {
148             Rule r = ruleDict[key];
149             while(r!=null) {
150                 // if we already have a match, and the precedence or priority of this
151
// rule is lower, quit the search for a second match
152
if (specificRule != null) {
153                     if (r.precedence < specificPrecedence ||
154                          (r.precedence==specificPrecedence && r.priority < specificPriority)) {
155                         break;
156                     }
157                 }
158                 //System.err.println("Testing " + Navigator.getPath(node) + " against " + r.pattern);
159
if (r.pattern.matches(node, context)) {
160                     //System.err.println("Matches");
161

162                     // is this a second match?
163
if (specificRule != null) {
164                         if (r.precedence==specificPrecedence && r.priority==specificPriority) {
165                             reportAmbiguity(node, specificRule.pattern, r.pattern, context);
166                         }
167                         break;
168                     }
169                     specificRule = r;
170                     specificPrecedence = r.precedence;
171                     specificPriority = r.priority;
172                     if (policy==Controller.RECOVER_SILENTLY) {
173                         break; // find the first; they are in priority order
174
}
175                 }
176                 r = r.next;
177             }
178         }
179
180         // search the general list
181

182         Rule r2 = ruleDict[NodeInfo.NODE];
183         while (r2 != null) {
184             if (r2.precedence < specificPrecedence ||
185                  (r2.precedence == specificPrecedence && r2.priority < specificPriority)) {
186                 break; // no point in looking at a lower priority rule than the one we've got
187
}
188             if (r2.pattern.matches(node, context)) {
189                 // is it a second match?
190
if (generalRule != null) {
191                     if (r2.precedence == generalRule.precedence && r2.priority ==generalRule.priority) {
192                         reportAmbiguity(node, r2.pattern, generalRule.pattern, context);
193                     }
194                     break;
195                 } else {
196                     generalRule = r2;
197                     if (policy==Controller.RECOVER_SILENTLY) {
198                         break; // find only the first; they are in priority order
199
}
200                 }
201             }
202             r2 = r2.next;
203         }
204
205         if (specificRule!=null && generalRule==null)
206             return specificRule.object;
207         if (specificRule==null && generalRule!=null)
208             return generalRule.object;
209         if (specificRule!=null && generalRule!=null) {
210             if (specificRule.precedence == generalRule.precedence &&
211                 specificRule.priority == generalRule.priority ) {
212                 // This situation is exceptional: we have a "specific" pattern and
213
// a "general" pattern with the same priority. We have to select
214
// the one that was added last
215
// (Bug reported by Norman Walsh Jan 2002)
216
Object JavaDoc result = (specificRule.sequence > generalRule.sequence ?
217                                     specificRule.object :
218                                     generalRule.object);
219                                     
220                 if (policy!=Controller.RECOVER_SILENTLY) {
221                     reportAmbiguity(node, specificRule.pattern, generalRule.pattern, context);
222                 }
223                 return result;
224             }
225             if (specificRule.precedence > generalRule.precedence ||
226                  (specificRule.precedence == generalRule.precedence &&
227                     specificRule.priority >= generalRule.priority)) {
228                 return specificRule.object;
229             } else {
230                 return generalRule.object;
231             }
232         }
233         return null;
234     }
235
236     /**
237     * Get the rule corresponding to a given Node, by finding the best Pattern match, subject to a minimum
238     * and maximum precedence. (This supports xsl:apply-imports)
239     * @param node the NodeInfo referring to the node to be matched
240     * @return the object (e.g. a NodeHandler) registered for that element, if any (otherwise null).
241     */

242
243     public Object JavaDoc getRule(NodeInfo node, int min, int max, Context context) throws XPathException {
244         int fing = node.getFingerprint();
245         int type = node.getNodeType();
246         int key = getList(fing, type);
247
248         Rule specificRule = null;
249         Rule generalRule = null;
250
251         // search the the specific list for this node type / name
252

253         if (key!=NodeInfo.NODE) {
254             Rule r = ruleDict[key];
255             while (r!=null) {
256                 if (r.precedence >= min && r.precedence <= max &&
257                          r.pattern.matches(node, context)) {
258                     specificRule = r;
259                     break; // find the first; they are in priority order
260
}
261                 r = r.next;
262             }
263         }
264
265         // search the generic list
266

267         Rule r2 = ruleDict[NodeInfo.NODE];
268         while (r2!=null) {
269             if (r2.precedence >= min && r2.precedence <= max && r2.pattern.matches(node, context)) {
270                 generalRule = r2;
271                 break; // find only the first; they are in priority order
272
}
273             r2 = r2.next;
274         }
275         if (specificRule!=null && generalRule==null)
276             return specificRule.object;
277         if (specificRule==null && generalRule!=null)
278             return generalRule.object;
279         if (specificRule!=null && generalRule!=null) {
280             if (specificRule.precedence > generalRule.precedence ||
281                  (specificRule.precedence == generalRule.precedence &&
282                     specificRule.priority >= generalRule.priority)) {
283                 return specificRule.object;
284             } else {
285                 return generalRule.object;
286             }
287         }
288         return null;
289     }
290
291     /**
292     * Report an ambiguity
293     */

294
295     private void reportAmbiguity(NodeInfo node, Pattern pat1, Pattern pat2, Context c)
296         throws TransformerException JavaDoc
297     {
298         // don't report an error if the conflict is between two branches of the same
299
// Union pattern
300
if (pat1.getStaticContext()==pat2.getStaticContext()) {
301             return;
302         }
303         c.getController().reportRecoverableError(
304             "Ambiguous rule match for " + Navigator.getPath(node) + "\n" +
305             "Matches both \"" + pat1 + "\" on line " + pat1.getLineNumber() + " of " + pat1.getSystemId() +
306             "\nand \"" + pat2 + "\" on line " + pat2.getLineNumber() + " of " + pat2.getSystemId(), null);
307         
308     }
309
310
311     /**
312     * Inner class Rule used to support the implementation
313     */

314
315     private static class Rule {
316         public Pattern pattern;
317         public Object JavaDoc object;
318         public int precedence;
319         public double priority;
320         public int sequence;
321         public Rule next;
322     
323         public Rule( Pattern p, Object JavaDoc o, int prec, double prio, int seq ) {
324             pattern = p;
325             object = o;
326             precedence = prec;
327             priority = prio;
328             sequence = seq;
329             next = null;
330         }
331         
332     }
333
334 }
335
336 //
337
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
338
// you may not use this file except in compliance with the License. You may obtain a copy of the
339
// License at http://www.mozilla.org/MPL/
340
//
341
// Software distributed under the License is distributed on an "AS IS" basis,
342
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
343
// See the License for the specific language governing rights and limitations under the License.
344
//
345
// The Original Code is: all this file.
346
//
347
// The Initial Developer of the Original Code is
348
// Michael Kay of International Computers Limited (mhkay@iclway.co.uk).
349
//
350
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
351
//
352
// Contributor(s): none.
353
//
354
Popular Tags