blob: 0ad573142fb604f2c644540a7dda1f7420a930d6 [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 1999 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) 1999, Lotus
* Development Corporation., http://www.lotus.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.xalan.processor;
import org.xml.sax.ContentHandler;
import org.apache.xalan.templates.Constants;
import org.apache.xml.utils.QName;
import org.apache.xalan.res.XSLMessages;
import org.apache.xalan.res.XSLTErrorResources;
import java.util.Hashtable;
import java.util.Enumeration;
/**
* This class defines the allowed structure for an element in a XSLT stylesheet,
* is meant to reflect the structure defined in http://www.w3.org/TR/xslt#dtd, and the
* mapping between Xalan classes and the markup elements in the XSLT instance.
* This actually represents both text nodes and elements.
*/
public class XSLTElementDef
{
/**
* Construct an instance of XSLTElementDef. This must be followed by a
* call to build().
*/
XSLTElementDef(){}
/**
* Construct an instance of XSLTElementDef.
*
* @param namespace The Namespace URI, "*", or null.
* @param name The local name (without prefix), "*", or null.
* @param nameAlias A potential alias for the name, or null.
* @param elements An array of allowed child element defs, or null.
* @param attributes An array of allowed attribute defs, or null.
* @param contentHandler The element processor for this element.
* @param classObject The class of the object that this element def should produce.
*/
XSLTElementDef(XSLTSchema schema, String namespace, String name, String nameAlias,
XSLTElementDef[] elements, XSLTAttributeDef[] attributes,
XSLTElementProcessor contentHandler, Class classObject)
{
build(namespace, name, nameAlias, elements, attributes, contentHandler,
classObject);
if ( (null != namespace)
&& (namespace.equals(Constants.S_XSLNAMESPACEURL)
|| namespace.equals(Constants.S_BUILTIN_EXTENSIONS_URL)) )
{
schema.addAvailableElement(new QName(namespace, name));
if(null != nameAlias)
schema.addAvailableElement(new QName(namespace, nameAlias));
}
}
/**
* Construct an instance of XSLTElementDef.
*
* @param namespace The Namespace URI, "*", or null.
* @param name The local name (without prefix), "*", or null.
* @param nameAlias A potential alias for the name, or null.
* @param elements An array of allowed child element defs, or null.
* @param attributes An array of allowed attribute defs, or null.
* @param contentHandler The element processor for this element.
* @param classObject The class of the object that this element def should produce.
* @param has_required true if this element has required elements by the XSLT specification.
*/
XSLTElementDef(XSLTSchema schema, String namespace, String name, String nameAlias,
XSLTElementDef[] elements, XSLTAttributeDef[] attributes,
XSLTElementProcessor contentHandler, Class classObject, boolean has_required)
{
this.m_has_required = has_required;
build(namespace, name, nameAlias, elements, attributes, contentHandler,
classObject);
if ( (null != namespace)
&& (namespace.equals(Constants.S_XSLNAMESPACEURL)
|| namespace.equals(Constants.S_BUILTIN_EXTENSIONS_URL)) )
{
schema.addAvailableElement(new QName(namespace, name));
if(null != nameAlias)
schema.addAvailableElement(new QName(namespace, nameAlias));
}
}
/**
* Construct an instance of XSLTElementDef.
*
* @param namespace The Namespace URI, "*", or null.
* @param name The local name (without prefix), "*", or null.
* @param nameAlias A potential alias for the name, or null.
* @param elements An array of allowed child element defs, or null.
* @param attributes An array of allowed attribute defs, or null.
* @param contentHandler The element processor for this element.
* @param classObject The class of the object that this element def should produce.
* @param has_required true if this element has required elements by the XSLT specification.
* @param required true if this element is required by the XSLT specification.
*/
XSLTElementDef(XSLTSchema schema, String namespace, String name, String nameAlias,
XSLTElementDef[] elements, XSLTAttributeDef[] attributes,
XSLTElementProcessor contentHandler, Class classObject,
boolean has_required, boolean required)
{
this(schema, namespace, name, nameAlias,
elements, attributes,
contentHandler, classObject, has_required);
this.m_required = required;
}
/**
* Construct an instance of XSLTElementDef.
*
* @param namespace The Namespace URI, "*", or null.
* @param name The local name (without prefix), "*", or null.
* @param nameAlias A potential alias for the name, or null.
* @param elements An array of allowed child element defs, or null.
* @param attributes An array of allowed attribute defs, or null.
* @param contentHandler The element processor for this element.
* @param classObject The class of the object that this element def should produce.
* @param has_required true if this element has required elements by the XSLT specification.
* @param required true if this element is required by the XSLT specification.
* @param order the order this element should appear according to the XSLT specification.
* @param multiAllowed whether this element is allowed more than once
*/
XSLTElementDef(XSLTSchema schema, String namespace, String name, String nameAlias,
XSLTElementDef[] elements, XSLTAttributeDef[] attributes,
XSLTElementProcessor contentHandler, Class classObject,
boolean has_required, boolean required, int order,
boolean multiAllowed)
{
this(schema, namespace, name, nameAlias,
elements, attributes,
contentHandler, classObject, has_required, required);
this.m_order = order;
this.m_multiAllowed = multiAllowed;
}
/**
* Construct an instance of XSLTElementDef.
*
* @param namespace The Namespace URI, "*", or null.
* @param name The local name (without prefix), "*", or null.
* @param nameAlias A potential alias for the name, or null.
* @param elements An array of allowed child element defs, or null.
* @param attributes An array of allowed attribute defs, or null.
* @param contentHandler The element processor for this element.
* @param classObject The class of the object that this element def should produce.
* @param has_required true if this element has required elements by the XSLT specification.
* @param required true if this element is required by the XSLT specification.
* @param has_order whether this element has ordered child elements
* @param order the order this element should appear according to the XSLT specification.
* @param multiAllowed whether this element is allowed more than once
*/
XSLTElementDef(XSLTSchema schema, String namespace, String name, String nameAlias,
XSLTElementDef[] elements, XSLTAttributeDef[] attributes,
XSLTElementProcessor contentHandler, Class classObject,
boolean has_required, boolean required, boolean has_order, int order,
boolean multiAllowed)
{
this(schema, namespace, name, nameAlias,
elements, attributes,
contentHandler, classObject, has_required, required);
this.m_order = order;
this.m_multiAllowed = multiAllowed;
this.m_isOrdered = has_order;
}
/**
* Construct an instance of XSLTElementDef.
*
* @param namespace The Namespace URI, "*", or null.
* @param name The local name (without prefix), "*", or null.
* @param nameAlias A potential alias for the name, or null.
* @param elements An array of allowed child element defs, or null.
* @param attributes An array of allowed attribute defs, or null.
* @param contentHandler The element processor for this element.
* @param classObject The class of the object that this element def should produce.
* @param has_order whether this element has ordered child elements
* @param order the order this element should appear according to the XSLT specification.
* @param multiAllowed whether this element is allowed more than once
*/
XSLTElementDef(XSLTSchema schema, String namespace, String name, String nameAlias,
XSLTElementDef[] elements, XSLTAttributeDef[] attributes,
XSLTElementProcessor contentHandler, Class classObject,
boolean has_order, int order, boolean multiAllowed)
{
this(schema, namespace, name, nameAlias,
elements, attributes,
contentHandler, classObject,
order, multiAllowed);
this.m_isOrdered = has_order;
}
/**
* Construct an instance of XSLTElementDef.
*
* @param namespace The Namespace URI, "*", or null.
* @param name The local name (without prefix), "*", or null.
* @param nameAlias A potential alias for the name, or null.
* @param elements An array of allowed child element defs, or null.
* @param attributes An array of allowed attribute defs, or null.
* @param contentHandler The element processor for this element.
* @param classObject The class of the object that this element def should produce.
* @param order the order this element should appear according to the XSLT specification.
* @param multiAllowed whether this element is allowed more than once
*/
XSLTElementDef(XSLTSchema schema, String namespace, String name, String nameAlias,
XSLTElementDef[] elements, XSLTAttributeDef[] attributes,
XSLTElementProcessor contentHandler, Class classObject,
int order, boolean multiAllowed)
{
this(schema, namespace, name, nameAlias, elements, attributes, contentHandler,
classObject);
this.m_order = order;
this.m_multiAllowed = multiAllowed;
}
/**
* Construct an instance of XSLTElementDef that represents text.
*
* @param classObject The class of the object that this element def should produce.
* @param contentHandler The element processor for this element.
* @param type Content type, one of T_ELEMENT, T_PCDATA, or T_ANY.
*/
XSLTElementDef(Class classObject, XSLTElementProcessor contentHandler,
int type)
{
this.m_classObject = classObject;
this.m_type = type;
setElementProcessor(contentHandler);
}
/**
* Construct an instance of XSLTElementDef.
*
* @param namespace The Namespace URI, "*", or null.
* @param name The local name (without prefix), "*", or null.
* @param nameAlias A potential alias for the name, or null.
* @param elements An array of allowed child element defs, or null.
* @param attributes An array of allowed attribute defs, or null.
* @param contentHandler The element processor for this element.
* @param classObject The class of the object that this element def should produce.
*/
void build(String namespace, String name, String nameAlias,
XSLTElementDef[] elements, XSLTAttributeDef[] attributes,
XSLTElementProcessor contentHandler, Class classObject)
{
this.m_namespace = namespace;
this.m_name = name;
this.m_nameAlias = nameAlias;
this.m_elements = elements;
this.m_attributes = attributes;
setElementProcessor(contentHandler);
this.m_classObject = classObject;
if (hasRequired() && m_elements != null)
{
int n = m_elements.length;
for (int i = 0; i < n; i++)
{
XSLTElementDef def = m_elements[i];
if (def != null && def.getRequired())
{
if (m_requiredFound == null)
m_requiredFound = new Hashtable();
m_requiredFound.put(def.getName(), "xsl:" +def.getName());
}
}
}
}
/**
* Tell if two objects are equal, when either one may be null.
* If both are null, they are considered equal.
*
* @param obj1 A reference to the first object, or null.
* @param obj2 A reference to the second object, or null.
*
* @return true if the to objects are equal by both being null or
* because obj2.equals(obj1) returns true.
*/
private static boolean equalsMayBeNull(Object obj1, Object obj2)
{
return (obj2 == obj1)
|| ((null != obj1) && (null != obj2) && obj2.equals(obj1));
}
/**
* Tell if the two string refs are equal,
* equality being defined as:
* 1) Both strings are null.
* 2) One string is null and the other is empty.
* 3) Both strings are non-null, and equal.
*
* @param s1 A reference to the first string, or null.
* @param s2 A reference to the second string, or null.
*
* @return true if Both strings are null, or if
* one string is null and the other is empty, or if
* both strings are non-null, and equal because
* s1.equals(s2) returns true.
*/
private static boolean equalsMayBeNullOrZeroLen(String s1, String s2)
{
int len1 = (s1 == null) ? 0 : s1.length();
int len2 = (s2 == null) ? 0 : s2.length();
return (len1 != len2) ? false
: (len1 == 0) ? true
: s1.equals(s2);
}
/** Content type enumerations */
static final int T_ELEMENT = 1, T_PCDATA = 2, T_ANY = 3;
/**
* The type of this element.
*/
private int m_type = T_ELEMENT;
/**
* Get the type of this element.
*
* @return Content type, one of T_ELEMENT, T_PCDATA, or T_ANY.
*/
int getType()
{
return m_type;
}
/**
* Set the type of this element.
*
* @param t Content type, one of T_ELEMENT, T_PCDATA, or T_ANY.
*/
void setType(int t)
{
m_type = t;
}
/**
* The allowed namespace for this element.
*/
private String m_namespace;
/**
* Get the allowed namespace for this element.
*
* @return The Namespace URI, "*", or null.
*/
String getNamespace()
{
return m_namespace;
}
/**
* The name of this element.
*/
private String m_name;
/**
* Get the local name of this element.
*
* @return The local name of this element, "*", or null.
*/
String getName()
{
return m_name;
}
/**
* The name of this element.
*/
private String m_nameAlias;
/**
* Get the name of this element.
*
* @return A potential alias for the name, or null.
*/
String getNameAlias()
{
return m_nameAlias;
}
/**
* The allowed elements for this type.
*/
private XSLTElementDef[] m_elements;
/**
* Get the allowed elements for this type.
*
* @return An array of allowed child element defs, or null.
*/
XSLTElementDef[] getElements()
{
return m_elements;
}
/**
* Set the allowed elements for this type.
*
* @param defs An array of allowed child element defs, or null.
*/
void setElements(XSLTElementDef[] defs)
{
m_elements = defs;
}
/**
* Tell if the namespace URI and local name match this
* element.
* @param uri The namespace uri, which may be null.
* @param localName The local name of an element, which may be null.
*
* @return true if the uri and local name arguments are considered
* to match the uri and local name of this element def.
*/
private boolean QNameEquals(String uri, String localName)
{
return (equalsMayBeNullOrZeroLen(m_namespace, uri)
&& (equalsMayBeNullOrZeroLen(m_name, localName)
|| equalsMayBeNullOrZeroLen(m_nameAlias, localName)));
}
/**
* Given a namespace URI, and a local name, get the processor
* for the element, or return null if not allowed.
*
* @param uri The Namespace URI, or an empty string.
* @param localName The local name (without prefix), or empty string if not namespace processing.
*
* @return The element processor that matches the arguments, or null.
*/
XSLTElementProcessor getProcessorFor(String uri, String localName)
{
XSLTElementProcessor elemDef = null; // return value
if (null == m_elements)
return null;
int n = m_elements.length;
int order = -1;
boolean multiAllowed = true;
for (int i = 0; i < n; i++)
{
XSLTElementDef def = m_elements[i];
// A "*" signals that the element allows literal result
// elements, so just assign the def, and continue to
// see if anything else matches.
if (def.m_name.equals("*"))
{
// Don't allow xsl elements
if (!equalsMayBeNullOrZeroLen(uri, Constants.S_XSLNAMESPACEURL))
{
elemDef = def.m_elementProcessor;
order = def.getOrder();
multiAllowed = def.getMultiAllowed();
}
}
else if (def.QNameEquals(uri, localName))
{
if (def.getRequired())
this.setRequiredFound(def.getName(), true);
order = def.getOrder();
multiAllowed = def.getMultiAllowed();
elemDef = def.m_elementProcessor;
break;
}
}
if (elemDef != null && this.isOrdered())
{
int lastOrder = getLastOrder();
if (order > lastOrder)
setLastOrder(order);
else if (order == lastOrder && !multiAllowed)
{
return null;
}
else if (order < lastOrder && order > 0)
{
return null;
}
}
return elemDef;
}
/**
* Given an unknown element, get the processor
* for the element.
*
* @param uri The Namespace URI, or an empty string.
* @param localName The local name (without prefix), or empty string if not namespace processing.
*
* @return normally a {@link ProcessorUnknown} reference.
* @see ProcessorUnknown
*/
XSLTElementProcessor getProcessorForUnknown(String uri, String localName)
{
// XSLTElementProcessor lreDef = null; // return value
if (null == m_elements)
return null;
int n = m_elements.length;
for (int i = 0; i < n; i++)
{
XSLTElementDef def = m_elements[i];
if (def.m_name.equals("unknown") && uri.length() > 0)
{
return def.m_elementProcessor;
}
}
return null;
}
/**
* The allowed attributes for this type.
*/
private XSLTAttributeDef[] m_attributes;
/**
* Get the allowed attributes for this type.
*
* @return An array of allowed attribute defs, or null.
*/
XSLTAttributeDef[] getAttributes()
{
return m_attributes;
}
/**
* Given a namespace URI, and a local name, return the element's
* attribute definition, if it has one.
*
* @param uri The Namespace URI, or an empty string.
* @param localName The local name (without prefix), or empty string if not namespace processing.
*
* @return The attribute def that matches the arguments, or null.
*/
XSLTAttributeDef getAttributeDef(String uri, String localName)
{
XSLTAttributeDef defaultDef = null;
XSLTAttributeDef[] attrDefs = getAttributes();
int nAttrDefs = attrDefs.length;
for (int k = 0; k < nAttrDefs; k++)
{
XSLTAttributeDef attrDef = attrDefs[k];
String uriDef = attrDef.getNamespace();
String nameDef = attrDef.getName();
if (nameDef.equals("*") && (equalsMayBeNullOrZeroLen(uri, uriDef) ||
(uriDef != null && uriDef.equals("*") && uri!=null && uri.length() > 0 )))
{
return attrDef;
}
else if (nameDef.equals("*") && (uriDef == null))
{
// In this case, all attributes are legal, so return
// this as the last resort.
defaultDef = attrDef;
}
else if (equalsMayBeNullOrZeroLen(uri, uriDef)
&& localName.equals(nameDef))
{
return attrDef;
}
}
if (null == defaultDef)
{
if (uri.length() > 0 && !equalsMayBeNullOrZeroLen(uri, Constants.S_XSLNAMESPACEURL))
{
return XSLTAttributeDef.m_foreignAttr;
}
}
return defaultDef;
}
/**
* If non-null, the ContentHandler/TransformerFactory for this element.
*/
private XSLTElementProcessor m_elementProcessor;
/**
* Return the XSLTElementProcessor for this element.
*
* @return The element processor for this element.
*/
XSLTElementProcessor getElementProcessor()
{
return m_elementProcessor;
}
/**
* Set the XSLTElementProcessor for this element.
*
* @param handler The element processor for this element.
*/
void setElementProcessor(XSLTElementProcessor handler)
{
if (handler != null)
{
m_elementProcessor = handler;
m_elementProcessor.setElemDef(this);
}
}
/**
* If non-null, the class object that should in instantiated for
* a Xalan instance of this element.
*/
private Class m_classObject;
/**
* Return the class object that should in instantiated for
* a Xalan instance of this element.
*
* @return The class of the object that this element def should produce, or null.
*/
Class getClassObject()
{
return m_classObject;
}
/**
* If true, this has a required element.
*/
private boolean m_has_required = false;
/**
* Get whether or not this has a required element.
*
* @return true if this this has a required element.
*/
boolean hasRequired()
{
return m_has_required;
}
/**
* If true, this is a required element.
*/
private boolean m_required = false;
/**
* Get whether or not this is a required element.
*
* @return true if this is a required element.
*/
boolean getRequired()
{
return m_required;
}
Hashtable m_requiredFound;
/**
* Set this required element found.
*
*/
void setRequiredFound(String elem, boolean found)
{
if (m_requiredFound.get(elem) != null)
m_requiredFound.remove(elem);
}
/**
* Get whether all required elements were found.
*
* @return true if all required elements were found.
*/
boolean getRequiredFound()
{
if (m_requiredFound == null)
return true;
return m_requiredFound.isEmpty();
}
/**
* Get required elements that were not found.
*
* @return required elements that were not found.
*/
String getRequiredElem()
{
if (m_requiredFound == null)
return null;
Enumeration elems = m_requiredFound.elements();
String s = "";
boolean first = true;
while (elems.hasMoreElements())
{
if (first)
first = false;
else
s = s + ", ";
s = s + (String)elems.nextElement();
}
return s;
}
boolean m_isOrdered = false;
/**
* Get whether this element requires ordered children.
*
* @return true if this element requires ordered children.
*/
boolean isOrdered()
{
/*if (!m_CheckedOrdered)
{
m_CheckedOrdered = true;
m_isOrdered = false;
if (null == m_elements)
return false;
int n = m_elements.length;
for (int i = 0; i < n; i++)
{
if (m_elements[i].getOrder() > 0)
{
m_isOrdered = true;
return true;
}
}
return false;
}
else*/
return m_isOrdered;
}
/**
* the order that this element should appear, or -1 if not ordered
*/
private int m_order = -1;
/**
* Get the order that this element should appear .
*
* @return the order that this element should appear.
*/
int getOrder()
{
return m_order;
}
/**
* the highest order of child elements have appeared so far,
* or -1 if not ordered
*/
private int m_lastOrder = -1;
/**
* Get the highest order of child elements have appeared so far .
*
* @return the highest order of child elements have appeared so far.
*/
int getLastOrder()
{
return m_lastOrder;
}
/**
* Set the highest order of child elements have appeared so far .
*
* @param order the highest order of child elements have appeared so far.
*/
void setLastOrder(int order)
{
m_lastOrder = order ;
}
/**
* True if this element can appear multiple times
*/
private boolean m_multiAllowed = true;
/**
* Get whether this element can appear multiple times
*
* @return true if this element can appear multiple times
*/
boolean getMultiAllowed()
{
return m_multiAllowed;
}
}