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