src/xalanc/XSLT/ElemElement.cpp (447 lines of code) (raw):

/* * 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. */ #include "ElemElement.hpp" #include <xercesc/sax/AttributeList.hpp> #include <xalanc/PlatformSupport/XalanMessageLoader.hpp> #include <xalanc/DOMSupport/DOMServices.hpp> #include "AVT.hpp" #include "Constants.hpp" #include "Stylesheet.hpp" #include "StylesheetConstructionContext.hpp" #include "StylesheetExecutionContext.hpp" namespace XALAN_CPP_NAMESPACE { ElemElement::ElemElement( StylesheetConstructionContext& constructionContext, Stylesheet& stylesheetTree, const AttributeListType& atts, XalanFileLoc lineNumber, XalanFileLoc columnNumber) : ElemUse(constructionContext, stylesheetTree, lineNumber, columnNumber, StylesheetConstructionContext::ELEMNAME_ELEMENT), m_nameAVT(0), m_namespaceAVT(0) { // Namespace aliases are not used for xsl:element, so // turn them off... // m_namespacesHandler.setProcessNamespaceAliaises(false); const XalanSize_t nAttrs = atts.getLength(); for (XalanSize_t i = 0; i < nAttrs; i++) { const XalanDOMChar* const aname = atts.getName(i); if(equals(aname, Constants::ATTRNAME_NAME)) { m_nameAVT = constructionContext.createAVT(getLocator(), aname, atts.getValue(i), *this); } else if(equals(aname, Constants::ATTRNAME_NAMESPACE)) { m_namespaceAVT = constructionContext.createAVT(getLocator(), aname, atts.getValue(i), *this); } else if(processUseAttributeSets( constructionContext, aname, atts, i) == false && processSpaceAttr( Constants::ELEMNAME_ELEMENT_WITH_PREFIX_STRING.c_str(), aname, atts, i, constructionContext) == false && isAttrOK( aname, atts, i, constructionContext) == false) { error( constructionContext, XalanMessages::ElementHasIllegalAttribute_2Param, Constants::ELEMNAME_ELEMENT_WITH_PREFIX_STRING.c_str(), aname); } } if(0 == m_nameAVT) { error( constructionContext, XalanMessages::ElementMustHaveAttribute_2Param, Constants::ELEMNAME_ELEMENT_WITH_PREFIX_STRING, Constants::ATTRNAME_NAME); } } ElemElement::~ElemElement() { } const XalanDOMString& ElemElement::getElementName() const { return Constants::ELEMNAME_ELEMENT_WITH_PREFIX_STRING; } typedef const StylesheetExecutionContext::GetCachedString GetCachedString; #if !defined(XALAN_RECURSIVE_STYLESHEET_EXECUTION) const ElemTemplateElement* ElemElement::startElement(StylesheetExecutionContext& executionContext) const { XalanDOMString& elemName = executionContext.getAndPushCachedString(); m_nameAVT->evaluate(elemName, *this, executionContext); bool isIllegalElement = !XalanQName::isValidQName(elemName); if (isIllegalElement == true) { warn( executionContext, XalanMessages::IllegalElementName_1Param, elemName); executionContext.pushSkipElementAttributes(true); } else { const GetCachedString elemNameSpaceGuard(executionContext); XalanDOMString& elemNameSpace = elemNameSpaceGuard.get(); if (m_namespaceAVT != 0) { m_namespaceAVT->evaluate(elemNameSpace, *this, executionContext); } XalanDOMString::size_type namespaceLen = elemNameSpace.length(); XalanDOMString::size_type len = elemName.length(); const XalanDOMString::size_type indexOfNSSep = indexOf(elemName, XalanUnicode::charColon); const bool havePrefix = indexOfNSSep == len ? false : true; const GetCachedString prefixGuard(executionContext); XalanDOMString& prefix = prefixGuard.get(); if (havePrefix == true) { substring(elemName, prefix, 0, indexOfNSSep); const XalanDOMString* const theNamespace = getNamespacesHandler().getNamespace(prefix); if(theNamespace == 0 && namespaceLen == 0) { warn( executionContext, XalanMessages::PrefixIsNotDeclared_1Param, prefix); if (m_namespaceAVT != 0) { elemName.erase(0, indexOfNSSep + 1); } else { isIllegalElement = true; warn( executionContext, XalanMessages::IllegalElementName_1Param, elemName); } } else if (theNamespace != 0 && namespaceLen == 0 && equals(prefix, DOMServices::s_XMLNamespace) == false) { elemNameSpace = *theNamespace; } } if (isIllegalElement == false) { executionContext.startElement(elemName.c_str()); if(0 == m_namespaceAVT && havePrefix == false ) { if (havePrefix == false) { fixupDefaultNamespace(executionContext); } } else { if(havePrefix == false) { if (namespaceLen > 0) { const XalanDOMString* const theDefaultNamespace = executionContext.getResultNamespaceForPrefix(s_emptyString); if (theDefaultNamespace == 0 || equals(*theDefaultNamespace, elemNameSpace) == false) { executionContext.addResultAttribute( DOMServices::s_XMLNamespace, elemNameSpace); } } else { // OK, the namespace we're generating is the default namespace, // so let's make sure that we really need it. If we don't, // we end up with another xmlns="" on the element we're // generating. Although this isn't really an error, it's // a bit unsightly, so let's suppress it... const XalanDOMString& theParentDefaultNamespace = getParentDefaultNamespace(); if (theParentDefaultNamespace.empty() == false || executionContext.getResultNamespaceForPrefix(s_emptyString) != 0) { executionContext.addResultAttribute(DOMServices::s_XMLNamespace, elemNameSpace); } } } else { const XalanDOMString* const theNamespace = executionContext.getResultNamespaceForPrefix(prefix); if (theNamespace == 0 || equals(*theNamespace, elemNameSpace) == false) { prefix.insert(0, DOMServices::s_XMLNamespaceWithSeparator); executionContext.addResultAttribute(prefix, elemNameSpace); } } } } if (isIllegalElement == true) { executionContext.pushSkipElementAttributes(true); } else { ElemUse::startElement(executionContext); executionContext.pushSkipElementAttributes(false); } } return beginExecuteChildren(executionContext); } void ElemElement::endElement(StylesheetExecutionContext& executionContext) const { endExecuteChildren(executionContext); bool ignoreAttributeElements = executionContext.popSkipElementAttributes(); const XalanDOMString& elemName = executionContext.getAndPopCachedString(); if (!ignoreAttributeElements) { executionContext.endElement(elemName.c_str()); ElemUse::endElement(executionContext); } } bool ElemElement::executeChildElement( StylesheetExecutionContext& executionContext, const ElemTemplateElement* element) const { return !(element->getXSLToken() == StylesheetConstructionContext::ELEMNAME_ATTRIBUTE && executionContext.getSkipElementAttributes() == true); } #endif #if defined(XALAN_RECURSIVE_STYLESHEET_EXECUTION) void ElemElement::execute(StylesheetExecutionContext& executionContext) const { const GetCachedString elemNameGuard(executionContext); XalanDOMString& elemName = elemNameGuard.get(); m_nameAVT->evaluate(elemName, *this, executionContext); bool isIllegalElement = !XalanQName::isValidQName(elemName); if (isIllegalElement == true) { warn( executionContext, XalanMessages::IllegalElementName_1Param, elemName); ElemUse::doExecute(executionContext, false); doExecuteChildren(executionContext, true); } else { const GetCachedString elemNameSpaceGuard(executionContext); XalanDOMString& elemNameSpace = elemNameSpaceGuard.get(); if (m_namespaceAVT != 0) { m_namespaceAVT->evaluate(elemNameSpace, *this, executionContext); } XalanDOMString::size_type namespaceLen = length(elemNameSpace); bool foundResultNamespaceForPrefix = false; XalanDOMString::size_type len = length(elemName); const XalanDOMString::size_type indexOfNSSep = indexOf(elemName, XalanUnicode::charColon); const bool havePrefix = indexOfNSSep == len ? false : true; const GetCachedString prefixGuard(executionContext); XalanDOMString& prefix = prefixGuard.get(); if (havePrefix == true) { substring(elemName, prefix, 0, indexOfNSSep); const XalanDOMString* const theNamespace = executionContext.getResultNamespaceForPrefix(prefix); if (theNamespace != 0) { foundResultNamespaceForPrefix = true; } else { const XalanDOMString* const theNamespace = getNamespacesHandler().getNamespace(prefix); if(theNamespace == 0 && namespaceLen == 0) { warn( executionContext, XalanMessages::PrefixIsNotDeclared_1Param, prefix); if (m_namespaceAVT != 0) { elemName.erase(0, indexOfNSSep + 1); } else { isIllegalElement = true; warn( executionContext, XalanMessages::IllegalElementName_1Param, elemName); } } else if (theNamespace != 0 && namespaceLen == 0 && equals(prefix, DOMServices::s_XMLNamespace) == false) { elemNameSpace = *theNamespace; } } } if (isIllegalElement == false) { executionContext.startElement(elemName.c_str()); if(0 == m_namespaceAVT && (havePrefix == false || foundResultNamespaceForPrefix == true)) { if (havePrefix == false) { fixupDefaultNamespace(executionContext); } } else { if(havePrefix == false) { if (namespaceLen > 0) { const XalanDOMString* const theDefaultNamespace = executionContext.getResultNamespaceForPrefix(s_emptyString); if (theDefaultNamespace == 0 || equals(*theDefaultNamespace, elemNameSpace) == false) { executionContext.addResultAttribute( DOMServices::s_XMLNamespace, elemNameSpace); } } else { // OK, the namespace we're generating is the default namespace, // so let's make sure that we really need it. If we don't, // we end up with another xmlns="" on the element we're // generating. Although this isn't really an error, it's // a bit unsightly, so let's suppress it... const XalanDOMString& theParentDefaultNamespace = getParentDefaultNamespace(); if (length(theParentDefaultNamespace) == 0) { if (executionContext.getResultNamespaceForPrefix(s_emptyString) != 0) { executionContext.addResultAttribute(DOMServices::s_XMLNamespace, elemNameSpace); } } else { executionContext.addResultAttribute(DOMServices::s_XMLNamespace, elemNameSpace); } } } else { const XalanDOMString* const theNamespace = executionContext.getResultNamespaceForPrefix(prefix); if (theNamespace == 0 || equals(*theNamespace, elemNameSpace) == false) { insert(prefix, 0, DOMServices::s_XMLNamespaceWithSeparator); executionContext.addResultAttribute(prefix, elemNameSpace); } } } } if (isIllegalElement == true) { ElemUse::doExecute(executionContext, false); doExecuteChildren(executionContext, true); } else { ElemUse::doExecute(executionContext, true); doExecuteChildren(executionContext, false); executionContext.endElement(elemName.c_str()); } } } void ElemElement::doExecuteChildren( StylesheetExecutionContext& executionContext, bool skipAttributeChildren) const { if (skipAttributeChildren == false) { // If we should execute all children, then just call // executeChildren()... executeChildren(executionContext); } else { StylesheetExecutionContext::PushAndPopElementFrame thePushAndPop(executionContext, this); for (ElemTemplateElement* node = getFirstChildElem(); node != 0; node = node->getNextSiblingElem()) { if (node->getXSLToken() != StylesheetConstructionContext::ELEMNAME_ATTRIBUTE) { node->execute(executionContext); } } } } #endif void ElemElement::namespacesPostConstruction( StylesheetConstructionContext& constructionContext, const NamespacesHandler& theParentHandler, NamespacesHandler& theHandler) { theHandler.postConstruction( constructionContext, false, getElementName(), &theParentHandler); } void ElemElement::fixupDefaultNamespace(StylesheetExecutionContext& executionContext) const { // OK, now let's check to make sure we don't have to change the default namespace... const XalanDOMString* const theCurrentDefaultNamespace = executionContext.getResultNamespaceForPrefix(s_emptyString); const XalanDOMString* const theElementDefaultNamespace = getNamespacesHandler().getNamespace(s_emptyString); if (theCurrentDefaultNamespace != 0) { if (theElementDefaultNamespace == 0) { // There was no default namespace, so we have to turn the // current one off. executionContext.addResultAttribute(DOMServices::s_XMLNamespace, s_emptyString); } else if (equals(*theCurrentDefaultNamespace, *theElementDefaultNamespace) == false) { // There is a default namespace, but it's different from the current one, // so we have to change it. executionContext.addResultAttribute(DOMServices::s_XMLNamespace, *theElementDefaultNamespace); } } else if (theElementDefaultNamespace != 0) { executionContext.addResultAttribute(DOMServices::s_XMLNamespace, *theElementDefaultNamespace); } } const XalanDOMString& ElemElement::getParentDefaultNamespace() const { const ElemTemplateElement* const theParent = getParentNodeElem(); if (theParent == 0) { return s_emptyString; } else { const XalanDOMString* const theParentDefaultNamespace = theParent->getNamespacesHandler().getNamespace(s_emptyString); if (theParentDefaultNamespace == 0) { return s_emptyString; } else { return *theParentDefaultNamespace; } } } }