KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > xalan > xsltc > compiler > Whitespace


1 /*
2  * Copyright 2001-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 /*
17  * $Id: Whitespace.java,v 1.10 2004/02/16 22:25:10 minchau Exp $
18  */

19
20 package org.apache.xalan.xsltc.compiler;
21
22 import java.util.StringTokenizer JavaDoc;
23 import java.util.Vector JavaDoc;
24
25 import org.apache.bcel.generic.ALOAD;
26 import org.apache.bcel.generic.BranchHandle;
27 import org.apache.bcel.generic.ConstantPoolGen;
28 import org.apache.bcel.generic.IF_ICMPEQ;
29 import org.apache.bcel.generic.ILOAD;
30 import org.apache.bcel.generic.INVOKEINTERFACE;
31 import org.apache.bcel.generic.INVOKEVIRTUAL;
32 import org.apache.bcel.generic.InstructionHandle;
33 import org.apache.bcel.generic.InstructionList;
34 import org.apache.bcel.generic.PUSH;
35 import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
36 import org.apache.xalan.xsltc.compiler.util.ErrorMsg;
37 import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
38 import org.apache.xalan.xsltc.compiler.util.Type;
39 import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
40 import org.apache.xalan.xsltc.compiler.util.Util;
41
42 /**
43  * @author Morten Jorgensen
44  */

45 final class Whitespace extends TopLevelElement {
46     // Three possible actions for the translet:
47
public static final int USE_PREDICATE = 0;
48     public static final int STRIP_SPACE = 1;
49     public static final int PRESERVE_SPACE = 2;
50
51     // The 3 different categories of strip/preserve rules (order important)
52
public static final int RULE_NONE = 0;
53     public static final int RULE_ELEMENT = 1; // priority 0
54
public static final int RULE_NAMESPACE = 2; // priority -1/4
55
public static final int RULE_ALL = 3; // priority -1/2
56

57     private String JavaDoc _elementList;
58     private int _action;
59     private int _importPrecedence;
60
61     /**
62      * Auxillary class for encapsulating a single strip/preserve rule
63      */

64     private final static class WhitespaceRule {
65     private final int _action;
66     private String JavaDoc _namespace; // Should be replaced by NS type (int)
67
private String JavaDoc _element; // Should be replaced by node type (int)
68
private int _type;
69     private int _priority;
70     
71     /**
72      * Strip/preserve rule constructor
73      */

74     public WhitespaceRule(int action, String JavaDoc element, int precedence) {
75         // Determine the action (strip or preserve) for this rule
76
_action = action;
77
78         // Get the namespace and element name for this rule
79
final int colon = element.indexOf(':');
80         if (colon >= 0) {
81         _namespace = element.substring(0,colon);
82         _element = element.substring(colon+1,element.length());
83         }
84         else {
85         _namespace = Constants.EMPTYSTRING;
86         _element = element;
87         }
88
89         // Determine the initial priority for this rule
90
_priority = precedence << 2;
91
92         // Get the strip/preserve type; either "NS:EL", "NS:*" or "*"
93
if (_element.equals("*")) {
94         if (_namespace == Constants.EMPTYSTRING) {
95             _type = RULE_ALL; // Strip/preserve _all_ elements
96
_priority += 2; // Lowest priority
97
}
98         else {
99             _type = RULE_NAMESPACE; // Strip/reserve elements within NS
100
_priority += 1; // Medium priority
101
}
102         }
103         else {
104         _type = RULE_ELEMENT; // Strip/preserve single element
105
}
106     }
107
108     /**
109      * For sorting rules depending on priority
110      */

111     public int compareTo(WhitespaceRule other) {
112         return _priority < other._priority
113         ? -1
114         : _priority > other._priority ? 1 : 0;
115     }
116
117     public int getAction() { return _action; }
118     public int getStrength() { return _type; }
119     public int getPriority() { return _priority; }
120     public String JavaDoc getElement() { return _element; }
121     public String JavaDoc getNamespace() { return _namespace; }
122     }
123
124     /**
125      * Parse the attributes of the xsl:strip/preserve-space element.
126      * The element should have not contents (ignored if any).
127      */

128     public void parseContents(Parser parser) {
129     // Determine if this is an xsl:strip- or preserve-space element
130
_action = _qname.getLocalPart().endsWith("strip-space")
131         ? STRIP_SPACE : PRESERVE_SPACE;
132
133     // Determine the import precedence
134
_importPrecedence = parser.getCurrentImportPrecedence();
135
136     // Get the list of elements to strip/preserve
137
_elementList = getAttribute("elements");
138     if (_elementList == null || _elementList.length() == 0) {
139         reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR, "elements");
140         return;
141     }
142
143     final SymbolTable stable = parser.getSymbolTable();
144     StringTokenizer JavaDoc list = new StringTokenizer JavaDoc(_elementList);
145     StringBuffer JavaDoc elements = new StringBuffer JavaDoc(Constants.EMPTYSTRING);
146
147     while (list.hasMoreElements()) {
148         String JavaDoc token = list.nextToken();
149         String JavaDoc prefix;
150         String JavaDoc namespace;
151         int col;
152
153         if ((col = token.indexOf(':')) != -1) {
154         prefix = token.substring(0,col);
155         }
156         else {
157         prefix = Constants.EMPTYSTRING;
158         }
159
160         namespace = lookupNamespace(prefix);
161         if (namespace != null)
162         elements.append(namespace+":"+
163                 token.substring(col+1,token.length()));
164         else
165         elements.append(token);
166
167         if (list.hasMoreElements())
168         elements.append(" ");
169     }
170     _elementList = elements.toString();
171     }
172
173
174     /**
175      * De-tokenize the elements listed in the 'elements' attribute and
176      * instanciate a set of strip/preserve rules.
177      */

