blob: a0066cf64c55bed2414c2ba850e589957625625a [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, International
* Business Machines, Inc., http://www.ibm.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
#include "NamespaceResolver.hpp"
#include <vector>
#include <XalanDOM/XalanNode.hpp>
#include <XalanDOM/XalanNamedNodeMap.hpp>
#include <PlatformSupport/XalanUnicode.hpp>
#include "DOMServices.hpp"
NamespaceResolver::NamespaceResolver() :
m_NSInfos()
{
}
NamespaceResolver::~NamespaceResolver()
{
}
void
NamespaceResolver::reset()
{
m_NSInfos.clear();
}
// Manefest constants: Prebuilt NSInfo objects for standard combinations
static NSInfo theNSInfoUnProcWithXMLNS(false, true);
static NSInfo theNSInfoUnProcWithoutXMLNS(false, false);
static NSInfo theNSInfoUnProcNoAncestorXMLNS(false, false, NSInfo::ANCESTORNOXMLNS);
static NSInfo theNSInfoNullWithXMLNS(true, true);
static NSInfo theNSInfoNullWithoutXMLNS(true, false);
static NSInfo theNSInfoNullNoAncestorXMLNS(true, false, NSInfo::ANCESTORNOXMLNS);
// Set (or reset) the cached NSInfo associated with a Node
void
NamespaceResolver::updateNamespace(
const XalanNode* theNode,
const NSInfo& theNamespace) const
{
#if defined(XALAN_NO_MUTABLE)
#if defined(XALAN_OLD_STYLE_CASTS)
((NamespaceResolver*)this)->m_NSInfos[theNode] = theNamespace;
#else
const_cast<NamespaceResolver*>(this)->m_NSInfos[theNode] = theNamespace;
#endif
#else
m_NSInfos[theNode] = theNamespace;
#endif
}
// Find the name of the namespace bound to the current node. This is done by searching
// upward through the model for a Namespace Declaration attribute. Once resolved, the
// namespace is cached in the m_NSInfos table for faster retrieval.
//
// Note that DOM Level 2 binds this value at the time the node is built, which should
// be considerably more efficient.
const XalanDOMString&
NamespaceResolver::getNamespaceOfNode(const XalanNode& theNode) const
{
#if !defined(XALAN_NO_NAMESPACES)
using std::make_pair;
using std::pair;
using std::vector;
#endif
const XalanNode* theLocalNode = &theNode;
bool hasProcessedNS;
NSInfo nsInfo;
const XalanNode::NodeType ntype = theLocalNode->getNodeType();
NSInfoMapType::const_iterator theIterator = m_NSInfos.end();
// Find the node in the map of cached nodes, if it's not an
// attribute node.
if(XalanNode::ATTRIBUTE_NODE != ntype)
{
theIterator = m_NSInfos.find(theLocalNode);
if (theIterator == m_NSInfos.end())
{
// Not found.
hasProcessedNS = false;
}
else
{
// Ahh, we found it.
nsInfo = (*theIterator).second;
hasProcessedNS = nsInfo.m_hasProcessedNS;
}
}
else
{
hasProcessedNS = false;
}
const XalanDOMString* namespaceOfPrefix = &DOMServices::s_emptyString;
if(hasProcessedNS)
{
namespaceOfPrefix = &nsInfo.m_namespace;
}
else
{
const XalanDOMString& nodeName = theLocalNode->getNodeName();
XalanDOMString::size_type indexOfNSSep = indexOf(nodeName, XalanUnicode::charColon);
XalanDOMString prefix;
// JKESS CHANGE: Attributes which have no prefix have no namespace,
// per standard Namespaces In XML behavior. They should not inherit from
// their owning Element, nor use the default namespace.
if(XalanNode::ATTRIBUTE_NODE == ntype && indexOfNSSep >= length(nodeName))
{
// Attibute nodes aren't handled by the nsInfos logic above.
// And since this is the no-prefix case, it won't buy us anything for
// explicit prefix lookups. Hence, I don't see any reason to register
// this result via updateNamespace.
// BIG UGLY RETURN HERE!!!!!!!
return *namespaceOfPrefix;
}
prefix = (indexOfNSSep < length(nodeName))
? substring(nodeName, 0, indexOfNSSep)
: XalanDOMString();
bool ancestorsHaveXMLNS = false;
bool nHasXMLNS = false;
// The xml: prefix is hardcoded
// (In the DOM, so is the xmlns: prefix... but that's DOM behavior,
// not specified by the NS spec.)
if(equals(prefix, DOMServices::s_XMLString) == true)
{
namespaceOfPrefix = &DOMServices::s_XMLNamespaceURI;
}
else
{
XalanNode::NodeType parentType = XalanNode::UNKNOWN_NODE;
const XalanNode* parent = theLocalNode;
typedef pair<const XalanNode*, NSInfo> CandidateNoAncestorEntryType;
typedef vector<CandidateNoAncestorEntryType> CandidateNoAncestorVectorType;
CandidateNoAncestorVectorType candidateNoAncestorXMLNS;
candidateNoAncestorXMLNS.reserve(eDefaultVectorSize);
// Hunt upward until resolve namespace or fail to do so.
while (0 != parent && length(*namespaceOfPrefix) == 0)
{
if(theIterator != m_NSInfos.end()
&& nsInfo.m_ancestorHasXMLNSAttrs == nsInfo.ANCESTORNOXMLNS)
{
break;
}
parentType = parent->getNodeType();
if(theIterator == m_NSInfos.end() ||
nsInfo.m_hasXMLNSAttrs == true)
{
bool elementHasXMLNS = false;
// Elements
if (parentType == XalanNode::ELEMENT_NODE)
{
// Scan the Element's Attr's, looking for namespace declarations
const XalanNamedNodeMap* const nnm =
parent->getAttributes();
assert(nnm != 0);
const unsigned int theLength = nnm->getLength();
for (unsigned int i = 0; i < theLength; i ++)
{
const XalanNode* attr = nnm->item(i);
const XalanDOMString& aname = attr->getNodeName();
// Quick test of first character, to reduce cost of startsWith.
if(charAt(aname, 0) == charAt(DOMServices::s_XMLNamespaceWithSeparator, 0))
{
// "xmlns:"* prefix declaration?
bool isPrefix = startsWith(aname, DOMServices::s_XMLNamespaceWithSeparator);
// or "xmlns" default declaration?
// JKESS: Reversed order of test; more efficient.
if (isPrefix == true || equals(aname, DOMServices::s_XMLNamespace) == true)
{
// Is this element the original node?
if(theLocalNode == parent)
{
nHasXMLNS = true; // local decls exist
}
elementHasXMLNS = true; // decls exist on current parent
ancestorsHaveXMLNS = true; // decls exist somewhere
// If xmlns:, what prefix is declared?
const XalanDOMString p = (isPrefix == true)
? substring(aname, length(DOMServices::s_XMLNamespaceWithSeparator))
: XalanDOMString();
// If it's the one we're looking for, resolve to NS
if (equals(p, prefix) == true)
{
namespaceOfPrefix = &attr->getNodeValue();
break;
}
}
}
}
}
// Fallthough for unresolved Elements,
// plus anything else except Attr's
if((XalanNode::ATTRIBUTE_NODE != parentType) &&
(theIterator == m_NSInfos.end()) &&
(theLocalNode != parent))
{
// Record whether this node defines any namespaces
// (_not_ whether it defines its own or what that is)
nsInfo = elementHasXMLNS
? theNSInfoUnProcWithXMLNS
: theNSInfoUnProcWithoutXMLNS;
updateNamespace(parent, nsInfo);
}
}
// Attr nodes need to look to their owning Element for their NS
// declaration (if any). Note that we're using the XPath data model,
// getParentOfNode(); in DOM terms, that's Attr.getOwningElement().
if(XalanNode::ATTRIBUTE_NODE == parentType)
{
parent = DOMServices::getParentOfNode(*parent);
}
// Any other node gets queued for annotation pass,
// along with our current nsInfo context
else
{
candidateNoAncestorXMLNS.push_back(make_pair(parent, nsInfo));
parent = parent->getParentNode();
}
// If we haven't run out of ancestors
if(0 != parent)
{
// Try to retrieve cached NS info for the newly selected parent
// for use in next pass through loop.
// If not found, continue using previous value
theIterator = m_NSInfos.find(parent);
if (theIterator != m_NSInfos.end())
{
nsInfo = (*theIterator).second;
}
}
}
// Anotation pass over the "any other node" queue
const CandidateNoAncestorVectorType::size_type nCandidates =
candidateNoAncestorXMLNS.size();
if(nCandidates > 0)
{
// If no inherited declarations (and we checked all the way to the root)
// then record nodes with no local declarations in the nsInfo cache,
// to avoid repeatedly searching this branch of the tree.
// ????? This feels overcomplicated, somehow...
if(false == ancestorsHaveXMLNS && 0 == parent)
{
for(unsigned int i = 0; i < nCandidates; i++)
{
const NSInfo& candidateInfo = candidateNoAncestorXMLNS[i].second;
if(candidateInfo == theNSInfoUnProcWithoutXMLNS ||
candidateInfo == theNSInfoNullWithoutXMLNS)
{
updateNamespace(parent, candidateInfo);
}
}
}
candidateNoAncestorXMLNS.clear();
}
}
// NOTE that attibute nodes aren't handled by the nsInfos logic,
// so it's unclear that the updateNamespace buys us anything directly.
// But I'm guessing it may be useful for getNamespaceForPrefix.
//
// ????? This seems like a lot of code for something which should be a set of
// bit-masks...
if(XalanNode::ATTRIBUTE_NODE != ntype)
{
assert(namespaceOfPrefix != 0);
// If Attribute's prefix wasn't resolved
if(0 == length(*namespaceOfPrefix))
{
// In context where other prefixes are defined
if(ancestorsHaveXMLNS == true)
{
// Local definitions exist
if(nHasXMLNS == true)
{
updateNamespace(theLocalNode, theNSInfoNullWithXMLNS);
}
// Only inherited definitions exist
else
{
updateNamespace(theLocalNode, theNSInfoNullWithoutXMLNS);
}
}
// No definitions exist
else
{
updateNamespace(theLocalNode, theNSInfoNullNoAncestorXMLNS);
}
}
else // Attribute's prefix was resolved, at least that one is declared
{
updateNamespace(theLocalNode, NSInfo(*namespaceOfPrefix, nHasXMLNS));
}
}
}
return *namespaceOfPrefix;
}