blob: 62130790ab2aa5f7d6138f15ac4bee5f18f14b6f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008 Standards for Technology in Automotive Retail
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* David Carver - STAR - bug 224197 - initial API and implementation
* based on work from Apache Xalan 2.7.0
*******************************************************************************/
/*
* Copyright 1999-2004 The Apache Software Foundation.
*
* Licensed 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: XSLTElementDef.java,v 1.2 2008/03/28 02:38:16 dacarver Exp $
*/
package org.eclipse.wst.xsl.core.internal.compiler.xslt10.processor;
import java.util.Enumeration;
import java.util.Hashtable;
import org.apache.xalan.templates.Constants;
import org.apache.xml.utils.QName;
/**
* 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().
*/
public 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.
*/
public 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(org.apache.xml.utils.Constants.S_XSLNAMESPACEURL)
|| namespace
.equals(org.apache.xml.utils.Constants.S_BUILTIN_EXTENSIONS_URL) || namespace
.equals(org.apache.xml.utils.Constants.S_BUILTIN_OLD_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.
*/
public 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(org.apache.xml.utils.Constants.S_XSLNAMESPACEURL)
|| namespace
.equals(org.apache.xml.utils.Constants.S_BUILTIN_EXTENSIONS_URL) || namespace
.equals(org.apache.xml.utils.Constants.S_BUILTIN_OLD_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.
*/
public 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
*/
public 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
*/
public 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
*/
public 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
*/
public 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.
*/
public 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.
*/
public 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 */
public 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.
*/
public int getType() {
return m_type;
}
/**
* Set the type of this element.
*
* @param t
* Content type, one of T_ELEMENT, T_PCDATA, or T_ANY.
*/
public 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.
*/
public 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.
*/
public 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.
*/
public 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.
* @xsl.usage internal
*/
public XSLTElementDef[] getElements() {
return m_elements;
}
/**
* Set the allowed elements for this type.
*
* @param defs
* An array of allowed child element defs, or null.
*/
public 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.
*/
public 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,
org.apache.xml.utils.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
*/
public 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.
*/
public 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.
*/
public 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,
org.apache.xml.utils.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.
* @xsl.usage internal
*/
public XSLTElementProcessor getElementProcessor() {
return m_elementProcessor;
}
/**
* Set the XSLTElementProcessor for this element.
*
* @param handler
* The element processor for this element.
* @xsl.usage internal
*/
public 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.
*/
public 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.
*/
public 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.
*/
public boolean getRequired() {
return m_required;
}
private Hashtable m_requiredFound;
/**
* Set this required element found.
*
*/
public 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.
*/
public 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.
*/
public 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.
*/
public 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.
*/
public 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.
*/
public 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.
*/
public 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
*/
public boolean getMultiAllowed() {
return m_multiAllowed;
}
}