178     public Vector JavaDoc getRules() {
179     final Vector JavaDoc rules = new Vector JavaDoc();
180     // Go through each element and instanciate strip/preserve-object
181
final StringTokenizer JavaDoc list = new StringTokenizer JavaDoc(_elementList);
182     while (list.hasMoreElements()) {
183         rules.add(new WhitespaceRule(_action,
184                      list.nextToken(),
185                      _importPrecedence));
186     }
187     return rules;
188     }
189     
190     
191     /**
192      * Scans through the rules vector and looks for a rule of higher
193      * priority that contradicts the current rule.
194      */

195     private static WhitespaceRule findContradictingRule(Vector JavaDoc rules,
196                             WhitespaceRule rule) {
197     for (int i = 0; i < rules.size(); i++) {
198         // Get the next rule in the prioritized list
199
WhitespaceRule currentRule = (WhitespaceRule)rules.elementAt(i);
200         // We only consider rules with higher priority
201
if (currentRule == rule) {
202         return null;
203         }
204         
205         /*
206          * See if there is a contradicting rule with higher priority.
207          * If the rules has the same action then this rule is redundant,
208          * if they have different action then this rule will never win.
209          */

210         switch (currentRule.getStrength()) {
211         case RULE_ALL:
212         return currentRule;
213         
214         case RULE_ELEMENT:
215         if (!rule.getElement().equals(currentRule.getElement())) {
216             break;
217         }
218         // intentional fall-through
219
case RULE_NAMESPACE:
220         if (rule.getNamespace().equals(currentRule.getNamespace())) {
221             return currentRule;
222         }
223         break;
224         }
225     }
226     return null;
227     }
228
229
230     /**
231      * Orders a set or rules by priority, removes redundant rules and rules
232      * that are shadowed by stronger, contradicting rules.
233      */

234     private static int prioritizeRules(Vector JavaDoc rules) {
235     WhitespaceRule currentRule;
236     int defaultAction = PRESERVE_SPACE;
237
238     // Sort all rules with regard to priority
239
quicksort(rules, 0, rules.size()-1);
240
241     // Check if there are any "xsl:strip-space" elements at all.
242
// If there are no xsl:strip elements we can ignore all xsl:preserve
243
// elements and signal that all whitespaces should be preserved
244
boolean strip = false;
245     for (int i = 0; i < rules.size(); i++) {
246         currentRule = (WhitespaceRule)rules.elementAt(i);
247         if (currentRule.getAction() == STRIP_SPACE) {
248         strip = true;
249         }
250     }
251     // Return with default action: PRESERVE_SPACE
252
if (!strip) {
253         rules.removeAllElements();
254         return PRESERVE_SPACE;
255     }
256
257     // Remove all rules that are contradicted by rules with higher priority
258
for (int idx = 0; idx < rules.size(); ) {
259         currentRule = (WhitespaceRule)rules.elementAt(idx);
260     
261         // Remove this single rule if it has no purpose
262
if (findContradictingRule(rules,currentRule) != null) {
263         rules.remove(idx);
264         }
265         else {
266         // Remove all following rules if this one overrides all
267
if (currentRule.getStrength() == RULE_ALL) {
268             defaultAction = currentRule.getAction();
269             for (int i = idx; i < rules.size(); i++) {
270             rules.removeElementAt(i);
271             }
272         }
273         // Skip to next rule (there might not be any)...
274
idx++;
275         }
276     }
277
278     // The rules vector could be empty if first rule has strength RULE_ALL
279
if (rules.size() == 0) {
280         return defaultAction;
281     }
282
283     // Now work backwards and strip away all rules that have the same
284
// action as the default rule (no reason the check them at the end).
285
do {
286         currentRule = (WhitespaceRule)rules.lastElement();
287         if (currentRule.getAction() == defaultAction) {
288         rules.removeElementAt(rules.size() - 1);
289         }
290         else {
291         break;
292         }
293     } while (rules.size() > 0);
294     
295     // Signal that whitespace detection predicate must be used.
296
return defaultAction;
297     }
298
299     public static void compileStripSpace(BranchHandle strip[],
300                      int sCount,
301                      InstructionList il) {
302     final InstructionHandle target = il.append(ICONST_1);
303     il.append(IRETURN);
304     for (int i = 0; i < sCount; i++) {
305         strip[i].setTarget(target);
306     }
307     }
308
309     public static void compilePreserveSpace(BranchHandle preserve[],
310                         int pCount,
311                         InstructionList il) {
312     final InstructionHandle target = il.append(ICONST_0);
313     il.append(IRETURN);
314     for (int i = 0; i < pCount; i++) {
315         preserve[i].setTarget(target);
316     }
317     }
318
319     /*
320     private static void compileDebug(ClassGenerator classGen,
321                      InstructionList il) {
322     final ConstantPoolGen cpg = classGen.getConstantPool();
323     final int prt = cpg.addMethodref("java/lang/System/out",
324                      "println",
325                      "(Ljava/lang/String;)V");
326     il.append(DUP);
327     il.append(new INVOKESTATIC(prt));
328     }
329     */

