package client.net.sf.saxon.ce.trans;
import client.net.sf.saxon.ce.expr.Expression;
import client.net.sf.saxon.ce.expr.Token;
import client.net.sf.saxon.ce.expr.XPathContext;
import client.net.sf.saxon.ce.expr.instruct.Template;
import client.net.sf.saxon.ce.om.NodeInfo;
import client.net.sf.saxon.ce.om.StructuredQName;
import client.net.sf.saxon.ce.pattern.*;
import client.net.sf.saxon.ce.style.StylesheetModule;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
/**
* <B>RuleManager</B> maintains a set of template rules, one set for each mode
* @version 10 December 1999: carved out of the old Controller class
* @author Michael H. Kay
*/
public final class RuleManager {
private Mode unnamedMode; // node handlers with default mode
private HashMap<StructuredQName, Mode> modes;
// tables of node handlers for non-default modes
private Mode omniMode = null; // node handlers that specify mode="all"
private int recoveryPolicy;
/**
* create a RuleManager and initialise variables.
*/
public RuleManager() {
resetHandlers();
}
/**
* Set the policy for handling recoverable errrors. Note that for some errors the decision can be
* made at run-time, but for the "ambiguous template match" error, the decision is (since 9.2)
* fixed at compile time.
* @param policy the recovery policy to be used. The options are {@link client.net.sf.saxon.ce.Configuration#RECOVER_SILENTLY},
* {@link client.net.sf.saxon.ce.Configuration#RECOVER_WITH_WARNINGS}, or {@link client.net.sf.saxon.ce.Configuration#DO_NOT_RECOVER}.
* @since 9.2
*/
public void setRecoveryPolicy(int policy) {
recoveryPolicy = policy;
unnamedMode.setRecoveryPolicy(policy);
}
/**
* Get the policy for handling recoverable errors. Note that for some errors the decision can be
* made at run-time, but for the "ambiguous template match" error, the decision is (since 9.2)
* fixed at compile time.
*
* @return the current policy.
* @since 9.2
*/
public int getRecoveryPolicy() {
return recoveryPolicy;
}
/**
* Set up a new table of handlers.
*/
public void resetHandlers() {
unnamedMode = new Mode(Mode.UNNAMED_MODE, Mode.UNNAMED_MODE_NAME);
unnamedMode.setRecoveryPolicy(recoveryPolicy);
modes = new HashMap<StructuredQName, Mode>(5);
}
/**
* Get the mode object for the unnamed mode
*/
public Mode getUnnamedMode() {
return unnamedMode;
}
/**
* Get the Mode object for a named mode. If there is not one already registered.
* a new Mode is created.
* @param modeName The name of the mode. Supply null to get the default
* mode or Mode.ALL_MODES to get the Mode object containing "mode=all" rules
* @param createIfAbsent if true, then if the mode does not already exist it will be created.
* If false, then if the mode does not already exist the method returns null.
* @return the Mode with this name
*/
public Mode getMode(StructuredQName modeName, boolean createIfAbsent) {
if (modeName == null || modeName.equals(Mode.UNNAMED_MODE_NAME)) {
return unnamedMode;
}
if (modeName.equals(Mode.ALL_MODES)) {
if (omniMode==null) {
omniMode = new Mode(Mode.NAMED_MODE, modeName);
omniMode.setRecoveryPolicy(recoveryPolicy);
}
return omniMode;
}
//Integer modekey = new Integer(modeNameCode & 0xfffff);
Mode m = modes.get(modeName);
if (m == null && createIfAbsent) {
m = new Mode(omniMode, modeName);
m.setRecoveryPolicy(recoveryPolicy);
modes.put(modeName, m);
// when creating a specific mode, copy all the rules currently held
// in the omniMode, as these apply to all modes
}
return m;
}
/**
* Get all the modes in a given namespace
* @param namespace the namespace URI
* @return a list of modes whose names are in this namespace
*/
public List<Mode> getModesInNamespace(String namespace) {
List<Mode> result = new ArrayList<Mode>();
for (StructuredQName name : modes.keySet()) {
if (namespace.equals(name.getNamespaceURI())) {
result.add(modes.get(name));
}
}
return result;
}
/**
* Register a template rule for a particular pattern. The priority of the rule
* is the default priority for the pattern, which depends on the syntax of
* the pattern suppllied.
* @param pattern A match pattern
* @param eh The Template to be used
* @param mode The processing mode
* @param module The stylesheet module containing the template rule
*/
// public void setTemplateRule(Pattern pattern, Template eh, Mode mode, StylesheetModule module) {
// // for a union pattern, register the parts separately (each with its own priority)
// if (pattern instanceof UnionPattern) {
// UnionPattern up = (UnionPattern)pattern;
// Pattern p1 = up.getLHS();
// Pattern p2 = up.getRHS();
// setTemplateRule(p1, eh, mode, module);
// setTemplateRule(p2, eh, mode, module);
// return;
// }
//
// double priority = pattern.getDefaultPriority();
// setTemplateRule(pattern, eh, mode, module, priority);
// }
/**
* Register a template for a particular pattern.
* @param pattern Must be a valid Pattern.
* @param eh The Template to be used
* @param mode The processing mode to which this template applies
* @param module The stylesheet module containing the template rule
* @param priority The priority of the rule: if an element matches several patterns, the
* one with highest priority is used
* @see Pattern
*/
public void setTemplateRule(Pattern pattern, Template eh, Mode mode, StylesheetModule module,
double priority, boolean ixslPreventDefault, String ixslEventProperty) {
// for a union pattern, register the parts separately
// Note: technically this is only necessary if using default priorities and if the priorities
// of the two halves are different. However, splitting increases the chance that the pattern
// can be matched by hashing on the element name, so we do it always
if (pattern instanceof UnionPattern) {
UnionPattern up = (UnionPattern)pattern;
Pattern p1 = up.getLHS();
Pattern p2 = up.getRHS();
Expression currentSetter = up.getVariableBindingExpression();
if (currentSetter != null) {
p1.setVariableBindingExpression(currentSetter);
p2.setVariableBindingExpression(currentSetter);
}
setTemplateRule(p1, eh, mode, module, priority, ixslPreventDefault, ixslEventProperty);
setTemplateRule(p2, eh, mode, module, priority, ixslPreventDefault, ixslEventProperty);
return;
}
// some union patterns end up as a CombinedNodeTest. Need to split these.
// (Same reasoning as above)
if (pattern instanceof NodeTestPattern &&
pattern.getNodeTest() instanceof CombinedNodeTest &&
((CombinedNodeTest)pattern.getNodeTest()).getOperator() == Token.UNION) {
CombinedNodeTest cnt = (CombinedNodeTest)pattern.getNodeTest();
NodeTest[] nt = cnt.getComponentNodeTests();
setTemplateRule(new NodeTestPattern(nt[0]), eh, mode, module,
priority, ixslPreventDefault, ixslEventProperty);
setTemplateRule(new NodeTestPattern(nt[1]), eh, mode, module,
priority, ixslPreventDefault, ixslEventProperty);
return;
}
if (Double.isNaN(priority)) {
priority = pattern.getDefaultPriority();
}
mode.addRule(pattern, eh, module, priority, true, ixslPreventDefault, ixslEventProperty);
// if adding a rule to the omniMode (mode='all') add it to all
// the other modes as well
if (mode==omniMode) {
unnamedMode.addRule(pattern, eh, module, priority, false, ixslPreventDefault, ixslEventProperty);
Iterator<Mode> iter = modes.values().iterator();
while (iter.hasNext()) {
Mode m = iter.next();
m.addRule(pattern, eh, module, priority, false, ixslPreventDefault, ixslEventProperty);
}
}
}
/**
* Find the template rule registered for a particular node in a specific mode.
* @param node The NodeInfo for the relevant node
* @param mode The processing mode
* @param c The controller for this transformation
* @return The template rule that will process this node
* Returns null if there is no specific handler registered.
*/
public Rule getTemplateRule (NodeInfo node, Mode mode, XPathContext c) throws XPathException {
if (mode==null) {
mode = unnamedMode;
}
return mode.getRule(node, c);
}
/**
* Get a template rule whose import precedence is in a particular range. This is used to support
* the xsl:apply-imports function
* @param node The node to be matched
* @param mode The mode for which a rule is required
* @param min The minimum import precedence that the rule must have
* @param max The maximum import precedence that the rule must have
* @param c The Controller for the transformation
* @return The template rule to be invoked
* @throws XPathException
*/
public Rule getTemplateRule (NodeInfo node, Mode mode, int min, int max, XPathContext c)
throws XPathException {
if (mode==null) {
mode = unnamedMode;
}
return mode.getRule(node, min, max, c);
}
/**
* Get the next-match handler after the current one
* @param node The node to be matched
* @param mode The processing mode
* @param currentRule The current template rule
* @param c The dynamic context for the transformation
* @return The template rule to be executed
* @throws XPathException
*/
public Rule getNextMatchHandler(NodeInfo node, Mode mode, Rule currentRule, XPathContext c)
throws XPathException {
if (mode==null) {
mode = unnamedMode;
}
return mode.getNextMatchRule(node, currentRule, c);
}
/**
* Allocate rankings to the rules within each mode. This method must be called when all
* the rules in each mode are known
*/
public void computeRankings() throws XPathException {
unnamedMode.computeRankings();
Iterator<Mode> iter = modes.values().iterator();
while (iter.hasNext()) {
Mode mode = iter.next();
mode.computeRankings();
}
}
}
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0.