#include "../config/pathan_config.h" /* * Copyright (c) 2001, DecisionSoft Limited All rights reserved. * Please see LICENSE.TXT for more information. */ #include #include #include #include #include #include #include "../exceptions/InvalidLexicalSpaceException.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include const XMLCh FunctionDeepEqual::name[] = { XERCES_CPP_NAMESPACE_QUALIFIER chLatin_d, XERCES_CPP_NAMESPACE_QUALIFIER chLatin_e, XERCES_CPP_NAMESPACE_QUALIFIER chLatin_e, XERCES_CPP_NAMESPACE_QUALIFIER chLatin_p, XERCES_CPP_NAMESPACE_QUALIFIER chDash, XERCES_CPP_NAMESPACE_QUALIFIER chLatin_e, XERCES_CPP_NAMESPACE_QUALIFIER chLatin_q, XERCES_CPP_NAMESPACE_QUALIFIER chLatin_u, XERCES_CPP_NAMESPACE_QUALIFIER chLatin_a, XERCES_CPP_NAMESPACE_QUALIFIER chLatin_l, XERCES_CPP_NAMESPACE_QUALIFIER chNull }; /** * fn:deep-equal($parameter1 as item()*, $parameter2 as item()*) as xs:boolean * fn:deep-equal($parameter1 as item()*, $parameter2 as item()*, $collation as string) as xs:boolean **/ FunctionDeepEqual::FunctionDeepEqual(const VectorOfDataItems &args, XPath2MemoryManager* memMgr) : ConstantFoldingFunction(name,2, 3, "item()*,item()*,string", args, memMgr) { } /*static*/ bool FunctionDeepEqual::deep_equal(Sequence seq1, Sequence seq2, Collation* collation, DynamicContext* context) { // if both of the arguments are the empty sequence, return true if(seq1.isEmpty() && seq2.isEmpty()) { return true; } // if one, but not both, of the arguments is the empty sequence, return false if(seq1.isEmpty() != seq2.isEmpty()) { return false; } // Check if they have the same number of items if(seq1.getLength()!=seq2.getLength()) { return false; } // Check if items in corresponding positions in the two sequences compare equal if they are values // or if they have deep equality if they are nodes Sequence::iterator end1 = seq1.end(); for(Sequence::iterator it1 = seq1.begin(), it2 = seq2.begin(); it1 != end1; ++it1, ++it2) { const Item* item1 = *it1; const Item* item2 = *it2; if(item1->isNode() && item2->isNode()) { if(!node_deep_equal((const Node*)item1, (const Node*)item2,collation, context)) { return false; } } else if(item1->isAtomicValue() && item2->isAtomicValue()) { const AnyAtomicType* atom1 = (const AnyAtomicType*)item1; const AnyAtomicType* atom2 = (const AnyAtomicType*)item2; if(atom1->isOfType(XERCES_CPP_NAMESPACE_QUALIFIER SchemaSymbols::fgURI_SCHEMAFORSCHEMA, XERCES_CPP_NAMESPACE_QUALIFIER SchemaSymbols::fgDT_STRING, context) && atom2->isOfType(XERCES_CPP_NAMESPACE_QUALIFIER SchemaSymbols::fgURI_SCHEMAFORSCHEMA, XERCES_CPP_NAMESPACE_QUALIFIER SchemaSymbols::fgDT_STRING, context)) { const XMLCh *str1 = ((const ATStringOrDerived*)atom1)->asString(context); const XMLCh *str2 = ((const ATStringOrDerived*)atom2)->asString(context); if(collation->compare(str1,str2)!=0) { return false; } } else if(atom1->isOfType(XERCES_CPP_NAMESPACE_QUALIFIER SchemaSymbols::fgURI_SCHEMAFORSCHEMA, XERCES_CPP_NAMESPACE_QUALIFIER SchemaSymbols::fgDT_ANYSIMPLETYPE, context) && atom2->isOfType(XERCES_CPP_NAMESPACE_QUALIFIER SchemaSymbols::fgURI_SCHEMAFORSCHEMA, XERCES_CPP_NAMESPACE_QUALIFIER SchemaSymbols::fgDT_ANYSIMPLETYPE, context)) { if(!atom1->equals(atom2, context)) { return false; } } else { try { if(!Equals::equals(atom1,atom2,context)) { return false; } } catch (XPath2ErrorException &e) { return false; } catch (IllegalArgumentException &e) { return false; } } } else { //One of the items is neither a node or an atomic type (this doesn't make sense) assert(false); return 0; //should never get here } } return true; } /*static*/ bool FunctionDeepEqual::node_deep_equal(const Node* node1, const Node* node2, Collation* collation, DynamicContext* context) { // If the two nodes are of different node-kinds, the result is false. if(!(XPath2Utils::equals(node1->dmNodeKind(), node2->dmNodeKind()))) { return false; } // If the two nodes have names, and the names are different when compared as expanded-QNames, // the result is false. Sequence expName1=node1->dmNodeName(context); Sequence expName2=node2->dmNodeName(context); if(expName1.getLength()!=expName2.getLength()) { return false; } if(expName1.getLength()==1) { const ATQNameOrDerived* qname1 = (const ATQNameOrDerived*)expName1.first(); const ATQNameOrDerived* qname2 = (const ATQNameOrDerived*)expName2.first(); if(!qname1->equals(qname2, context)) { return false; } } // If the two nodes are text nodes, comment nodes, processing instruction nodes, or namespace nodes, // then the result is true if and only if the two nodes have equal string-values, when compared using // the selected collation. const XMLCh* nodeType=node1->dmNodeKind(); const XMLCh *node1str = node1->dmStringValue(context); const XMLCh *node2str = node2->dmStringValue(context); if( ((XERCES_CPP_NAMESPACE_QUALIFIER XMLString::equals(nodeType, Node::text_string)) || (XERCES_CPP_NAMESPACE_QUALIFIER XMLString::equals(nodeType, Node::comment_string)) || (XERCES_CPP_NAMESPACE_QUALIFIER XMLString::equals(nodeType, Node::processing_instruction_string)) || (XERCES_CPP_NAMESPACE_QUALIFIER XMLString::equals(nodeType, Node::namespace_string))) && collation->compare(node1str, node2str)!=0) { return false; } // If either node has attributes, then the result is false if either node has an attribute that is not // deep-equal to an attribute of the other node, using the selected collation. XERCES_CPP_NAMESPACE_QUALIFIER DOMNamedNodeMap* attrs1=node1->getDOMNode()->getAttributes(); XERCES_CPP_NAMESPACE_QUALIFIER DOMNamedNodeMap* attrs2=node2->getDOMNode()->getAttributes(); if((attrs1!=NULL && attrs2==NULL) || (attrs1==NULL && attrs2!=NULL)) { return false; } else if(attrs1!=NULL && attrs2!=NULL) { if(attrs1->getLength()!=attrs2->getLength()) { return false; } for(XMLSize_t i=0;igetLength();i++) { XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* attr1=attrs1->item(i); XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* attr2=attrs2->getNamedItem(attr1->getNodeName()); if(attr2==NULL) { return false; } if(!node_deep_equal(context->getMemoryManager()->createNode(attr1), context->getMemoryManager()->createNode(attr2),collation, context)) { return false; } } } // If neither node has element children, then the result is true only if the other node also has simple // content, and if the simple content of the two nodes (that is, the result of the xf:data function) is // equal under the rules for the xf:deep-equal function, using the selected collation. // (Note: attributes always have simple content.) XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* children1=node1->getDOMNode()->getFirstChild(); XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* children2=node2->getDOMNode()->getFirstChild(); bool bHasSubElements1=false,bHasSubElements2=false; Sequence sChildren1 = Sequence(context->getMemoryManager()); Sequence sChildren2 = Sequence(context->getMemoryManager()); if(children1!=NULL && children2!=NULL) { while(children1!=NULL) { if(children1->getNodeType() == XERCES_CPP_NAMESPACE_QUALIFIER DOMNode::ELEMENT_NODE) bHasSubElements1=true; if((children1->getNodeType() == XERCES_CPP_NAMESPACE_QUALIFIER DOMNode::ELEMENT_NODE) || (children1->getNodeType() == XERCES_CPP_NAMESPACE_QUALIFIER DOMNode::TEXT_NODE)) sChildren1.addItem(context->getMemoryManager()->createNode(children1)); children1=children1->getNextSibling(); } while(children2!=NULL) { if(children2->getNodeType() == XERCES_CPP_NAMESPACE_QUALIFIER DOMNode::ELEMENT_NODE) bHasSubElements2=true; if(children2->getNodeType() == XERCES_CPP_NAMESPACE_QUALIFIER DOMNode::ELEMENT_NODE || children2->getNodeType() == XERCES_CPP_NAMESPACE_QUALIFIER DOMNode::TEXT_NODE) sChildren2.addItem(context->getMemoryManager()->createNode(children2)); children2=children2->getNextSibling(); } } if(!bHasSubElements1 && !bHasSubElements2) { return deep_equal(node1->dmTypedValue(context),node2->dmTypedValue(context),collation, context); } return deep_equal(sChildren1,sChildren2,collation, context); } Sequence FunctionDeepEqual::collapseTreeInternal(DynamicContext* context, int flags) const { Sequence arg1=getParamNumber(1,context); Sequence arg2=getParamNumber(2,context); if(arg1.isEmpty() || arg2.isEmpty()) return Sequence(context->getMemoryManager()); Collation* collation=NULL; if(getNumArgs()>2) { Sequence collArg = getParamNumber(3,context); const XMLCh* collName = ((const ATStringOrDerived*)collArg.first())->asString(context); try { DatatypeFactory::STR2AT::createAnyURI(context->getMemoryManager(), collName, context); } catch(InvalidLexicalSpaceException &e) { DSLthrow(FunctionException, X("FunctionDeepEqual::collapseTreeInternal"), X("Invalid collationURI")); } collation=context->getCollation(collName); if(collation==NULL) DSLthrow(FunctionException,X("FunctionDeepEqual::collapseTreeInternal"),X("Collation object is not available")); } else collation=context->getDefaultCollation(); if(collation==NULL) collation=context->getCollation(CodepointCollation::getCodepointCollationName()); return Sequence(DatatypeFactory::POD2AT::createBoolean(context->getMemoryManager(), deep_equal(arg1, arg2, collation, context), context), context->getMemoryManager()); }