330
331     /**
332      * Compiles the predicate method
333      */

334     private static void compilePredicate(Vector JavaDoc rules,
335                      int defaultAction,
336                      ClassGenerator classGen) {
337     final ConstantPoolGen cpg = classGen.getConstantPool();
338     final InstructionList il = new InstructionList();
339     final XSLTC xsltc = classGen.getParser().getXSLTC();
340
341     // private boolean Translet.stripSpace(int type) - cannot be static
342
final MethodGenerator stripSpace =
343         new MethodGenerator(ACC_PUBLIC | ACC_FINAL ,
344             org.apache.bcel.generic.Type.BOOLEAN,
345             new org.apache.bcel.generic.Type[] {
346                 Util.getJCRefType(DOM_INTF_SIG),
347                 org.apache.bcel.generic.Type.INT,
348                 org.apache.bcel.generic.Type.INT
349             },
350             new String JavaDoc[] { "dom","node","type" },
351             "stripSpace",classGen.getClassName(),il,cpg);
352
353     classGen.addInterface("org/apache/xalan/xsltc/StripFilter");
354
355     final int paramDom = stripSpace.getLocalIndex("dom");
356     final int paramCurrent = stripSpace.getLocalIndex("node");
357     final int paramType = stripSpace.getLocalIndex("type");
358
359     BranchHandle strip[] = new BranchHandle[rules.size()];
360     BranchHandle preserve[] = new BranchHandle[rules.size()];
361     int sCount = 0;
362     int pCount = 0;
363
364     // Traverse all strip/preserve rules
365
for (int i = 0; i<rules.size(); i++) {
366         // Get the next rule in the prioritised list
367
WhitespaceRule rule = (WhitespaceRule)rules.elementAt(i);
368
369         // Returns the namespace for a node in the DOM
370
final int gns = cpg.addInterfaceMethodref(DOM_INTF,
371                               "getNamespaceName",
372                               "(I)Ljava/lang/String;");
373
374         final int strcmp = cpg.addMethodref("java/lang/String",
375                         "compareTo",
376                         "(Ljava/lang/String;)I");
377
378         // Handle elements="ns:*" type rule
379
if (rule.getStrength() == RULE_NAMESPACE) {
380         il.append(new ALOAD(paramDom));
381         il.append(new ILOAD(paramCurrent));
382         il.append(new INVOKEINTERFACE(gns,2));
383         il.append(new PUSH(cpg, rule.getNamespace()));
384         il.append(new INVOKEVIRTUAL(strcmp));
385         il.append(ICONST_0);
386
387         if (rule.getAction() == STRIP_SPACE) {
388             strip[sCount++] = il.append(new IF_ICMPEQ(null));
389         }
390         else {
391             preserve[pCount++] = il.append(new IF_ICMPEQ(null));
392         }
393         }
394         // Handle elements="ns:el" type rule
395
else if (rule.getStrength() == RULE_ELEMENT) {
396         // Create the QName for the element
397
final Parser parser = classGen.getParser();
398         QName qname;
399         if (rule.getNamespace() != Constants.EMPTYSTRING )
400             qname = parser.getQName(rule.getNamespace(), null,
401                         rule.getElement());
402         else
403             qname = parser.getQName(rule.getElement());
404
405         // Register the element.
406
final int elementType = xsltc.registerElement(qname);
407         il.append(new ILOAD(paramType));
408         il.append(new PUSH(cpg, elementType));
409
410         // Compare current node type with wanted element type
411
if (rule.getAction() == STRIP_SPACE)
412             strip[sCount++] = il.append(new IF_ICMPEQ(null));
413         else
414             preserve[pCount++] = il.append(new IF_ICMPEQ(null));
415         }
416     }
417
418     if (defaultAction == STRIP_SPACE) {
419         compileStripSpace(strip, sCount, il);
420         compilePreserveSpace(preserve, pCount, il);
421     }
422     else {
423         compilePreserveSpace(preserve, pCount, il);
424         compileStripSpace(strip, sCount, il);
425     }
426
427     stripSpace.stripAttributes(true);
428     stripSpace.setMaxLocals();
429     stripSpace.setMaxStack();
430     stripSpace.removeNOPs();
431
432     classGen.addMethod(stripSpace.getMethod());
433     }
434
435     /**
436      * Compiles the predicate method
437      */

