blob: ecf59ad15184c588027b7a29c0a4c18eb4d74a8c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* $Id$
*/
package org.apache.xalan.xsltc.compiler;
import org.apache.bcel.generic.ALOAD;
import org.apache.bcel.generic.ASTORE;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.GETSTATIC;
import org.apache.bcel.generic.INVOKESTATIC;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.LocalVariableGen;
import org.apache.bcel.generic.PUSH;
import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
import org.apache.xalan.xsltc.compiler.util.ErrorMsg;
import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
import org.apache.xalan.xsltc.compiler.util.Type;
import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
import org.apache.xalan.xsltc.compiler.util.Util;
import org.apache.xml.utils.XML11Char;
/**
* @author Jacek Ambroziak
* @author Santiago Pericas-Geertsen
* @author Morten Jorgensen
*/
final class XslElement extends Instruction {
private String _prefix;
private boolean _ignore = false;
private boolean _isLiteralName = true;
private AttributeValueTemplate _name;
private AttributeValueTemplate _namespace;
/**
* Displays the contents of the element
*/
public void display(int indent) {
indent(indent);
Util.println("Element " + _name);
displayContents(indent + IndentIncrement);
}
/**
* This method is now deprecated. The new implemation of this class
* never declares the default NS.
*/
public boolean declaresDefaultNS() {
return false;
}
public void parseContents(Parser parser) {
final SymbolTable stable = parser.getSymbolTable();
// Handle the 'name' attribute
String name = getAttribute("name");
if (name == EMPTYSTRING) {
ErrorMsg msg = new ErrorMsg(ErrorMsg.ILLEGAL_ELEM_NAME_ERR,
name, this);
parser.reportError(WARNING, msg);
parseChildren(parser);
_ignore = true; // Ignore the element if the QName is invalid
return;
}
// Get namespace attribute
String namespace = getAttribute("namespace");
// Optimize compilation when name is known at compile time
_isLiteralName = Util.isLiteral(name);
if (_isLiteralName) {
if (!XML11Char.isXML11ValidQName(name)) {
ErrorMsg msg = new ErrorMsg(ErrorMsg.ILLEGAL_ELEM_NAME_ERR,
name, this);
parser.reportError(WARNING, msg);
parseChildren(parser);
_ignore = true; // Ignore the element if the QName is invalid
return;
}
final QName qname = parser.getQNameSafe(name);
String prefix = qname.getPrefix();
String local = qname.getLocalPart();
if (prefix == null) {
prefix = EMPTYSTRING;
}
if (!hasAttribute("namespace")) {
namespace = lookupNamespace(prefix);
if (namespace == null) {
ErrorMsg err = new ErrorMsg(ErrorMsg.NAMESPACE_UNDEF_ERR,
prefix, this);
parser.reportError(WARNING, err);
parseChildren(parser);
_ignore = true; // Ignore the element if prefix is undeclared
return;
}
_prefix = prefix;
_namespace = new AttributeValueTemplate(namespace, parser, this);
}
else {
if (prefix == EMPTYSTRING) {
if (Util.isLiteral(namespace)) {
prefix = lookupPrefix(namespace);
if (prefix == null) {
prefix = stable.generateNamespacePrefix();
}
}
// Prepend prefix to local name
final StringBuffer newName = new StringBuffer(prefix);
if (prefix != EMPTYSTRING) {
newName.append(':');
}
name = newName.append(local).toString();
}
_prefix = prefix;
_namespace = new AttributeValueTemplate(namespace, parser, this);
}
}
else {
// name attribute contains variable parts. If there is no namespace
// attribute, the generated code needs to be prepared to look up
// any prefix in the stylesheet at run-time.
_namespace = (namespace == EMPTYSTRING) ? null :
new AttributeValueTemplate(namespace, parser, this);
}
_name = new AttributeValueTemplate(name, parser, this);
final String useSets = getAttribute("use-attribute-sets");
if (useSets.length() > 0) {
if (!Util.isValidQNames(useSets)) {
ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, useSets, this);
parser.reportError(Constants.ERROR, err);
}
setFirstElement(new UseAttributeSets(useSets, parser));
}
parseChildren(parser);
}
/**
* Run type check on element name & contents
*/
public Type typeCheck(SymbolTable stable) throws TypeCheckError {
if (!_ignore) {
_name.typeCheck(stable);
if (_namespace != null) {
_namespace.typeCheck(stable);
}
}
typeCheckContents(stable);
return Type.Void;
}
/**
* This method is called when the name of the element is known at compile time.
* In this case, there is no need to inspect the element name at runtime to
* determine if a prefix exists, needs to be generated, etc.
*/
public void translateLiteral(ClassGenerator classGen, MethodGenerator methodGen) {
final ConstantPoolGen cpg = classGen.getConstantPool();
final InstructionList il = methodGen.getInstructionList();
if (!_ignore) {
il.append(methodGen.loadHandler());
_name.translate(classGen, methodGen);
il.append(DUP2);
il.append(methodGen.startElement());
if (_namespace != null) {
il.append(methodGen.loadHandler());
il.append(new PUSH(cpg, _prefix));
_namespace.translate(classGen,methodGen);
il.append(methodGen.namespace());
}
}
translateContents(classGen, methodGen);
if (!_ignore) {
il.append(methodGen.endElement());
}
}
/**
* At runtime the compilation of xsl:element results in code that: (i)
* evaluates the avt for the name, (ii) checks for a prefix in the name
* (iii) generates a new prefix and create a new qname when necessary
* (iv) calls startElement() on the handler (v) looks up a uri in the XML
* when the prefix is not known at compile time (vi) calls namespace()
* on the handler (vii) evaluates the contents (viii) calls endElement().
*/
public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
LocalVariableGen local = null;
final ConstantPoolGen cpg = classGen.getConstantPool();
final InstructionList il = methodGen.getInstructionList();
// Optimize translation if element name is a literal
if (_isLiteralName) {
translateLiteral(classGen, methodGen);
return;
}
if (!_ignore) {
// if the qname is an AVT, then the qname has to be checked at runtime if it is a valid qname
LocalVariableGen nameValue =
methodGen.addLocalVariable2("nameValue",
Util.getJCRefType(STRING_SIG),
null);
// store the name into a variable first so _name.translate only needs to be called once
_name.translate(classGen, methodGen);
nameValue.setStart(il.append(new ASTORE(nameValue.getIndex())));
il.append(new ALOAD(nameValue.getIndex()));
// call checkQName if the name is an AVT
final int check = cpg.addMethodref(BASIS_LIBRARY_CLASS, "checkQName",
"("
+STRING_SIG
+")V");
il.append(new INVOKESTATIC(check));
// Push handler for call to endElement()
il.append(methodGen.loadHandler());
// load name value again
nameValue.setEnd(il.append(new ALOAD(nameValue.getIndex())));
if (_namespace != null) {
_namespace.translate(classGen, methodGen);
}
else {
// If name is an AVT and namespace is not specified, need to
// look up any prefix in the stylesheet by calling
// BasisLibrary.lookupStylesheetQNameNamespace(
// name, stylesheetNode, ancestorsArray,
// prefixURIsIndexArray, prefixURIPairsArray,
// !ignoreDefaultNamespace)
String transletClassName = getXSLTC().getClassName();
il.append(DUP);
il.append(new PUSH(cpg, getNodeIDForStylesheetNSLookup()));
il.append(new GETSTATIC(cpg.addFieldref(
transletClassName,
STATIC_NS_ANCESTORS_ARRAY_FIELD,
NS_ANCESTORS_INDEX_SIG)));
il.append(new GETSTATIC(cpg.addFieldref(
transletClassName,
STATIC_PREFIX_URIS_IDX_ARRAY_FIELD,
PREFIX_URIS_IDX_SIG)));
il.append(new GETSTATIC(cpg.addFieldref(
transletClassName,
STATIC_PREFIX_URIS_ARRAY_FIELD,
PREFIX_URIS_ARRAY_SIG)));
// Default namespace is significant
il.append(ICONST_0);
il.append(
new INVOKESTATIC(
cpg.addMethodref(BASIS_LIBRARY_CLASS,
LOOKUP_STYLESHEET_QNAME_NS_REF,
LOOKUP_STYLESHEET_QNAME_NS_SIG)));
}
// Push additional arguments
il.append(methodGen.loadHandler());
il.append(methodGen.loadDOM());
il.append(methodGen.loadCurrentNode());
// Invoke BasisLibrary.startXslElemCheckQName()
il.append(new INVOKESTATIC(
cpg.addMethodref(BASIS_LIBRARY_CLASS, "startXslElement",
"(" + STRING_SIG
+ STRING_SIG
+ TRANSLET_OUTPUT_SIG
+ DOM_INTF_SIG + "I)" + STRING_SIG)));
}
translateContents(classGen, methodGen);
if (!_ignore) {
il.append(methodGen.endElement());
}
}
/**
* Override this method to make sure that xsl:attributes are not
* copied to output if this xsl:element is to be ignored
*/
public void translateContents(ClassGenerator classGen,
MethodGenerator methodGen) {
final int n = elementCount();
for (int i = 0; i < n; i++) {
final SyntaxTreeNode item =
(SyntaxTreeNode)getContents().elementAt(i);
if (_ignore && item instanceof XslAttribute) continue;
item.translate(classGen, methodGen);
}
}
}