blob: 44b991bb2ebf837d84c6fee493c2d07b19005677 [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 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 java.util.Iterator;
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 (e.g. A/B and C/C/B)
* (3) may also contain patterns matching "*" and "node()"
* (element sequence only) or matching "@*" (attribute
* sequence only).
*
* A test sequence may have a default template, which will be
* instantiated if none of the other patterns match.
*/
final class TestSeq {
/**
* Integer code for the kernel type of this test sequence
*/
private int _kernelType;
/**
* Vector of all patterns in the test sequence. May include
* patterns with "*", "@*" or "node()" kernel.
*/
private Vector _patterns = null;
/**
* A reference to the Mode object.
*/
private Mode _mode = null;
/**
* Default template for this test sequence
*/
private Template _default = null;
/**
* Instruction list representing this test sequence.
*/
private InstructionList _instructionList;
/**
* Cached handle to avoid compiling more than once.
*/
private InstructionHandle _start = null;
/**
* Creates a new test sequence given a set of patterns and a mode.
*/
public TestSeq(Vector patterns, Mode mode) {
this(patterns, -2, mode);
}
public TestSeq(Vector patterns, int kernelType, Mode mode) {
_patterns = patterns;
_kernelType = kernelType;
_mode = mode;
}
/**
* Returns a string representation of this test sequence. Notice
* that test sequences are mutable, so the value returned by this
* method is different before and after calling reduce().
*/
public String toString() {
final int count = _patterns.size();
final StringBuffer result = new StringBuffer();
for (int i = 0; i < count; i++) {
final LocationPathPattern pattern =
(LocationPathPattern) _patterns.elementAt(i);
if (i == 0) {
result.append("Testseq for kernel " + _kernelType)
.append('\n');
}
result.append(" pattern " + i + ": ")
.append(pattern.toString())
.append('\n');
}
return result.toString();
}
/**
* Returns the instruction list for this test sequence
*/
public InstructionList getInstructionList() {
return _instructionList;
}
/**
* Return the highest priority for a pattern in this test
* sequence. This is either the priority of the first or
* of the default pattern.
*/
public double getPriority() {
final Template template = (_patterns.size() == 0) ? _default
: ((Pattern) _patterns.elementAt(0)).getTemplate();
return template.getPriority();
}
/**
* Returns the position of the highest priority pattern in
* this test sequence.
*/
public int getPosition() {
final Template template = (_patterns.size() == 0) ? _default
: ((Pattern) _patterns.elementAt(0)).getTemplate();
return template.getPosition();
}
/**
* Reduce the patterns in this test sequence. Creates a new
* vector of patterns and sets the default pattern if it
* finds a patterns that is fully reduced.
*/
public void reduce() {
final Vector newPatterns = new Vector();
final int count = _patterns.size();
for (int i = 0; i < count; i++) {
final LocationPathPattern pattern =
(LocationPathPattern)_patterns.elementAt(i);
// Reduce this pattern
pattern.reduceKernelPattern();
// Is this pattern fully reduced?
if (pattern.isWildcard()) {
_default = pattern.getTemplate();
break; // Ignore following patterns
}
else {
newPatterns.addElement(pattern);
}
}
_patterns = newPatterns;
}
/**
* Returns, by reference, the templates that are included in
* this test sequence. Note that a single template can occur
* in several test sequences if its pattern is a union.
*/
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
* (e.g. 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);
}
/**
* Compile the code for this test sequence. Compile patterns
* from highest to lowest priority. Note that since patterns
* can be share by multiple test sequences, instruction lists
* must be copied before backpatching.
*/
public InstructionHandle compile(ClassGenerator classGen,
MethodGenerator methodGen,
InstructionHandle continuation)
{
// Returned cached value if already compiled
if (_start != null) {
return _start;
}
// If not patterns, then return handle for default template
final int count = _patterns.size();
if (count == 0) {
return (_start = getTemplateHandle(_default));
}
// Init handle to jump when all patterns failed
InstructionHandle fail = (_default == null) ? continuation
: getTemplateHandle(_default);
// Compile all patterns in reverse order
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
InstructionList ilist = _mode.getInstructionList(pattern);
if (ilist == null) {
ilist = pattern.compile(classGen, methodGen);
_mode.addInstructionList(pattern, ilist);
}
// Make a copy of the instruction list for backpatching
InstructionList copyOfilist = ilist.copy();
FlowList trueList = pattern.getTrueList();
if (trueList != null) {
trueList = trueList.copyAndRedirect(ilist, copyOfilist);
}
FlowList falseList = pattern.getFalseList();
if (falseList != null) {
falseList = falseList.copyAndRedirect(ilist, copyOfilist);
}
il.append(copyOfilist);
// On success branch to the template code
final InstructionHandle gtmpl = getTemplateHandle(template);
final InstructionHandle success = il.append(new GOTO_W(gtmpl));
if (trueList != null) {
trueList.backPatch(success);
}
if (falseList != null) {
falseList.backPatch(fail);
}
// 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);
}
}