438     private static void compileDefault(int defaultAction,
439                        ClassGenerator classGen) {
440     final ConstantPoolGen cpg = classGen.getConstantPool();
441     final InstructionList il = new InstructionList();
442     final XSLTC xsltc = classGen.getParser().getXSLTC();
443
444     // private boolean Translet.stripSpace(int type) - cannot be static
445
final MethodGenerator stripSpace =
446         new MethodGenerator(ACC_PUBLIC | ACC_FINAL ,
447             org.apache.bcel.generic.Type.BOOLEAN,
448             new org.apache.bcel.generic.Type[] {
449                 Util.getJCRefType(DOM_INTF_SIG),
450                 org.apache.bcel.generic.Type.INT,
451                 org.apache.bcel.generic.Type.INT
452             },
453             new String JavaDoc[] { "dom","node","type" },
454             "stripSpace",classGen.getClassName(),il,cpg);
455
456     classGen.addInterface("org/apache/xalan/xsltc/StripFilter");
457
458     if (defaultAction == STRIP_SPACE)
459         il.append(ICONST_1);
460     else
461         il.append(ICONST_0);
462     il.append(IRETURN);
463
464     stripSpace.stripAttributes(true);
465     stripSpace.setMaxLocals();
466     stripSpace.setMaxStack();
467     stripSpace.removeNOPs();
468
469     classGen.addMethod(stripSpace.getMethod());
470     }
471
472
473     /**
474      * Takes a vector of WhitespaceRule objects and generates a predicate
475      * method. This method returns the translets default action for handling
476      * whitespace text-nodes:
477      * - USE_PREDICATE (run the method generated by this method)
478      * - STRIP_SPACE (always strip whitespace text-nodes)
479      * - PRESERVE_SPACE (always preserve whitespace text-nodes)
480      */

481     public static int translateRules(Vector JavaDoc rules,
482                      ClassGenerator classGen) {
483     // Get the core rules in prioritized order
484
final int defaultAction = prioritizeRules(rules);
485     // The rules vector may be empty after prioritising
486
if (rules.size() == 0) {
487         compileDefault(defaultAction,classGen);
488         return defaultAction;
489     }
490     // Now - create a predicate method and sequence through rules...
491
compilePredicate(rules, defaultAction, classGen);
492     // Return with the translets required action (
493
return USE_PREDICATE;
494     }
495
496     /**
497      * Sorts a range of rules with regard to PRIORITY only
498      */

499     private static void quicksort(Vector JavaDoc rules, int p, int r) {
500     while (p < r) {
501         final int q = partition(rules, p, r);
502         quicksort(rules, p, q);
503         p = q + 1;
504     }
505     }
506     
507     /**
508      * Used with quicksort method above
509      */

510     private static int partition(Vector JavaDoc rules, int p, int r) {
511     final WhitespaceRule x = (WhitespaceRule)rules.elementAt((p+r) >>> 1);
512     int i = p - 1, j = r + 1;
513     while (true) {
514         while (x.compareTo((WhitespaceRule)rules.elementAt(--j)) < 0) {
515         }
516         while (x.compareTo((WhitespaceRule)rules.elementAt(++i)) > 0) {
517         }
518         if (i < j) {
519         final WhitespaceRule tmp = (WhitespaceRule)rules.elementAt(i);
520         rules.setElementAt(rules.elementAt(j), i);
521         rules.setElementAt(tmp, j);
522         }
523         else {
524         return j;
525         }
526     }
527     }
528     
529     /**
530      * Type-check contents/attributes - nothing to do...
531      */

532     public Type typeCheck(SymbolTable stable) throws TypeCheckError {
533     return Type.Void; // We don't return anything.
534
}
535
536     /**
537      * This method should not produce any code
538      */

539     public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
540     }
541 }
542
Popular Tags