/* * Copyright 2001-2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * $Id: Whitespace.java,v 1.10 2004/02/16 22:25:10 minchau Exp $ */ package com.sun.org.apache.xalan.internal.xsltc.compiler; import java.util.StringTokenizer; import java.util.Vector; import com.sun.org.apache.bcel.internal.generic.ALOAD; import com.sun.org.apache.bcel.internal.generic.BranchHandle; import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen; import com.sun.org.apache.bcel.internal.generic.IF_ICMPEQ; import com.sun.org.apache.bcel.internal.generic.ILOAD; import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE; import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL; import com.sun.org.apache.bcel.internal.generic.InstructionHandle; import com.sun.org.apache.bcel.internal.generic.InstructionList; import com.sun.org.apache.bcel.internal.generic.PUSH; import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator; import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg; import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator; import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type; import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError; import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util; /** * @author Morten Jorgensen */ final class Whitespace extends TopLevelElement { // Three possible actions for the translet: public static final int USE_PREDICATE = 0; public static final int STRIP_SPACE = 1; public static final int PRESERVE_SPACE = 2; // The 3 different categories of strip/preserve rules (order important) public static final int RULE_NONE = 0; public static final int RULE_ELEMENT = 1; // priority 0 public static final int RULE_NAMESPACE = 2; // priority -1/4 public static final int RULE_ALL = 3; // priority -1/2 private String _elementList; private int _action; private int _importPrecedence; /** * Auxillary class for encapsulating a single strip/preserve rule */ private final static class WhitespaceRule { private final int _action; private String _namespace; // Should be replaced by NS type (int) private String _element; // Should be replaced by node type (int) private int _type; private int _priority; /** * Strip/preserve rule constructor */ public WhitespaceRule(int action, String element, int precedence) { // Determine the action (strip or preserve) for this rule _action = action; // Get the namespace and element name for this rule final int colon = element.indexOf(':'); if (colon >= 0) { _namespace = element.substring(0,colon); _element = element.substring(colon+1,element.length()); } else { _namespace = Constants.EMPTYSTRING; _element = element; } // Determine the initial priority for this rule _priority = precedence << 2; // Get the strip/preserve type; either "NS:EL", "NS:*" or "*" if (_element.equals("*")) { if (_namespace == Constants.EMPTYSTRING) { _type = RULE_ALL; // Strip/preserve _all_ elements _priority += 2; // Lowest priority } else { _type = RULE_NAMESPACE; // Strip/reserve elements within NS _priority += 1; // Medium priority } } else { _type = RULE_ELEMENT; // Strip/preserve single element } } /** * For sorting rules depending on priority */ public int compareTo(WhitespaceRule other) { return _priority < other._priority ? -1 : _priority > other._priority ? 1 : 0; } public int getAction() { return _action; } public int getStrength() { return _type; } public int getPriority() { return _priority; } public String getElement() { return _element; } public String getNamespace() { return _namespace; } } /** * Parse the attributes of the xsl:strip/preserve-space element. * The element should have not contents (ignored if any). */ public void parseContents(Parser parser) { // Determine if this is an xsl:strip- or preserve-space element _action = _qname.getLocalPart().endsWith("strip-space") ? STRIP_SPACE : PRESERVE_SPACE; // Determine the import precedence _importPrecedence = parser.getCurrentImportPrecedence(); // Get the list of elements to strip/preserve _elementList = getAttribute("elements"); if (_elementList == null || _elementList.length() == 0) { reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR, "elements"); return; } final SymbolTable stable = parser.getSymbolTable(); StringTokenizer list = new StringTokenizer(_elementList); StringBuffer elements = new StringBuffer(Constants.EMPTYSTRING); while (list.hasMoreElements()) { String token = list.nextToken(); String prefix; String namespace; int col; if ((col = token.indexOf(':')) != -1) { prefix = token.substring(0,col); } else { prefix = Constants.EMPTYSTRING; } namespace = lookupNamespace(prefix); if (namespace != null) elements.append(namespace+":"+ token.substring(col+1,token.length())); else elements.append(token); if (list.hasMoreElements()) elements.append(" "); } _elementList = elements.toString(); } /** * De-tokenize the elements listed in the 'elements' attribute and * instanciate a set of strip/preserve rules. */ public Vector getRules() { final Vector rules = new Vector(); // Go through each element and instanciate strip/preserve-object final StringTokenizer list = new StringTokenizer(_elementList); while (list.hasMoreElements()) { rules.add(new WhitespaceRule(_action, list.nextToken(), _importPrecedence)); } return rules; } /** * Scans through the rules vector and looks for a rule of higher * priority that contradicts the current rule. */ private static WhitespaceRule findContradictingRule(Vector rules, WhitespaceRule rule) { for (int i = 0; i < rules.size(); i++) { // Get the next rule in the prioritized list WhitespaceRule currentRule = (WhitespaceRule)rules.elementAt(i); // We only consider rules with higher priority if (currentRule == rule) { return null; } /* * See if there is a contradicting rule with higher priority. * If the rules has the same action then this rule is redundant, * if they have different action then this rule will never win. */ switch (currentRule.getStrength()) { case RULE_ALL: return currentRule; case RULE_ELEMENT: if (!rule.getElement().equals(currentRule.getElement())) { break; } // intentional fall-through case RULE_NAMESPACE: if (rule.getNamespace().equals(currentRule.getNamespace())) { return currentRule; } break; } } return null; } /** * Orders a set or rules by priority, removes redundant rules and rules * that are shadowed by stronger, contradicting rules. */ private static int prioritizeRules(Vector rules) { WhitespaceRule currentRule; int defaultAction = PRESERVE_SPACE; // Sort all rules with regard to priority quicksort(rules, 0, rules.size()-1); // Check if there are any "xsl:strip-space" elements at all. // If there are no xsl:strip elements we can ignore all xsl:preserve // elements and signal that all whitespaces should be preserved boolean strip = false; for (int i = 0; i < rules.size(); i++) { currentRule = (WhitespaceRule)rules.elementAt(i); if (currentRule.getAction() == STRIP_SPACE) { strip = true; } } // Return with default action: PRESERVE_SPACE if (!strip) { rules.removeAllElements(); return PRESERVE_SPACE; } // Remove all rules that are contradicted by rules with higher priority for (int idx = 0; idx < rules.size(); ) { currentRule = (WhitespaceRule)rules.elementAt(idx); // Remove this single rule if it has no purpose if (findContradictingRule(rules,currentRule) != null) { rules.remove(idx); } else { // Remove all following rules if this one overrides all if (currentRule.getStrength() == RULE_ALL) { defaultAction = currentRule.getAction(); for (int i = idx; i < rules.size(); i++) { rules.removeElementAt(i); } } // Skip to next rule (there might not be any)... idx++; } } // The rules vector could be empty if first rule has strength RULE_ALL if (rules.size() == 0) { return defaultAction; } // Now work backwards and strip away all rules that have the same // action as the default rule (no reason the check them at the end). do { currentRule = (WhitespaceRule)rules.lastElement(); if (currentRule.getAction() == defaultAction) { rules.removeElementAt(rules.size() - 1); } else { break; } } while (rules.size() > 0); // Signal that whitespace detection predicate must be used. return defaultAction; } public static void compileStripSpace(BranchHandle strip[], int sCount, InstructionList il) { final InstructionHandle target = il.append(ICONST_1); il.append(IRETURN); for (int i = 0; i < sCount; i++) { strip[i].setTarget(target); } } public static void compilePreserveSpace(BranchHandle preserve[], int pCount, InstructionList il) { final InstructionHandle target = il.append(ICONST_0); il.append(IRETURN); for (int i = 0; i < pCount; i++) { preserve[i].setTarget(target); } } /* private static void compileDebug(ClassGenerator classGen, InstructionList il) { final ConstantPoolGen cpg = classGen.getConstantPool(); final int prt = cpg.addMethodref("java/lang/System/out", "println", "(Ljava/lang/String;)V"); il.append(DUP); il.append(new INVOKESTATIC(prt)); } */ /** * Compiles the predicate method */ private static void compilePredicate(Vector rules, int defaultAction, ClassGenerator classGen) { final ConstantPoolGen cpg = classGen.getConstantPool(); final InstructionList il = new InstructionList(); final XSLTC xsltc = classGen.getParser().getXSLTC(); // private boolean Translet.stripSpace(int type) - cannot be static final MethodGenerator stripSpace = new MethodGenerator(ACC_PUBLIC | ACC_FINAL , com.sun.org.apache.bcel.internal.generic.Type.BOOLEAN, new com.sun.org.apache.bcel.internal.generic.Type[] { Util.getJCRefType(DOM_INTF_SIG), com.sun.org.apache.bcel.internal.generic.Type.INT, com.sun.org.apache.bcel.internal.generic.Type.INT }, new String[] { "dom","node","type" }, "stripSpace",classGen.getClassName(),il,cpg); classGen.addInterface("com/sun/org/apache/xalan/internal/xsltc/StripFilter"); final int paramDom = stripSpace.getLocalIndex("dom"); final int paramCurrent = stripSpace.getLocalIndex("node"); final int paramType = stripSpace.getLocalIndex("type"); BranchHandle strip[] = new BranchHandle[rules.size()]; BranchHandle preserve[] = new BranchHandle[rules.size()]; int sCount = 0; int pCount = 0; // Traverse all strip/preserve rules for (int i = 0; i>> 1); int i = p - 1, j = r + 1; while (true) { while (x.compareTo((WhitespaceRule)rules.elementAt(--j)) < 0) { } while (x.compareTo((WhitespaceRule)rules.elementAt(++i)) > 0) { } if (i < j) { final WhitespaceRule tmp = (WhitespaceRule)rules.elementAt(i); rules.setElementAt(rules.elementAt(j), i); rules.setElementAt(tmp, j); } else { return j; } } } /** * Type-check contents/attributes - nothing to do... */ public Type typeCheck(SymbolTable stable) throws TypeCheckError { return Type.Void; // We don't return anything. } /** * This method should not produce any code */ public void translate(ClassGenerator classGen, MethodGenerator methodGen) { } }