1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 /* 6 * Copyright 2001-2004 The Apache Software Foundation. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 /* 21 * $Id: Whitespace.java,v 1.5 2005/09/28 13:48:18 pvedula Exp $ 22 */ 23 24 package com.sun.org.apache.xalan.internal.xsltc.compiler; 25 26 import java.util.StringTokenizer; 27 import java.util.Vector; 28 29 import com.sun.org.apache.bcel.internal.generic.ALOAD; 30 import com.sun.org.apache.bcel.internal.generic.BranchHandle; 31 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen; 32 import com.sun.org.apache.bcel.internal.generic.IF_ICMPEQ; 33 import com.sun.org.apache.bcel.internal.generic.ILOAD; 34 import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE; 35 import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL; 36 import com.sun.org.apache.bcel.internal.generic.InstructionHandle; 37 import com.sun.org.apache.bcel.internal.generic.InstructionList; 38 import com.sun.org.apache.bcel.internal.generic.PUSH; 39 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator; 40 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg; 41 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator; 42 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type; 43 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError; 44 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util; 45 46 /** 47 * @author Morten Jorgensen 48 */ 49 final class Whitespace extends TopLevelElement { 50 // Three possible actions for the translet: 51 public static final int USE_PREDICATE = 0; 52 public static final int STRIP_SPACE = 1; 53 public static final int PRESERVE_SPACE = 2; 54 55 // The 3 different categories of strip/preserve rules (order important) 56 public static final int RULE_NONE = 0; 57 public static final int RULE_ELEMENT = 1; // priority 0 58 public static final int RULE_NAMESPACE = 2; // priority -1/4 59 public static final int RULE_ALL = 3; // priority -1/2 60 61 private String _elementList; 62 private int _action; 63 private int _importPrecedence; 64 65 /** 66 * Auxillary class for encapsulating a single strip/preserve rule 67 */ 68 private final static class WhitespaceRule { 69 private final int _action; 70 private String _namespace; // Should be replaced by NS type (int) 71 private String _element; // Should be replaced by node type (int) 72 private int _type; 73 private int _priority; 74 75 /** 76 * Strip/preserve rule constructor 77 */ 78 public WhitespaceRule(int action, String element, int precedence) { 79 // Determine the action (strip or preserve) for this rule 80 _action = action; 81 82 // Get the namespace and element name for this rule 83 final int colon = element.lastIndexOf(':'); 84 if (colon >= 0) { 85 _namespace = element.substring(0,colon); 86 _element = element.substring(colon+1,element.length()); 87 } 88 else { 89 _namespace = Constants.EMPTYSTRING; 90 _element = element; 91 } 92 93 // Determine the initial priority for this rule 94 _priority = precedence << 2; 95 96 // Get the strip/preserve type; either "NS:EL", "NS:*" or "*" 97 if (_element.equals("*")) { 98 if (_namespace == Constants.EMPTYSTRING) { 99 _type = RULE_ALL; // Strip/preserve _all_ elements 100 _priority += 2; // Lowest priority 101 } 102 else { 103 _type = RULE_NAMESPACE; // Strip/reserve elements within NS 104 _priority += 1; // Medium priority 105 } 106 } 107 else { 108 _type = RULE_ELEMENT; // Strip/preserve single element 109 } 110 } 111 112 /** 113 * For sorting rules depending on priority 114 */ 115 public int compareTo(WhitespaceRule other) { 116 return _priority < other._priority 117 ? -1 118 : _priority > other._priority ? 1 : 0; 119 } 120 121 public int getAction() { return _action; } 122 public int getStrength() { return _type; } 123 public int getPriority() { return _priority; } 124 public String getElement() { return _element; } 125 public String getNamespace() { return _namespace; } 126 } 127 128 /** 129 * Parse the attributes of the xsl:strip/preserve-space element. 130 * The element should have not contents (ignored if any). 131 */ 132 public void parseContents(Parser parser) { 133 // Determine if this is an xsl:strip- or preserve-space element 134 _action = _qname.getLocalPart().endsWith("strip-space") 135 ? STRIP_SPACE : PRESERVE_SPACE; 136 137 // Determine the import precedence 138 _importPrecedence = parser.getCurrentImportPrecedence(); 139 140 // Get the list of elements to strip/preserve 141 _elementList = getAttribute("elements"); 142 if (_elementList == null || _elementList.length() == 0) { 143 reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR, "elements"); 144 return; 145 } 146 147 final SymbolTable stable = parser.getSymbolTable(); 148 StringTokenizer list = new StringTokenizer(_elementList); 149 StringBuffer elements = new StringBuffer(Constants.EMPTYSTRING); 150 151 while (list.hasMoreElements()) { 152 String token = list.nextToken(); 153 String prefix; 154 String namespace; 155 int col = token.indexOf(':'); 156 157 if (col != -1) { 158 namespace = lookupNamespace(token.substring(0,col)); 159 if (namespace != null) { 160 elements.append(namespace).append(':').append(token.substring(col + 1)); 161 } else { 162 elements.append(token); 163 } 164 } else { 165 elements.append(token); 166 } 167 168 if (list.hasMoreElements()) 169 elements.append(" "); 170 } 171 _elementList = elements.toString(); 172 } 173 174 175 /** 176 * De-tokenize the elements listed in the 'elements' attribute and 177 * instanciate a set of strip/preserve rules. 178 */ 179 public Vector getRules() { 180 final Vector rules = new Vector(); 181 // Go through each element and instanciate strip/preserve-object 182 final StringTokenizer list = new StringTokenizer(_elementList); 183 while (list.hasMoreElements()) { 184 rules.add(new WhitespaceRule(_action, 185 list.nextToken(), 186 _importPrecedence)); 187 } 188 return rules; 189 } 190 191 192 /** 193 * Scans through the rules vector and looks for a rule of higher 194 * priority that contradicts the current rule. 195 */ 196 private static WhitespaceRule findContradictingRule(Vector rules, 197 WhitespaceRule rule) { 198 for (int i = 0; i < rules.size(); i++) { 199 // Get the next rule in the prioritized list 200 WhitespaceRule currentRule = (WhitespaceRule)rules.elementAt(i); 201 // We only consider rules with higher priority 202 if (currentRule == rule) { 203 return null; 204 } 205 206 /* 207 * See if there is a contradicting rule with higher priority. 208 * If the rules has the same action then this rule is redundant, 209 * if they have different action then this rule will never win. 210 */ 211 switch (currentRule.getStrength()) { 212 case RULE_ALL: 213 return currentRule; 214 215 case RULE_ELEMENT: 216 if (!rule.getElement().equals(currentRule.getElement())) { 217 break; 218 } 219 // intentional fall-through 220 case RULE_NAMESPACE: 221 if (rule.getNamespace().equals(currentRule.getNamespace())) { 222 return currentRule; 223 } 224 break; 225 } 226 } 227 return null; 228 } 229 230 231 /** 232 * Orders a set or rules by priority, removes redundant rules and rules 233 * that are shadowed by stronger, contradicting rules. 234 */ 235 private static int prioritizeRules(Vector rules) { 236 WhitespaceRule currentRule; 237 int defaultAction = PRESERVE_SPACE; 238 239 // Sort all rules with regard to priority 240 quicksort(rules, 0, rules.size()-1); 241 242 // Check if there are any "xsl:strip-space" elements at all. 243 // If there are no xsl:strip elements we can ignore all xsl:preserve 244 // elements and signal that all whitespaces should be preserved 245 boolean strip = false; 246 for (int i = 0; i < rules.size(); i++) { 247 currentRule = (WhitespaceRule)rules.elementAt(i); 248 if (currentRule.getAction() == STRIP_SPACE) { 249 strip = true; 250 } 251 } 252 // Return with default action: PRESERVE_SPACE 253 if (!strip) { 254 rules.removeAllElements(); 255 return PRESERVE_SPACE; 256 } 257 258 // Remove all rules that are contradicted by rules with higher priority 259 for (int idx = 0; idx < rules.size(); ) { 260 currentRule = (WhitespaceRule)rules.elementAt(idx); 261 262 // Remove this single rule if it has no purpose 263 if (findContradictingRule(rules,currentRule) != null) { 264 rules.remove(idx); 265 } 266 else { 267 // Remove all following rules if this one overrides all 268 if (currentRule.getStrength() == RULE_ALL) { 269 defaultAction = currentRule.getAction(); 270 for (int i = idx; i < rules.size(); i++) { 271 rules.removeElementAt(i); 272 } 273 } 274 // Skip to next rule (there might not be any)... 275 idx++; 276 } 277 } 278 279 // The rules vector could be empty if first rule has strength RULE_ALL 280 if (rules.size() == 0) { 281 return defaultAction; 282 } 283 284 // Now work backwards and strip away all rules that have the same 285 // action as the default rule (no reason the check them at the end). 286 do { 287 currentRule = (WhitespaceRule)rules.lastElement(); 288 if (currentRule.getAction() == defaultAction) { 289 rules.removeElementAt(rules.size() - 1); 290 } 291 else { 292 break; 293 } 294 } while (rules.size() > 0); 295 296 // Signal that whitespace detection predicate must be used. 297 return defaultAction; 298 } 299 300 public static void compileStripSpace(BranchHandle strip[], 301 int sCount, 302 InstructionList il) { 303 final InstructionHandle target = il.append(ICONST_1); 304 il.append(IRETURN); 305 for (int i = 0; i < sCount; i++) { 306 strip[i].setTarget(target); 307 } 308 } 309 310 public static void compilePreserveSpace(BranchHandle preserve[], 311 int pCount, 312 InstructionList il) { 313 final InstructionHandle target = il.append(ICONST_0); 314 il.append(IRETURN); 315 for (int i = 0; i < pCount; i++) { 316 preserve[i].setTarget(target); 317 } 318 } 319 320 /* 321 private static void compileDebug(ClassGenerator classGen, 322 InstructionList il) { 323 final ConstantPoolGen cpg = classGen.getConstantPool(); 324 final int prt = cpg.addMethodref("java/lang/System/out", 325 "println", 326 "(Ljava/lang/String;)V"); 327 il.append(DUP); 328 il.append(new INVOKESTATIC(prt)); 329 } 330 */ 331 332 /** 333 * Compiles the predicate method 334 */ 335 private static void compilePredicate(Vector rules, 336 int defaultAction, 337 ClassGenerator classGen) { 338 final ConstantPoolGen cpg = classGen.getConstantPool(); 339 final InstructionList il = new InstructionList(); 340 final XSLTC xsltc = classGen.getParser().getXSLTC(); 341 342 // private boolean Translet.stripSpace(int type) - cannot be static 343 final MethodGenerator stripSpace = 344 new MethodGenerator(ACC_PUBLIC | ACC_FINAL , 345 com.sun.org.apache.bcel.internal.generic.Type.BOOLEAN, 346 new com.sun.org.apache.bcel.internal.generic.Type[] { 347 Util.getJCRefType(DOM_INTF_SIG), 348 com.sun.org.apache.bcel.internal.generic.Type.INT, 349 com.sun.org.apache.bcel.internal.generic.Type.INT 350 }, 351 new String[] { "dom","node","type" }, 352 "stripSpace",classGen.getClassName(),il,cpg); 353 354 classGen.addInterface("com/sun/org/apache/xalan/internal/xsltc/StripFilter"); 355 356 final int paramDom = stripSpace.getLocalIndex("dom"); 357 final int paramCurrent = stripSpace.getLocalIndex("node"); 358 final int paramType = stripSpace.getLocalIndex("type"); 359 360 BranchHandle strip[] = new BranchHandle[rules.size()]; 361 BranchHandle preserve[] = new BranchHandle[rules.size()]; 362 int sCount = 0; 363 int pCount = 0; 364 365 // Traverse all strip/preserve rules 366 for (int i = 0; i<rules.size(); i++) { 367 // Get the next rule in the prioritised list 368 WhitespaceRule rule = (WhitespaceRule)rules.elementAt(i); 369 370 // Returns the namespace for a node in the DOM 371 final int gns = cpg.addInterfaceMethodref(DOM_INTF, 372 "getNamespaceName", 373 "(I)Ljava/lang/String;"); 374 375 final int strcmp = cpg.addMethodref("java/lang/String", 376 "compareTo", 377 "(Ljava/lang/String;)I"); 378 379 // Handle elements="ns:*" type rule 380 if (rule.getStrength() == RULE_NAMESPACE) { 381 il.append(new ALOAD(paramDom)); 382 il.append(new ILOAD(paramCurrent)); 383 il.append(new INVOKEINTERFACE(gns,2)); 384 il.append(new PUSH(cpg, rule.getNamespace())); 385 il.append(new INVOKEVIRTUAL(strcmp)); 386 il.append(ICONST_0); 387 388 if (rule.getAction() == STRIP_SPACE) { 389 strip[sCount++] = il.append(new IF_ICMPEQ(null)); 390 } 391 else { 392 preserve[pCount++] = il.append(new IF_ICMPEQ(null)); 393 } 394 } 395 // Handle elements="ns:el" type rule 396 else if (rule.getStrength() == RULE_ELEMENT) { 397 // Create the QName for the element 398 final Parser parser = classGen.getParser(); 399 QName qname; 400 if (rule.getNamespace() != Constants.EMPTYSTRING ) 401 qname = parser.getQName(rule.getNamespace(), null, 402 rule.getElement()); 403 else 404 qname = parser.getQName(rule.getElement()); 405 406 // Register the element. 407 final int elementType = xsltc.registerElement(qname); 408 il.append(new ILOAD(paramType)); 409 il.append(new PUSH(cpg, elementType)); 410 411 // Compare current node type with wanted element type 412 if (rule.getAction() == STRIP_SPACE) 413 strip[sCount++] = il.append(new IF_ICMPEQ(null)); 414 else 415 preserve[pCount++] = il.append(new IF_ICMPEQ(null)); 416 } 417 } 418 419 if (defaultAction == STRIP_SPACE) { 420 compileStripSpace(strip, sCount, il); 421 compilePreserveSpace(preserve, pCount, il); 422 } 423 else { 424 compilePreserveSpace(preserve, pCount, il); 425 compileStripSpace(strip, sCount, il); 426 } 427 428 classGen.addMethod(stripSpace); 429 } 430 431 /** 432 * Compiles the predicate method 433 */ 434 private static void compileDefault(int defaultAction, 435 ClassGenerator classGen) { 436 final ConstantPoolGen cpg = classGen.getConstantPool(); 437 final InstructionList il = new InstructionList(); 438 final XSLTC xsltc = classGen.getParser().getXSLTC(); 439 440 // private boolean Translet.stripSpace(int type) - cannot be static 441 final MethodGenerator stripSpace = 442 new MethodGenerator(ACC_PUBLIC | ACC_FINAL , 443 com.sun.org.apache.bcel.internal.generic.Type.BOOLEAN, 444 new com.sun.org.apache.bcel.internal.generic.Type[] { 445 Util.getJCRefType(DOM_INTF_SIG), 446 com.sun.org.apache.bcel.internal.generic.Type.INT, 447 com.sun.org.apache.bcel.internal.generic.Type.INT 448 }, 449 new String[] { "dom","node","type" }, 450 "stripSpace",classGen.getClassName(),il,cpg); 451 452 classGen.addInterface("com/sun/org/apache/xalan/internal/xsltc/StripFilter"); 453 454 if (defaultAction == STRIP_SPACE) 455 il.append(ICONST_1); 456 else 457 il.append(ICONST_0); 458 il.append(IRETURN); 459 460 classGen.addMethod(stripSpace); 461 } 462 463 464 /** 465 * Takes a vector of WhitespaceRule objects and generates a predicate 466 * method. This method returns the translets default action for handling 467 * whitespace text-nodes: 468 * - USE_PREDICATE (run the method generated by this method) 469 * - STRIP_SPACE (always strip whitespace text-nodes) 470 * - PRESERVE_SPACE (always preserve whitespace text-nodes) 471 */ 472 public static int translateRules(Vector rules, 473 ClassGenerator classGen) { 474 // Get the core rules in prioritized order 475 final int defaultAction = prioritizeRules(rules); 476 // The rules vector may be empty after prioritising 477 if (rules.size() == 0) { 478 compileDefault(defaultAction,classGen); 479 return defaultAction; 480 } 481 // Now - create a predicate method and sequence through rules... 482 compilePredicate(rules, defaultAction, classGen); 483 // Return with the translets required action ( 484 return USE_PREDICATE; 485 } 486 487 /** 488 * Sorts a range of rules with regard to PRIORITY only 489 */ 490 private static void quicksort(Vector rules, int p, int r) { 491 while (p < r) { 492 final int q = partition(rules, p, r); 493 quicksort(rules, p, q); 494 p = q + 1; 495 } 496 } 497 498 /** 499 * Used with quicksort method above 500 */ 501 private static int partition(Vector rules, int p, int r) { 502 final WhitespaceRule x = (WhitespaceRule)rules.elementAt((p+r) >>> 1); 503 int i = p - 1, j = r + 1; 504 while (true) { 505 while (x.compareTo((WhitespaceRule)rules.elementAt(--j)) < 0) { 506 } 507 while (x.compareTo((WhitespaceRule)rules.elementAt(++i)) > 0) { 508 } 509 if (i < j) { 510 final WhitespaceRule tmp = (WhitespaceRule)rules.elementAt(i); 511 rules.setElementAt(rules.elementAt(j), i); 512 rules.setElementAt(tmp, j); 513 } 514 else { 515 return j; 516 } 517 } 518 } 519 520 /** 521 * Type-check contents/attributes - nothing to do... 522 */ 523 public Type typeCheck(SymbolTable stable) throws TypeCheckError { 524 return Type.Void; // We don't return anything. 525 } 526 527 /** 528 * This method should not produce any code 529 */ 530 public void translate(ClassGenerator classGen, MethodGenerator methodGen) { 531 } 532 }