blob: 1e2eabd59bdc48d29574fcbd94baa362cd9d7c6e [file] [log] [blame]
/*
* @(#)$Id$
*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2001-2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xalan" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 2001, Sun
* Microsystems., http://www.sun.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* @author Morten Jorgensen
*
*/
package org.apache.xalan.xsltc.compiler;
import java.util.Vector;
import java.util.StringTokenizer;
import org.apache.xalan.xsltc.compiler.util.Type;
import org.apache.xalan.xsltc.compiler.util.ReferenceType;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Field;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.*;
import org.apache.xalan.xsltc.dom.*;
import org.apache.xalan.xsltc.compiler.util.*;
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 ,
org.apache.bcel.generic.Type.BOOLEAN,
new org.apache.bcel.generic.Type[] {
Util.getJCRefType(DOM_INTF_SIG),
org.apache.bcel.generic.Type.INT,
org.apache.bcel.generic.Type.INT
},
new String[] { "dom","node","type" },
"stripSpace",classGen.getClassName(),il,cpg);
classGen.addInterface("org/apache/xalan/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<rules.size(); i++) {
// Get the next rule in the prioritised list
WhitespaceRule rule = (WhitespaceRule)rules.elementAt(i);
// Returns the namespace for a node in the DOM
final int gns = cpg.addInterfaceMethodref(DOM_INTF,
"getNamespaceName",
"(I)Ljava/lang/String;");
final int strcmp = cpg.addMethodref("java/lang/String",
"compareTo",
"(Ljava/lang/String;)I");
// Handle elements="ns:*" type rule
if (rule.getStrength() == RULE_NAMESPACE) {
il.append(new ALOAD(paramDom));
il.append(new ILOAD(paramCurrent));
il.append(new INVOKEINTERFACE(gns,2));
il.append(new PUSH(cpg, rule.getNamespace()));
il.append(new INVOKEVIRTUAL(strcmp));
il.append(ICONST_0);
if (rule.getAction() == STRIP_SPACE) {
strip[sCount++] = il.append(new IF_ICMPEQ(null));
}
else {
preserve[pCount++] = il.append(new IF_ICMPEQ(null));
}
}
// Handle elements="ns:el" type rule
else if (rule.getStrength() == RULE_ELEMENT) {
// Create the QName for the element
final Parser parser = classGen.getParser();
QName qname;
if (rule.getNamespace() != Constants.EMPTYSTRING )
qname = parser.getQName(rule.getNamespace(), null,
rule.getElement());
else
qname = parser.getQName(rule.getElement());
// Register the element.
final int elementType = xsltc.registerElement(qname);
il.append(new ILOAD(paramType));
il.append(new PUSH(cpg, elementType));
// Compare current node type with wanted element type
if (rule.getAction() == STRIP_SPACE)
strip[sCount++] = il.append(new IF_ICMPEQ(null));
else
preserve[pCount++] = il.append(new IF_ICMPEQ(null));
}
}
if (defaultAction == STRIP_SPACE) {
compileStripSpace(strip, sCount, il);
compilePreserveSpace(preserve, pCount, il);
}
else {
compilePreserveSpace(preserve, pCount, il);
compileStripSpace(strip, sCount, il);
}
stripSpace.stripAttributes(true);
stripSpace.setMaxLocals();
stripSpace.setMaxStack();
stripSpace.removeNOPs();
classGen.addMethod(stripSpace.getMethod());
}
/**
* Compiles the predicate method
*/
private static void compileDefault(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 ,
org.apache.bcel.generic.Type.BOOLEAN,
new org.apache.bcel.generic.Type[] {
Util.getJCRefType(DOM_INTF_SIG),
org.apache.bcel.generic.Type.INT,
org.apache.bcel.generic.Type.INT
},
new String[] { "dom","node","type" },
"stripSpace",classGen.getClassName(),il,cpg);
classGen.addInterface("org/apache/xalan/xsltc/StripFilter");
if (defaultAction == STRIP_SPACE)
il.append(ICONST_1);
else
il.append(ICONST_0);
il.append(IRETURN);
stripSpace.stripAttributes(true);
stripSpace.setMaxLocals();
stripSpace.setMaxStack();
stripSpace.removeNOPs();
classGen.addMethod(stripSpace.getMethod());
}
/**
* Takes a vector of WhitespaceRule objects and generates a predicate
* method. This method returns the translets default action for handling
* whitespace text-nodes:
* - USE_PREDICATE (run the method generated by this method)
* - STRIP_SPACE (always strip whitespace text-nodes)
* - PRESERVE_SPACE (always preserve whitespace text-nodes)
*/
public static int translateRules(Vector rules,
ClassGenerator classGen) {
// Get the core rules in prioritized order
final int defaultAction = prioritizeRules(rules);
// The rules vector may be empty after prioritising
if (rules.size() == 0) {
compileDefault(defaultAction,classGen);
return defaultAction;
}
// Now - create a predicate method and sequence through rules...
compilePredicate(rules, defaultAction, classGen);
// Return with the translets required action (
return USE_PREDICATE;
}
/**
* Sorts a range of rules with regard to PRIORITY only
*/
private static void quicksort(Vector rules, int p, int r) {
while (p < r) {
final int q = partition(rules, p, r);
quicksort(rules, p, q);
p = q + 1;
}
}
/**
* Used with quicksort method above
*/
private static int partition(Vector rules, int p, int r) {
final WhitespaceRule x = (WhitespaceRule)rules.elementAt((p+r) >>> 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) {
}
}