package client.net.sf.saxon.ce.style;
import client.net.sf.saxon.ce.LogController;
import client.net.sf.saxon.ce.PreparedStylesheet;
import client.net.sf.saxon.ce.expr.Expression;
import client.net.sf.saxon.ce.expr.Literal;
import client.net.sf.saxon.ce.expr.TraceExpression;
import client.net.sf.saxon.ce.expr.instruct.*;
import client.net.sf.saxon.ce.lib.NamespaceConstant;
import client.net.sf.saxon.ce.lib.Validation;
import client.net.sf.saxon.ce.om.*;
import client.net.sf.saxon.ce.trace.Location;
import client.net.sf.saxon.ce.trans.XPathException;
import client.net.sf.saxon.ce.tree.linked.DocumentImpl;
import client.net.sf.saxon.ce.tree.linked.LinkedTreeBuilder;
import client.net.sf.saxon.ce.tree.util.NamespaceIterator;
import client.net.sf.saxon.ce.tree.util.Navigator;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.google.gwt.logging.client.LogConfiguration;
/**
* This class represents a literal result element in the style sheet
* (typically an HTML element to be output). <br>
* It is also used to represent unknown top-level elements, which are ignored.
*/
public class LiteralResultElement extends StyleElement {
private int resultNameCode;
private int[] attributeNames;
private Expression[] attributeValues;
private int numberOfAttributes;
private boolean toplevel;
private List<NamespaceBinding> namespaceCodes = new ArrayList();
private AttributeSet[] attributeSets;
private boolean inheritNamespaces = true;
/**
* Determine whether this type of element is allowed to contain a sequence constructor
* @return true: yes, it may contain a sequence constructor
*/
public boolean mayContainSequenceConstructor() {
return true;
}
/**
* Specify that this is an instruction
*/
public boolean isInstruction() {
return true;
}
/**
* Process the attribute list
*/
public void prepareAttributes() throws XPathException {
// Process the values of all attributes. At this stage we deal with attribute
// values (especially AVTs), but we do not apply namespace aliasing to the
// attribute names.
AttributeCollection atts = getAttributeList();
int num = atts.getLength();
if (num == 0) {
numberOfAttributes = 0;
} else {
NamePool namePool = getNamePool();
attributeNames = new int[num];
attributeValues = new Expression[num];
numberOfAttributes = 0;
for (int i=0; i<num; i++) {
int anameCode = atts.getNameCode(i);
short attURIcode = namePool.getURICode(anameCode);
if (attURIcode==NamespaceConstant.XSLT_CODE) {
int fp = anameCode & NamePool.FP_MASK;
if (fp == StandardNames.XSL_USE_ATTRIBUTE_SETS) {
// deal with this later
} else if (fp == StandardNames.XSL_DEFAULT_COLLATION) {
// already dealt with
} else if (fp == StandardNames.XSL_EXTENSION_ELEMENT_PREFIXES) {
// already dealt with
} else if (fp == StandardNames.XSL_EXCLUDE_RESULT_PREFIXES) {
// already dealt with
} else if (fp == StandardNames.XSL_VERSION) {
// already dealt with
} else if (fp == StandardNames.XSL_XPATH_DEFAULT_NAMESPACE) {
// already dealt with
} else if (fp == StandardNames.XSL_TYPE) {
// deal with this later
} else if (fp == StandardNames.XSL_USE_WHEN) {
// already dealt with
} else if (fp == StandardNames.XSL_VALIDATION) {
// deal with this later
} else if (fp == StandardNames.XSL_INHERIT_NAMESPACES) {
String inheritAtt = atts.getValue(i);
if (inheritAtt.equals("yes")) {
inheritNamespaces = true;
} else if (inheritAtt.equals("no")) {
inheritNamespaces = false;
} else {
compileError("The xsl:inherit-namespaces attribute has permitted values (yes, no)", "XTSE0020");
}
} else {
compileError("Unknown XSL attribute " + namePool.getDisplayName(anameCode), "XTSE0805");
}
} else {
attributeNames[numberOfAttributes] = anameCode;
Expression exp = makeAttributeValueTemplate(atts.getValue(i));
attributeValues[numberOfAttributes] = exp;
numberOfAttributes++;
}
}
// now shorten the arrays if necessary. This is necessary if there are [xsl:]-prefixed
// attributes that weren't copied into the arrays.
if (numberOfAttributes < attributeNames.length) {
int[] attributeNames2 = new int[numberOfAttributes];
System.arraycopy(attributeNames, 0, attributeNames2, 0, numberOfAttributes);
attributeNames = attributeNames2;
Expression[] attributeValues2 = new Expression[numberOfAttributes];
System.arraycopy(attributeValues, 0, attributeValues2, 0, numberOfAttributes);
attributeValues = attributeValues2;
}
}
}
/**
* Validate that this node is OK
* @param decl
*/
public void validate(Declaration decl) throws XPathException {
toplevel = (getParent() instanceof XSLStylesheet);
resultNameCode = getNameCode();
NamePool namePool = getNamePool();
String elementURI = namePool.getURI(resultNameCode);
if (toplevel) {
// A top-level element can never be a "real" literal result element,
// but this class gets used for unknown elements found at the top level
if (elementURI.isEmpty()) {
compileError("Top level elements must have a non-null namespace URI", "XTSE0130");
}
} else {
// Build the list of output namespace nodes. Note we no longer optimize this list.
// See comments in the 9.1 source code for some history of this decision.
Iterator<NamespaceBinding> inscope = NamespaceIterator.iterateNamespaces(this);
while (inscope.hasNext()) {
namespaceCodes.add(inscope.next());
}
// Spec bug 5857: if there is no other binding for the default namespace, add an undeclaration
String defaultNamespace = getURIForPrefix("", true);
if (defaultNamespace.isEmpty()) {
namespaceCodes.add(NamespaceBinding.DEFAULT_UNDECLARATION);
}
// apply any aliases required to create the list of output namespaces
PrincipalStylesheetModule sheet = getPrincipalStylesheetModule();
if (sheet.hasNamespaceAliases()) {
for (int i=0; i<namespaceCodes.size(); i++) {
// System.err.println("Examining namespace " + namespaceCodes[i]);
String suri = namespaceCodes.get(i).getURI();
NamespaceBinding ncode = sheet.getNamespaceAlias(suri);
if (ncode != null && !ncode.getURI().equals(suri)) {
// apply the namespace alias. Change in 7.3: use the prefix associated
// with the new namespace, not the old prefix.
namespaceCodes.set(i, ncode);
}
}
// determine if there is an alias for the namespace of the element name
NamespaceBinding elementAlias = sheet.getNamespaceAlias(elementURI);
if (elementAlias != null && !elementAlias.getURI().equals(elementURI)) {
resultNameCode = namePool.allocate(elementAlias.getPrefix(),
elementAlias.getURI(),
getLocalPart());
}
}
// deal with special attributes
String useAttSets = Navigator.getAttributeValue(this, NamespaceConstant.XSLT, "use-attribute-sets");
if (useAttSets != null) {
attributeSets = getAttributeSets(useAttSets, null);
}
String type = Navigator.getAttributeValue(this, NamespaceConstant.XSLT, "type");
if (type != null) {
compileError("The xsl:type attribute is available only with a schema-aware XSLT processor", "XTSE1660");
}
String validate = Navigator.getAttributeValue(this, NamespaceConstant.XSLT, "validation");
if (validate != null && Validation.getCode(validate)!= Validation.STRIP) {
compileError("To perform validation, a schema-aware XSLT processor is needed", "XTSE1660");
}
// establish the names to be used for all the output attributes;
// also type-check the AVT expressions
if (numberOfAttributes > 0) {
for (int i=0; i<numberOfAttributes; i++) {
int anameCode = attributeNames[i];
int alias = anameCode;
String attURIcode = namePool.getURI(anameCode);
if (!attURIcode.isEmpty()) { // attribute has a namespace prefix
NamespaceBinding newNSCode = sheet.getNamespaceAlias(attURIcode);
if ((newNSCode != null && !newNSCode.getURI().equals(attURIcode))) {
alias = namePool.allocate( newNSCode.getPrefix(),
newNSCode.getURI(),
getAttributeList().getLocalName(i));
}
}
attributeNames[i] = alias;
attributeValues[i] = typeCheck(attributeValues[i]);
}
}
// remove any namespaces that are on the exclude-result-prefixes list.
// The namespace is excluded even if it is the namespace of the element or an attribute,
// though in that case namespace fixup will reinstate it.
for (int n=namespaceCodes.size()-1; n>=0; n--) {
String uri = namespaceCodes.get(n).getURI();
if (isExcludedNamespace(uri) && !sheet.isAliasResultNamespace(uri)) {
namespaceCodes.remove(n);
}
}
}
}
/**
* Validate the children of this node, recursively. Overridden for top-level
* data elements.
* @param decl
*/
protected void validateChildren(Declaration decl) throws XPathException {
if (!toplevel) {
super.validateChildren(decl);
}
}
/**
* Compile code to process the literal result element at runtime
*/
public Expression compile(Executable exec, Declaration decl) throws XPathException {
// top level elements in the stylesheet are ignored
if (toplevel) return null;
NamespaceBinding[] bindings = namespaceCodes.toArray(new NamespaceBinding[namespaceCodes.size()]);
FixedElement inst = new FixedElement(resultNameCode, bindings, inheritNamespaces);
inst.setBaseURI(getBaseURI());
Expression content = compileSequenceConstructor(exec, decl, iterateAxis(Axis.CHILD));
if (numberOfAttributes > 0) {
for (int i=attributeNames.length - 1; i>=0; i--) {
FixedAttribute att = new FixedAttribute(attributeNames[i]);
try {
att.setSelect(attributeValues[i], exec.getConfiguration());
} catch (XPathException err) {
compileError(err);
}
att.setSourceLocator(this);
Expression exp = att;
if (LogConfiguration.loggingIsEnabled() && LogController.traceIsEnabled()) {
TraceExpression trace = new TraceExpression(exp);
trace.setNamespaceResolver(this);
trace.setConstructType(Location.LITERAL_RESULT_ATTRIBUTE);
trace.setSourceLocator(this);
trace.setObjectName(this.getNamePool().getStructuredQName(attributeNames[i]));
exp = trace;
}
if (content == null) {
content = exp;
} else {
content = Block.makeBlock(exp, content);
content.setSourceLocator(this);
}
}
}
if (attributeSets != null) {
UseAttributeSets use = new UseAttributeSets(attributeSets);
if (content == null) {
content = use;
} else {
content = Block.makeBlock(use, content);
content.setSourceLocator(this);
}
}
if (content == null) {
content = Literal.makeEmptySequence();
}
inst.setContentExpression(content);
return inst;
}
/**
* Make a top-level literal result element into a stylesheet. This implements
* the "Simplified Stylesheet" facility.
* @param pss the PreparedStylesheet (the compiled stylesheet as provided)
* @return the reconstructed stylesheet with an xsl:stylesheet and xsl:template element added
*/
public DocumentImpl makeStylesheet(PreparedStylesheet pss)
throws XPathException {
// the implementation grafts the LRE node onto a containing xsl:template and
// xsl:stylesheet
StyleNodeFactory nodeFactory = pss.getStyleNodeFactory();
NamePool pool = getNamePool();
String xslPrefix = getPrefixForURI(NamespaceConstant.XSLT);
if (xslPrefix==null) {
String message;
if (getLocalPart().equals("stylesheet") || getLocalPart().equals("transform")) {
if (getPrefixForURI(NamespaceConstant.MICROSOFT_XSL) != null) {
message = "Saxon is not able to process Microsoft's WD-xsl dialect";
} else {
message = "Namespace for stylesheet element should be " + NamespaceConstant.XSLT;
}
} else {
message = "The supplied file does not appear to be a stylesheet";
}
XPathException err = new XPathException(message);
err.setLocator(this);
err.setErrorCode("XTSE0150");
err.setIsStaticError(true);
pss.reportError(err);
throw err;
}
// check there is an xsl:version attribute (it's mandatory), and copy
// it to the new xsl:stylesheet element
String version = Navigator.getAttributeValue(this, NamespaceConstant.XSLT, "version");
if (version==null) {
XPathException err = new XPathException("Simplified stylesheet: xsl:version attribute is missing");
err.setErrorCode("XTSE0150");
err.setIsStaticError(true);
err.setLocator(this);
pss.reportError(err);
throw err;
}
try {
DocumentImpl oldRoot = (DocumentImpl)getDocumentRoot();
LinkedTreeBuilder builder = new LinkedTreeBuilder();
builder.setPipelineConfiguration(pss.getConfiguration().makePipelineConfiguration());
builder.setNodeFactory(nodeFactory);
builder.setSystemId(this.getSystemId());
builder.open();
builder.startDocument();
int st = StandardNames.XSL_STYLESHEET;
builder.startElement(st, 0);
builder.namespace(new NamespaceBinding("xsl", NamespaceConstant.XSLT), 0);
builder.attribute(pool.allocate("", "", "version"), version);
builder.startContent();
int te = StandardNames.XSL_TEMPLATE;
builder.startElement(te, 0);
builder.attribute(pool.allocate("", "", "match"), "/");
builder.startContent();
builder.graftElement(this);
builder.endElement();
builder.endElement();
builder.endDocument();
builder.close();
DocumentImpl newRoot = (DocumentImpl)builder.getCurrentRoot();
newRoot.graftLocationMap(oldRoot);
return newRoot;
} catch (XPathException err) {
//TransformerConfigurationException e = new TransformerConfigurationException(err);
err.setLocator(this);
throw err;
}
}
}
// 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.