blob: 0ea54cb7439c70481baaf5a3cdb6c18334955a7a [file] [log] [blame]
/*
* @(#)$Id$
*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2001 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 Jacek Ambroziak
* @author Santiago Pericas-Geertsen
* @author Erwin Bolwidt <ejb@klomp.org>
* @author Morten Jorgensen <morten.jorgensen@sun.com>
*
*/
package org.apache.xalan.xsltc.compiler;
import java.util.Vector;
import java.util.Hashtable;
import java.util.Dictionary;
import java.util.Enumeration;
import org.apache.bcel.generic.*;
import org.apache.xalan.xsltc.compiler.util.*;
/**
* A test sequence is a sequence of patterns that
*
* (1) occured in templates in the same mode
* (2) share the same kernel node type (such as A/B and C/C/B).
*
* A test sequence may have a default template, which will be run if
* none of the patterns do not match. This template is always a template
* that matches solely on the shared kernel node type.
*/
final class TestSeq {
private Vector _patterns = null; // all patterns
private Mode _mode = null; // the shared mode
private Template _default = null; // the default template
private InstructionList _instructionList;
/**
* Creates a new test sequence, given a set of patterns and a mode.
*/
public TestSeq(Vector patterns, Mode mode) {
_patterns = patterns;
_mode = mode;
}
/**
* The priority is only calculated if the test sequence has a default
* template. This is bad, bad, bad. We should get the priority from the
* other templates that make up the test sequence.
*/
public double getPriority() {
double prio = (0 - Double.MAX_VALUE);
final int count = _patterns.size();
for (int i = 0; i < count; i++) {
final Pattern pattern = (Pattern)_patterns.elementAt(i);
final Template template = pattern.getTemplate();
final double tp = template.getPriority();
if (tp > prio) prio = tp;
}
if (_default != null) {
final double tp = _default.getPriority();
if (tp > prio) prio = tp;
}
return prio;
}
/**
* This method should return the last position of any template included
* in this test sequence.
*/
public int getPosition() {
int pos = Integer.MIN_VALUE;
final int count = _patterns.size();
for (int i = 0; i < count; i++) {
final Pattern pattern = (Pattern)_patterns.elementAt(i);
final Template template = pattern.getTemplate();
final int tp = template.getPosition();
if (tp > pos) pos = tp;
}
if (_default != null) {
final int tp = _default.getPosition();
if (tp > pos) pos = tp;
}
return pos;
}
/**
* Reduce the patterns in this test sequence to exclude the shared
* kernel node type. After the switch() in the translet's applyTemplates()
* we already know that we have a hit for the kernel node type, we only
* have the check the rest of the pattern.
*/
public void reduce() {
final Vector newPatterns = new Vector();
final int count = _patterns.size();
// Traverse the existing set of patterns (they are in prioritised order)
for (int i = 0; i < count; i++) {
final LocationPathPattern pattern =
(LocationPathPattern)_patterns.elementAt(i);
// Reduce this pattern (get rid of kernel node type)
pattern.reduceKernelPattern();
// Add this pattern to the new vector of patterns.
if (!pattern.isWildcard()) {
newPatterns.addElement(pattern);
}
// Set template as default if its pattern matches purely on kernel
else {
_default = pattern.getTemplate();
// Following patterns can be ignored since default has priority
break;
}
}
_patterns = newPatterns;
}
/**
* Returns, by reference, the templates that are included in this test
* sequence. Remember that a single template can occur in several test
* sequences if its pattern is a union (ex. match="A/B | A/C").
*/
public void findTemplates(Dictionary templates) {
if (_default != null)
templates.put(_default, this);
for (int i = 0; i < _patterns.size(); i++) {
final LocationPathPattern pattern =
(LocationPathPattern)_patterns.elementAt(i);
templates.put(pattern.getTemplate(), this);
}
}
/**
* Get the instruction handle to a template's code. This is used when
* a single template occurs in several test sequences; that is, if its
* pattern is a union of patterns (ex. match="A/B | A/C").
*/
private InstructionHandle getTemplateHandle(Template template) {
return (InstructionHandle)_mode.getTemplateInstructionHandle(template);
}
/**
* Returns pattern n in this test sequence
*/
private LocationPathPattern getPattern(int n) {
return (LocationPathPattern)_patterns.elementAt(n);
}
private InstructionHandle _start = null;
/**
* Copile the code for this test sequence. The code will first test for
* the pattern with the highest priority, then go on to the next ones,
* until it hits or finds the default template.
*/
public InstructionHandle compile(ClassGenerator classGen,
MethodGenerator methodGen,
InstructionHandle continuation) {
final int count = _patterns.size();
if (_start != null) return(_start);
// EZ DC if there is only one (default) pattern
if (count == 0) getTemplateHandle(_default);
// The 'fail' instruction handle represents a branch to go to when
// test fails. It is updated in each iteration, so that the tests
// are linked together in the if-elseif-elseif-else fashion.
InstructionHandle fail;
// Initialize 'fail' to either the code for the default template
if (_default != null)
fail = getTemplateHandle(_default);
// ..or if that does not exist, to a location set by the caller.
else
fail = continuation;
for (int n = (count - 1); n >= 0; n--) {
final LocationPathPattern pattern = getPattern(n);
final Template template = pattern.getTemplate();
final InstructionList il = new InstructionList();
// Patterns expect current node on top of stack
il.append(methodGen.loadCurrentNode());
// Apply the test-code compiled for the pattern
il.append(pattern.compile(classGen, methodGen));
// On success branch to the template code
final InstructionHandle gtmpl = getTemplateHandle(template);
final InstructionHandle success = il.append(new GOTO_W(gtmpl));
pattern.backPatchTrueList(success);
pattern.backPatchFalseList(fail);
// We're working backwards here. The next pattern's 'fail' target
// is this pattern's first instruction
fail = il.getStart();
// Append existing instruction list to the end of this one
if (_instructionList != null) il.append(_instructionList);
// Set current instruction list to be this one.
_instructionList = il;
}
return(_start = fail);
}
/**
* Returns the instruction list for this test sequence
*/
public InstructionList getInstructionList() {
return _instructionList;
}
}