GME  13
XIncludeUtils.cpp
Go to the documentation of this file.
00001 /*
00002  * Licensed to the Apache Software Foundation (ASF) under one or more
00003  * contributor license agreements.  See the NOTICE file distributed with
00004  * this work for additional information regarding copyright ownership.
00005  * The ASF licenses this file to You under the Apache License, Version 2.0
00006  * (the "License"); you may not use this file except in compliance with
00007  * the License.  You may obtain a copy of the License at
00008  *
00009  *      http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS,
00013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 
00018 /*
00019  * $Id: XIncludeUtils.cpp 933212 2010-04-12 12:17:58Z amassari $
00020  */
00021 
00022 #include <xercesc/xinclude/XIncludeUtils.hpp>
00023 #include <xercesc/xinclude/XIncludeLocation.hpp>
00024 #include <xercesc/dom/DOM.hpp>
00025 #include <xercesc/util/TransService.hpp>
00026 #include <xercesc/util/XMLUri.hpp>
00027 #include <xercesc/util/XMLMsgLoader.hpp>
00028 #include <xercesc/util/XMLResourceIdentifier.hpp>
00029 #include <xercesc/util/BinInputStream.hpp>
00030 #include <xercesc/util/OutOfMemoryException.hpp>
00031 #include <xercesc/internal/XMLInternalErrorHandler.hpp>
00032 #include <xercesc/parsers/XercesDOMParser.hpp>
00033 #include <xercesc/sax/InputSource.hpp>
00034 #include <xercesc/framework/URLInputSource.hpp>
00035 
00036 XERCES_CPP_NAMESPACE_BEGIN
00037 
00038 XIncludeUtils::XIncludeUtils(XMLErrorReporter *errorReporter){
00039     fErrorReporter = errorReporter;
00040     fIncludeHistoryHead = NULL;
00041 }
00042 
00043 XIncludeUtils::~XIncludeUtils(){
00044     freeInclusionHistory();
00045 }
00046 
00047 // ---------------------------------------------------------------------------
00048 //  Generic function to parse a dom node performing any Xinclude's it ecounters,
00049 //   storing its results in parsedDocument, which is expected to be a real
00050 //   document. sourceNode is the current location in parsedDocument, and
00051 //   all xinclude manipulation is done in place (i.e. source is manipulated).
00052 // ---------------------------------------------------------------------------
00053 bool
00054 XIncludeUtils::parseDOMNodeDoingXInclude(DOMNode *sourceNode, DOMDocument *parsedDocument, XMLEntityHandler* entityResolver){
00055     if (sourceNode) {
00056         /* create the list of child elements here, since it gets changed during the parse */
00057         RefVectorOf<DOMNode> children(10, false);
00058         for (DOMNode *child = sourceNode->getFirstChild(); child != NULL; child = child->getNextSibling()){
00059             children.addElement(child);
00060         }
00061 
00062         if (sourceNode->getNodeType() == DOMNode::ELEMENT_NODE){
00063             if (isXIIncludeDOMNode(sourceNode)){
00064                 /* once we do an include on the source element, it is unsafe to do the include
00065                    on the children, since they will have been changed by the top level include */
00066                 bool success = doDOMNodeXInclude(sourceNode, parsedDocument, entityResolver);
00067 
00068                 //popFromCurrentInclusionHistoryStack(NULL);
00069                 /* return here as we do not want to fall through to the parsing of the children below
00070                    - they should have been replaced by the XInclude */
00071                 return success;
00072             } else if (isXIFallbackDOMNode(sourceNode)){
00073                 /* This must be a fallback element that is not a child of an include element.
00074                    This is defined as a fatal error */
00075                 XIncludeUtils::reportError(sourceNode, XMLErrs::XIncludeOrphanFallback,
00076                     NULL, parsedDocument->getDocumentURI());
00077                 return false;
00078             }
00079         }
00080 
00081         /* to have got here, we must not have found an xinclude element in the current element, so
00082            need to walk the entire child list parsing for each. An xinclude in  a
00083            node does not affect a peer, so we can simply parse each child in turn */
00084         for (XMLSize_t i = 0; i < children.size(); i++){
00085             parseDOMNodeDoingXInclude(children.elementAt(i), parsedDocument, entityResolver);
00086         }
00087     }
00088     return false;
00089 }
00090 
00091 // ---------------------------------------------------------------------------
00092 // utility func to extract a DOMNodes Base attr value if present
00093 // ---------------------------------------------------------------------------
00094 static const XMLCh *
00095 getBaseAttrValue(DOMNode *node){
00096     if (node->getNodeType() == DOMNode::ELEMENT_NODE){
00097         DOMElement *elem = (DOMElement *)node;
00098         if(elem->hasAttributes()) {
00099             /* get all the attributes of the node */
00100             DOMNamedNodeMap *pAttributes = elem->getAttributes();
00101             XMLSize_t nSize = pAttributes->getLength();
00102             for(XMLSize_t i=0;i<nSize;++i) {
00103                 DOMAttr *pAttributeNode = (DOMAttr*) pAttributes->item(i);
00104                 /* get attribute name */
00105                 if (XMLString::equals(pAttributeNode->getName(), XIncludeUtils::fgXIBaseAttrName)){
00106                     /*if (namespace == XMLUni::fgXMLString){
00107 
00108                     }*/
00109                     return pAttributeNode->getValue();
00110                 }
00111             }
00112         }
00113     }
00114     return NULL;
00115 }
00116 
00117 // ---------------------------------------------------------------------------
00118 //  This method assumes that currentNode is an xinclude element and parses
00119 //   it accordingly, acting on what it finds.
00120 // ---------------------------------------------------------------------------
00121 bool
00122 XIncludeUtils::doDOMNodeXInclude(DOMNode *xincludeNode, DOMDocument *parsedDocument, XMLEntityHandler* entityResolver){
00123     bool modifiedNode = false;
00124     /* the relevant attributes to look for */
00125     const XMLCh *href = NULL;
00126     const XMLCh *parse = NULL;
00127     const XMLCh *xpointer = NULL;
00128     const XMLCh *encoding = NULL;
00129     const XMLCh *accept = NULL;
00130     const XMLCh *acceptlanguage = NULL;
00131     DOMNode *includeParent = xincludeNode->getParentNode();
00132 
00133 
00134     if(xincludeNode->hasAttributes()) {
00135         /* get all the attributes of the node */
00136         DOMNamedNodeMap *pAttributes = xincludeNode->getAttributes();
00137         XMLSize_t nSize = pAttributes->getLength();
00138         for(XMLSize_t i=0;i<nSize;++i) {
00139             DOMAttr *pAttributeNode = (DOMAttr*) pAttributes->item(i);
00140             const XMLCh *attrName = pAttributeNode->getName();
00141             /* check each attribute against the potential useful names */
00142             if (XMLString::equals(attrName, XIncludeUtils::fgXIIncludeHREFAttrName)){
00143                 href = pAttributeNode->getValue();
00144             } else if (XMLString::equals(attrName, XIncludeUtils::fgXIIncludeParseAttrName)){
00145                 parse = pAttributeNode->getValue();
00146             } else if (XMLString::equals(attrName, XIncludeUtils::fgXIIncludeXPointerAttrName)){
00147                 xpointer = pAttributeNode->getValue();
00148             } else if (XMLString::equals(attrName, XIncludeUtils::fgXIIncludeEncodingAttrName)){
00149                 encoding = pAttributeNode->getValue();
00150             } else if (XMLString::equals(attrName, XIncludeUtils::fgXIIncludeAcceptAttrName)){
00151                 accept = pAttributeNode->getValue();
00152             } else if (XMLString::equals(attrName, XIncludeUtils::fgXIIncludeAcceptLanguageAttrName)){
00153                 acceptlanguage = pAttributeNode->getValue();
00154             } else {
00155                 /* if any other attribute is in the xi namespace, it's an error */
00156                 const XMLCh *attrNamespaceURI = pAttributeNode->getNamespaceURI();
00157                 if (attrNamespaceURI && XMLString::equals(attrNamespaceURI, XIncludeUtils::fgXIIIncludeNamespaceURI)){
00158                 } else {
00159                     /* ignore - any other attribute is allowed according to spec,
00160                        and must be ignored */
00161                 }
00162             }
00163         }
00164     }
00165     // 3.1 xi:include Element
00166     // The children property of the xi:include element may include a single xi:fallback element;
00167     // the appearance of more than one xi:fallback element, an xi:include element,
00168     // or any other element from the XInclude namespace is a fatal error.
00169     DOMNode *child;
00170     DOMElement *fallback = NULL;
00171     for (child = xincludeNode->getFirstChild(); child != 0; child=child->getNextSibling()){
00172         if(child->getNodeType()!=DOMNode::ELEMENT_NODE)
00173             continue;
00174         if ( isXIFallbackDOMNode(child) ){
00175             if (fallback != NULL){
00176                 /* fatal error - there are more than one fallback children */
00177                 XIncludeUtils::reportError(xincludeNode, XMLErrs::XIncludeMultipleFallbackElems,
00178                     parsedDocument->getDocumentURI(), parsedDocument->getDocumentURI());
00179                 return false;
00180             }
00181             fallback = (DOMElement*)child;
00182         }
00183         else if(isXIIncludeDOMNode(child) || XMLString::equals(child->getNamespaceURI(), XIncludeUtils::fgXIIIncludeNamespaceURI)) {
00184             /* fatal error - an xi element different from xi:fallback is a child of xi:include */
00185             XIncludeUtils::reportError(xincludeNode, XMLErrs::XIncludeDisallowedChild,
00186                 child->getNodeName(), parsedDocument->getDocumentURI());
00187             return false;
00188         }
00189     }
00190 
00191     if (href == NULL){
00192         /* this is an unrecoverable error until we have xpointer support -
00193            if there is an xpointer, the current document is assumed
00194            however, there is no xpointer support yet */
00195         XIncludeUtils::reportError(xincludeNode, XMLErrs::XIncludeNoHref,
00196             NULL, parsedDocument->getDocumentURI());
00197         return false;
00198     }
00199 
00200     /* set up the accept and accept-language values */
00201     if (accept != NULL){
00202 
00203     }
00204 
00205     if (parse == NULL){
00206         /* use the default, as specified */
00207         parse = XIncludeUtils::fgXIIncludeParseAttrXMLValue;
00208     }
00209 
00210     if (xpointer != NULL){
00211         /* not supported yet */
00212         /* Note that finding an xpointer attr along with parse="text" is a Fatal Error
00213          *  - http://www.w3.org/TR/xinclude/#include-location */
00214         XIncludeUtils::reportError(xincludeNode, XMLErrs::XIncludeXPointerNotSupported,
00215             NULL, href);
00216         return false;
00217     }
00218 
00219     /* set up the href according to what has gone before */
00220     XIncludeLocation hrefLoc(href);
00221     XIncludeLocation relativeLocation(href);
00222     const XMLCh *includeBase = xincludeNode->getBaseURI();
00223     if (includeBase != NULL){
00224         hrefLoc.prependPath(includeBase);
00225     }
00226 
00227     if (getBaseAttrValue(xincludeNode) != NULL){
00228         relativeLocation.prependPath(getBaseAttrValue(xincludeNode));
00229     }
00230 
00231     /*  Take the relevant action - we need to retrieve the target as a whole before
00232         we can know if it was successful or not, therefore the do* methods do
00233         not modify the parsedDocument. Swapping the results in is left to the
00234         caller (i.e. here) */
00235     DOMText *includedText = NULL;
00236     DOMDocument *includedDoc = NULL;
00237     if (XMLString::equals(parse, XIncludeUtils::fgXIIncludeParseAttrXMLValue)){
00238         /* including a XML element */
00239         includedDoc = doXIncludeXMLFileDOM(hrefLoc.getLocation(), relativeLocation.getLocation(), xincludeNode, parsedDocument, entityResolver);
00240     } else if (XMLString::equals(parse, XIncludeUtils::fgXIIncludeParseAttrTextValue)){
00241         /* including a text value */
00242         includedText = doXIncludeTEXTFileDOM(hrefLoc.getLocation(), relativeLocation.getLocation(), encoding, xincludeNode, parsedDocument, entityResolver);
00243     } else {
00244         /* invalid parse attribute value - fatal error according to the specification */
00245         XIncludeUtils::reportError(xincludeNode, XMLErrs::XIncludeInvalidParseVal,
00246             parse, parsedDocument->getDocumentURI());
00247         return false;
00248     }
00249 
00250     RefVectorOf<DOMNode> delayedProcessing(12,false);
00251     if (includedDoc == NULL && includedText == NULL){
00252         /* there was an error - this is now a resource error
00253            let's see if there is a fallback */
00254         XIncludeUtils::reportError(xincludeNode, XMLErrs::XIncludeIncludeFailedResourceError,
00255             hrefLoc.getLocation(), parsedDocument->getDocumentURI());
00256 
00257         if (includeParent == NULL){
00258             includeParent = parsedDocument;
00259         }
00260 
00261         // we could be getting errors trying to insert elements at the root of the document, so we should use replaceChild;
00262         // in order to handle multiple nodes, add them to a document fragment and use that to replace the original node
00263         if (fallback){
00264             /* baseURI fixups - see http://www.w3.org/TR/xinclude/#base for details. */
00265             XMLUri parentURI(includeParent->getBaseURI());
00266             XMLUri includedURI(fallback->getBaseURI());
00267 
00268             if (fallback->hasChildNodes()){
00269                 DOMDocumentFragment* frag = parsedDocument->createDocumentFragment();
00270                 DOMNode *child = fallback->getFirstChild();
00271                 /* add the content of the fallback element, and remove the fallback elem itself */
00272                 for ( ; child != NULL ; child=child->getNextSibling()){
00273                     if (child->getNodeType() == DOMNode::DOCUMENT_TYPE_NODE){
00274                         continue;
00275                     }
00276                     DOMNode *newNode = parsedDocument->importNode(child, true);
00277                     /* if the paths differ we need to add a base attribute */
00278                     if (newNode->getNodeType()==DOMNode::ELEMENT_NODE && !XMLString::equals(parentURI.getPath(), includedURI.getPath())){
00279                         if (getBaseAttrValue(newNode) == NULL){
00280                             /* need to calculate the proper path difference to get the relativePath */
00281                             ((DOMElement*)newNode)->setAttribute(fgXIBaseAttrName, getBaseAttrValue(fallback->getParentNode()));
00282                         } else {
00283                             /* the included node has base of its own which takes precedence */
00284                             XIncludeLocation xil(getBaseAttrValue(newNode));
00285                             if (getBaseAttrValue(fallback->getParentNode()) != NULL){
00286                                 /* prepend any specific base modification of the xinclude node */
00287                                 xil.prependPath(getBaseAttrValue(fallback->getParentNode()));
00288                             }
00289                             ((DOMElement*)newNode)->setAttribute(fgXIBaseAttrName, xil.getLocation());
00290                         }
00291                     }
00292                     DOMNode *newChild = frag->appendChild(newNode);
00293                     // don't process the node now, wait until it is placed in the final position
00294                     delayedProcessing.addElement(newChild);
00295                     //parseDOMNodeDoingXInclude(newChild, parsedDocument, entityResolver);
00296                 }
00297                 includeParent->replaceChild(frag, xincludeNode);
00298                 frag->release();
00299 
00300                 for(XMLSize_t i=0;i<delayedProcessing.size();i++)
00301                 {
00302                     DOMNode* childNode=delayedProcessing.elementAt(i);
00303                     parseDOMNodeDoingXInclude(childNode, parsedDocument, entityResolver);
00304                 }
00305                 modifiedNode = true;
00306             } else {
00307                 /* empty fallback element - simply remove it! */
00308                 includeParent->removeChild(xincludeNode);
00309                 modifiedNode = true;
00310             }
00311         } else {
00312             XIncludeUtils::reportError(xincludeNode, XMLErrs::XIncludeIncludeFailedNoFallback,
00313                 parsedDocument->getDocumentURI(), parsedDocument->getDocumentURI());
00314             return false;
00315         }
00316     } else {
00317         if (includedDoc){
00318             /* record the successful include while we process the children */
00319             addDocumentURIToCurrentInclusionHistoryStack(hrefLoc.getLocation());
00320 
00321             DOMDocumentFragment* frag = parsedDocument->createDocumentFragment();
00322             /* need to import the document prolog here */
00323             DOMNode *child = includedDoc->getFirstChild();
00324             for (; child != NULL; child = child->getNextSibling()) {
00325                 if (child->getNodeType() == DOMNode::DOCUMENT_TYPE_NODE)
00326                     continue;
00327                 // check for NOTATION or ENTITY clash
00328                 if(child->getNodeType()==DOMNode::ELEMENT_NODE && includedDoc->getDoctype()!=NULL) {
00329                     DOMNamedNodeMap *pAttributes = child->getAttributes();
00330                     XMLSize_t nSize = pAttributes->getLength();
00331                     for(XMLSize_t i=0;i<nSize;++i) {
00332                         DOMAttr *pAttributeNode = (DOMAttr*) pAttributes->item(i);
00333                         const DOMTypeInfo * typeInfo=pAttributeNode->getSchemaTypeInfo();
00334                         if(typeInfo && XMLString::equals(typeInfo->getTypeNamespace(), XMLUni::fgInfosetURIName)) {
00335                             if(XMLString::equals(typeInfo->getTypeName(), XMLUni::fgNotationString)) {
00336                                 const XMLCh* notationName=pAttributeNode->getNodeValue();
00337                                 DOMNotation* notat=(DOMNotation*)includedDoc->getDoctype()->getNotations()->getNamedItem(notationName);
00338                                 // ensure we have a DTD
00339                                 if(parsedDocument->getDoctype()==NULL)
00340                                     parsedDocument->insertBefore(parsedDocument->createDocumentType(parsedDocument->getDocumentElement()->getNodeName(), NULL,NULL), parsedDocument->getFirstChild());
00341                                 DOMNotation* myNotation=(DOMNotation*)parsedDocument->getDoctype()->getNotations()->getNamedItem(notationName);
00342                                 if(myNotation==NULL)
00343                                 {
00344                                     // it's missing, add it
00345                                     parsedDocument->getDoctype()->getNotations()->setNamedItem(parsedDocument->importNode(notat, true));
00346                                 }
00347                                 else if(XMLString::equals(myNotation->getPublicId(), notat->getPublicId()) &&
00348                                         XMLString::equals(myNotation->getSystemId(), notat->getSystemId()) &&
00349                                         XMLString::equals(myNotation->getBaseURI(), notat->getBaseURI()))
00350                                 {
00351                                     // it's duplicate, ignore it
00352                                 }
00353                                 else
00354                                 {
00355                                     // it's a conflict, report it
00356                                     XIncludeUtils::reportError(xincludeNode, XMLErrs::XIncludeConflictingNotation,
00357                                         notationName, parsedDocument->getDocumentURI());
00358                                 }
00359                             }
00360                             else if(XMLString::equals(typeInfo->getTypeName(), XMLUni::fgEntityString)) {
00361                                 const XMLCh* entityName=pAttributeNode->getNodeValue();
00362                                 DOMEntity* ent=(DOMEntity*)includedDoc->getDoctype()->getEntities()->getNamedItem(entityName);
00363                                 // ensure we have a DTD
00364                                 if(parsedDocument->getDoctype()==NULL)
00365                                     parsedDocument->insertBefore(parsedDocument->createDocumentType(parsedDocument->getDocumentElement()->getNodeName(), NULL,NULL), parsedDocument->getFirstChild());
00366                                 DOMEntity* myEnt=(DOMEntity*)parsedDocument->getDoctype()->getEntities()->getNamedItem(entityName);
00367                                 if(myEnt==NULL)
00368                                 {
00369                                     // it's missing, add it
00370                                     parsedDocument->getDoctype()->getEntities()->setNamedItem(parsedDocument->importNode(ent, true));
00371                                 }
00372                                 else if(XMLString::equals(myEnt->getPublicId(), ent->getPublicId()) &&
00373                                         XMLString::equals(myEnt->getSystemId(), ent->getSystemId()) &&
00374                                         XMLString::equals(myEnt->getBaseURI(), ent->getBaseURI()))
00375                                 {
00376                                     // it's duplicate, ignore it
00377                                 }
00378                                 else
00379                                 {
00380                                     // it's a conflict, report it
00381                                     XIncludeUtils::reportError(xincludeNode, XMLErrs::XIncludeConflictingEntity,
00382                                         entityName, parsedDocument->getDocumentURI());
00383                                 }
00384                             }
00385                         }
00386                     }
00387                 }
00388                 DOMNode *newNode = parsedDocument->importNode(child, true);
00389                 DOMNode *newChild = frag->appendChild(newNode);
00390                 // don't process the node now, wait until it is placed in the final position
00391                 delayedProcessing.addElement(newChild);
00392                 //parseDOMNodeDoingXInclude(newChild, parsedDocument, entityResolver);
00393             }
00394             includeParent->replaceChild(frag, xincludeNode);
00395             frag->release();
00396 
00397             for(XMLSize_t i=0;i<delayedProcessing.size();i++)
00398             {
00399                 DOMNode* childNode=delayedProcessing.elementAt(i);
00400                 parseDOMNodeDoingXInclude(childNode, parsedDocument, entityResolver);
00401             }
00402             popFromCurrentInclusionHistoryStack(NULL);
00403             modifiedNode = true;
00404         } else if (includedText){
00405             includeParent->replaceChild(includedText, xincludeNode);
00406             modifiedNode = true;
00407         }
00408     }
00409 
00410     if (includedDoc)
00411         includedDoc->release();
00412 
00413     return modifiedNode;
00414 }
00415 
00416 DOMDocument *
00417 XIncludeUtils::doXIncludeXMLFileDOM(const XMLCh *href,
00418                                     const XMLCh *relativeHref,
00419                                     DOMNode *includeNode,
00420                                     DOMDocument *parsedDocument,
00421                                     XMLEntityHandler* entityResolver){
00422     if (XIncludeUtils::isInCurrentInclusionHistoryStack(href)){
00423          /* including something back up the current history */
00424          XIncludeUtils::reportError(parsedDocument, XMLErrs::XIncludeCircularInclusionLoop,
00425               href, href);
00426          return NULL;
00427     }
00428 
00429     if (XMLString::equals(href, parsedDocument->getBaseURI())){
00430         /* trying to include itself */
00431         XIncludeUtils::reportError(parsedDocument, XMLErrs::XIncludeCircularInclusionDocIncludesSelf,
00432               href, href);
00433         return NULL;
00434     }
00435 
00436     /* Instantiate the DOM parser. */
00437     XercesDOMParser parser;
00438     parser.setDoNamespaces(true);
00439     /* don't want to recurse the xi processing here */
00440     parser.setDoXInclude(false);
00441     /* create the schema info nodes, so that we can detect conflicting notations */
00442     parser.setCreateSchemaInfo(true);
00443     XMLInternalErrorHandler xierrhandler;
00444     parser.setErrorHandler(&xierrhandler);
00445 
00446     DOMDocument *includedNode = NULL;
00447     try {
00448         InputSource* is=NULL;
00449         Janitor<InputSource> janIS(is);
00450         if(entityResolver) {
00451             XMLResourceIdentifier resIdentifier(XMLResourceIdentifier::ExternalEntity,
00452                                                 relativeHref,
00453                                                 NULL,
00454                                                 NULL,
00455                                                 includeNode->getBaseURI());
00456             is=entityResolver->resolveEntity(&resIdentifier);
00457             janIS.reset(is);
00458         }
00459         if(janIS.get()!=NULL)
00460             parser.parse(*janIS.get());
00461         else
00462             parser.parse(href);
00463         /* need to be able to release the parser but keep the document */
00464         if (!xierrhandler.getSawError() && !xierrhandler.getSawFatal())
00465             includedNode = parser.adoptDocument();
00466     }
00467     catch (const XMLException& /*toCatch*/)
00468     {
00469         XIncludeUtils::reportError(parsedDocument, XMLErrs::XIncludeResourceErrorWarning,
00470               href, href);
00471     }
00472     catch (const DOMException& /*toCatch*/)
00473     {
00474         XIncludeUtils::reportError(parsedDocument, XMLErrs::XIncludeResourceErrorWarning,
00475               href, href);
00476     }
00477     catch (...)
00478     {
00479         XIncludeUtils::reportError(parsedDocument, XMLErrs::XIncludeResourceErrorWarning,
00480              href, href);
00481     }
00482 
00483     //addDocumentURIToCurrentInclusionHistoryStack(href);
00484 
00485     if(includedNode != NULL){
00486         /* baseURI fixups - see http://www.w3.org/TR/xinclude/#base for details. */
00487         DOMElement *topLevelElement = includedNode->getDocumentElement();
00488         if (topLevelElement && topLevelElement->getNodeType() == DOMNode::ELEMENT_NODE ){
00489             XMLUri parentURI(includeNode->getBaseURI());
00490             XMLUri includedURI(includedNode->getBaseURI());
00491 
00492             /* if the paths differ we need to add a base attribute */
00493             if (!XMLString::equals(parentURI.getPath(), includedURI.getPath())){
00494                 if (getBaseAttrValue(topLevelElement) == NULL){
00495                     /* need to calculate the proper path difference to get the relativePath */
00496                     topLevelElement->setAttribute(fgXIBaseAttrName, relativeHref);
00497                 } else {
00498                     /* the included node has base of its own which takes precedence */
00499                     XIncludeLocation xil(getBaseAttrValue(topLevelElement));
00500                     if (getBaseAttrValue(includeNode) != NULL){
00501                         /* prepend any specific base modification of the xinclude node */
00502                         xil.prependPath(getBaseAttrValue(includeNode));
00503                     }
00504                     topLevelElement->setAttribute(fgXIBaseAttrName, xil.getLocation());
00505                 }
00506             }
00507         }
00508     }
00509     return includedNode;
00510 }
00511 
00512 DOMText *
00513 XIncludeUtils::doXIncludeTEXTFileDOM(const XMLCh *href,
00514                                      const XMLCh *relativeHref,
00515                                      const XMLCh *encoding,
00516                                      DOMNode *includeNode,
00517                                      DOMDocument *parsedDocument,
00518                                      XMLEntityHandler* entityResolver){
00519     if (encoding == NULL)
00520         /* "UTF-8" is stipulated default by spec */
00521         encoding = XMLUni::fgUTF8EncodingString;
00522 
00523     XMLTransService::Codes failReason;
00524     XMLTranscoder* transcoder = XMLPlatformUtils::fgTransService->makeNewTranscoderFor(encoding, failReason, 16*1024);
00525     Janitor<XMLTranscoder> janTranscoder(transcoder);
00526     if (failReason){
00527         XIncludeUtils::reportError(parsedDocument, XMLErrs::XIncludeCannotOpenFile, href, href);
00528         return NULL;
00529     }
00530 
00531     //addDocumentURIToCurrentInclusionHistoryStack(href);
00532 
00533     InputSource* is=NULL;
00534     Janitor<InputSource> janIS(is);
00535     if(entityResolver) {
00536         XMLResourceIdentifier resIdentifier(XMLResourceIdentifier::ExternalEntity,
00537                                             relativeHref,
00538                                             NULL,
00539                                             NULL,
00540                                             includeNode->getBaseURI());
00541         is=entityResolver->resolveEntity(&resIdentifier);
00542         janIS.reset(is);
00543     }
00544     if(janIS.get()==NULL)
00545         janIS.reset(new URLInputSource(href));
00546     if(janIS.get()==NULL) {
00547         XIncludeUtils::reportError(parsedDocument, XMLErrs::XIncludeCannotOpenFile,
00548             href, href);
00549         return NULL;
00550     }
00551     BinInputStream* stream=janIS.get()->makeStream();
00552     if(stream==NULL) {
00553         XIncludeUtils::reportError(parsedDocument, XMLErrs::XIncludeCannotOpenFile,
00554             href, href);
00555         return NULL;
00556     }
00557     Janitor<BinInputStream> janStream(stream);
00558     const XMLSize_t maxToRead=16*1024;
00559     XMLByte* buffer=(XMLByte*)XMLPlatformUtils::fgMemoryManager->allocate(maxToRead * sizeof(XMLByte));
00560     if(buffer==NULL)
00561         throw OutOfMemoryException();
00562     ArrayJanitor<XMLByte> janBuffer(buffer, XMLPlatformUtils::fgMemoryManager);
00563     XMLCh* xmlChars=(XMLCh*)XMLPlatformUtils::fgMemoryManager->allocate(maxToRead*2*sizeof(XMLCh));
00564     if(xmlChars==NULL)
00565         throw OutOfMemoryException();
00566     ArrayJanitor<XMLCh> janUniBuffer(xmlChars, XMLPlatformUtils::fgMemoryManager);
00567     unsigned char *charSizes = (unsigned char *)XMLPlatformUtils::fgMemoryManager->allocate(maxToRead * sizeof(unsigned char));
00568     if(charSizes==NULL)
00569         throw OutOfMemoryException();
00570     ArrayJanitor<unsigned char> janCharSizes(charSizes, XMLPlatformUtils::fgMemoryManager);
00571 
00572     XMLSize_t nRead, nOffset=0;
00573     XMLBuffer repository;
00574     while((nRead=stream->readBytes(buffer+nOffset, maxToRead-nOffset))>0){
00575         XMLSize_t bytesEaten=0;
00576         XMLSize_t nCount = transcoder->transcodeFrom(buffer, nRead, xmlChars, maxToRead*2, bytesEaten, charSizes);
00577         repository.append(xmlChars, nCount);
00578         if(bytesEaten<nRead) {
00579             nOffset=nRead-bytesEaten;
00580             memmove(buffer, buffer+bytesEaten, nRead-bytesEaten);
00581         }
00582     }
00583     return parsedDocument->createTextNode(repository.getRawBuffer());
00584 }
00585 
00586 /*static*/ bool
00587 XIncludeUtils::isXIIncludeDOMNode(DOMNode *node){
00588     const XMLCh *nodeName = node->getLocalName();
00589     const XMLCh *namespaceURI = node->getNamespaceURI();
00590 
00591     return isXIIncludeElement(nodeName, namespaceURI);
00592 }
00593 
00594 /*static*/ bool
00595 XIncludeUtils::isXIFallbackDOMNode(DOMNode *node){
00596     const XMLCh *nodeName = node->getLocalName();
00597     const XMLCh *namespaceURI = node->getNamespaceURI();
00598 
00599     return isXIFallbackElement(nodeName, namespaceURI);
00600 }
00601 
00602 /*static*/ bool
00603 XIncludeUtils::isXIIncludeElement(const XMLCh *name, const XMLCh *namespaceURI){
00604     if (namespaceURI == NULL || name == NULL){
00605         /* no namespaces not supported */
00606         return false;
00607     }
00608     if (XMLString::equals(name, fgXIIncludeQName)
00609         && XMLString::equals(namespaceURI, fgXIIIncludeNamespaceURI)){
00610         return true;
00611     }
00612     return false;
00613 }
00614 
00615 /*static*/ bool
00616 XIncludeUtils::isXIFallbackElement(const XMLCh *name, const XMLCh *namespaceURI){
00617     if (namespaceURI == NULL || name == NULL){
00618         /* no namespaces not supported */
00619         return false;
00620     }
00621     if (XMLString::equals(name, fgXIFallbackQName)
00622         && XMLString::equals(namespaceURI, fgXIIIncludeNamespaceURI)){
00623         return true;
00624     }
00625     return false;
00626 }
00627 
00628 /* 4.1.1 */
00629 const XMLCh *
00630 XIncludeUtils::getEscapedHRefAttrValue(const XMLCh * /*hrefAttrValue*/, bool & /*needsDeallocating*/){
00631     XMLCh *escapedAttr = NULL;
00632     return escapedAttr;
00633 }
00634 
00635 /* 4.1.2 */
00636 bool
00637 XIncludeUtils::setContentNegotiation(const XMLCh * /*acceptAttrValue*/, const XMLCh * /*acceptLangAttrValue*/){
00638     return false;
00639 }
00640 
00641 bool
00642 XIncludeUtils::checkTextIsValidForInclude(XMLCh * /*includeChars*/){
00643     return false;
00644 }
00645 
00646 // ========================================================
00647 // the stack utilities are slightly convoluted debug versions, they
00648 // will be pared down for the release code
00649 // ========================================================
00650 static XIncludeHistoryNode *
00651 getTopOfCurrentInclusionHistoryStack(XIncludeHistoryNode *head){
00652     XIncludeHistoryNode *historyCursor = head;
00653     if (historyCursor == NULL){
00654         return NULL;
00655     }
00656     while (historyCursor->next != NULL){
00657         historyCursor = historyCursor->next;
00658     }
00659     return historyCursor;
00660 }
00661 
00662 bool
00663 XIncludeUtils::addDocumentURIToCurrentInclusionHistoryStack(const XMLCh *URItoAdd){
00664     XIncludeHistoryNode *newNode = (XIncludeHistoryNode *)XMLPlatformUtils::fgMemoryManager->allocate(sizeof(XIncludeHistoryNode));
00665     if (newNode == NULL){
00666         return false;
00667     }
00668     newNode->URI = XMLString::replicate(URItoAdd);
00669     newNode->next = NULL;
00670 
00671     if (fIncludeHistoryHead == NULL){
00672         fIncludeHistoryHead = newNode;
00673         return true;
00674     }
00675     XIncludeHistoryNode *topNode = getTopOfCurrentInclusionHistoryStack(fIncludeHistoryHead);
00676     topNode->next = newNode;
00677     return true;
00678 }
00679 
00680 bool
00681 XIncludeUtils::isInCurrentInclusionHistoryStack(const XMLCh *toFind){
00682     XIncludeHistoryNode *historyCursor = fIncludeHistoryHead;
00683     /* walk the list */
00684     while (historyCursor != NULL){
00685         if (XMLString::equals(toFind, historyCursor->URI)){
00686             return true;
00687         }
00688         historyCursor = historyCursor->next;
00689     }
00690     return false;
00691 }
00692 
00693 XIncludeHistoryNode *
00694 XIncludeUtils::popFromCurrentInclusionHistoryStack(const XMLCh * /*toPop*/){
00695     XIncludeHistoryNode *historyCursor = fIncludeHistoryHead;
00696     XIncludeHistoryNode *penultimateCursor = historyCursor;
00697 
00698     if (fIncludeHistoryHead == NULL){
00699         return NULL;
00700     }
00701 
00702     while (historyCursor->next != NULL){
00703         penultimateCursor = historyCursor;
00704         historyCursor = historyCursor->next;
00705     }
00706 
00707     if (historyCursor == fIncludeHistoryHead){
00708         fIncludeHistoryHead = NULL;
00709     } else {
00710         penultimateCursor->next = NULL;
00711     }
00712 
00713     XMLString::release(&(historyCursor->URI));
00714     XMLPlatformUtils::fgMemoryManager->deallocate((void *)historyCursor);
00715     return NULL;
00716 }
00717 
00718 void
00719 XIncludeUtils::freeInclusionHistory(){
00720     XIncludeHistoryNode *historyCursor = XIncludeUtils::fIncludeHistoryHead;
00721     while (historyCursor != NULL){
00722         XIncludeHistoryNode *next = historyCursor->next;
00723         XMLString::release(&(historyCursor->URI));
00724         XMLPlatformUtils::fgMemoryManager->deallocate((void *)historyCursor);
00725         historyCursor = next;
00726     }
00727     XIncludeUtils::fIncludeHistoryHead = NULL;
00728 }
00729 
00730 bool
00731 XIncludeUtils::reportError(const DOMNode* const    /*errorNode*/
00732                               , XMLErrs::Codes errorType
00733                               , const XMLCh*   const    errorMsg
00734                               , const XMLCh * const href)
00735 {
00736     bool toContinueProcess = true;   /* default value for no error handler */
00737 
00738     const XMLCh* const                    systemId = href;
00739     const XMLCh* const                    publicId = href;
00740     /* TODO - look these up somehow? */
00741     const XMLFileLoc                 lineNum = 0;
00742     const XMLFileLoc                 colNum = 0;
00743 
00744     if (fErrorReporter)
00745     {
00746         // Load the message into a local for display
00747         const XMLSize_t msgSize = 1023;
00748         XMLCh errText[msgSize + 1];
00749 
00750         /* TODO - investigate whether this is complete */
00751         XMLMsgLoader  *errMsgLoader = XMLPlatformUtils::loadMsgSet(XMLUni::fgXMLErrDomain);
00752         if (errorMsg == NULL){
00753             if (errMsgLoader->loadMsg(errorType, errText, msgSize))
00754             {
00755                     // <TBD> Probably should load a default msg here
00756             }
00757         } else {
00758             if (errMsgLoader->loadMsg(errorType, errText, msgSize, errorMsg))
00759             {
00760                     // <TBD> Probably should load a default msg here
00761             }
00762         }
00763 
00764         fErrorReporter->error(errorType
00765                               , XMLUni::fgXMLErrDomain    //fgXMLErrDomain
00766                               , XMLErrs::errorType(errorType)
00767                               , errText
00768                               , systemId
00769                               , publicId
00770                               , lineNum
00771                               , colNum);
00772     }
00773 
00774     if (XMLErrs::isFatal(errorType))
00775         fErrorCount++;
00776 
00777     return toContinueProcess;
00778 }
00779 
00780 /* TODO - declared in this file for convenience, prob ought to be moved out to
00781    util/XMLUni.cpp before releasing */
00782 const XMLCh XIncludeUtils::fgXIIncludeQName[] =
00783 {
00784     chLatin_i, chLatin_n, chLatin_c, chLatin_l, chLatin_u, chLatin_d, chLatin_e, chNull
00785 };
00786 const XMLCh XIncludeUtils::fgXIFallbackQName[] =
00787 {
00788     chLatin_f, chLatin_a, chLatin_l, chLatin_l, chLatin_b, chLatin_a, chLatin_c, chLatin_k, chNull
00789 };
00790 const XMLCh XIncludeUtils::fgXIIncludeHREFAttrName[] =
00791 {
00792     chLatin_h, chLatin_r, chLatin_e, chLatin_f, chNull
00793 };
00794 const XMLCh XIncludeUtils::fgXIIncludeParseAttrName[] =
00795 {
00796     chLatin_p, chLatin_a, chLatin_r, chLatin_s, chLatin_e, chNull
00797 };
00798 const XMLCh XIncludeUtils::fgXIIncludeXPointerAttrName[] =
00799 {
00800     chLatin_x, chLatin_p, chLatin_o, chLatin_i, chLatin_n, chLatin_t, chLatin_e, chLatin_r, chNull
00801 };
00802 const XMLCh XIncludeUtils::fgXIIncludeEncodingAttrName[] =
00803 {
00804      chLatin_e, chLatin_n, chLatin_c, chLatin_o, chLatin_d, chLatin_i, chLatin_n, chLatin_g, chNull
00805 };
00806 const XMLCh XIncludeUtils::fgXIIncludeAcceptAttrName[] =
00807 {
00808      chLatin_a, chLatin_c, chLatin_c, chLatin_e, chLatin_p, chLatin_t, chNull
00809 };
00810 const XMLCh XIncludeUtils::fgXIIncludeAcceptLanguageAttrName[] =
00811 {
00812      chLatin_a, chLatin_c, chLatin_c, chLatin_e, chLatin_p, chLatin_t, chDash, chLatin_l, chLatin_a,
00813          chLatin_n, chLatin_g, chLatin_u, chLatin_a, chLatin_g, chLatin_e, chNull
00814 };
00815 const XMLCh XIncludeUtils::fgXIIncludeParseAttrXMLValue[] =
00816 {
00817     chLatin_x, chLatin_m, chLatin_l, chNull
00818 };
00819 const XMLCh XIncludeUtils::fgXIIncludeParseAttrTextValue[] =
00820 {
00821     chLatin_t, chLatin_e, chLatin_x, chLatin_t, chNull
00822 };
00823 const XMLCh XIncludeUtils::fgXIIIncludeNamespaceURI[] =
00824 {
00825     /* http://www.w3.org/2001/XInclude */
00826     chLatin_h, chLatin_t, chLatin_t, chLatin_p, chColon, chForwardSlash
00827     ,   chForwardSlash, chLatin_w, chLatin_w, chLatin_w, chPeriod
00828     ,   chLatin_w, chDigit_3, chPeriod, chLatin_o, chLatin_r, chLatin_g
00829     ,   chForwardSlash, chDigit_2, chDigit_0, chDigit_0, chDigit_1
00830     ,    chForwardSlash, chLatin_X, chLatin_I, chLatin_n, chLatin_c, chLatin_l
00831     ,    chLatin_u, chLatin_d, chLatin_e, chNull
00832 };
00833 const XMLCh XIncludeUtils::fgXIBaseAttrName[] =
00834 {
00835     chLatin_x, chLatin_m, chLatin_l, chColon, chLatin_b, chLatin_a, chLatin_s, chLatin_e, chNull
00836 };
00837 
00838 XERCES_CPP_NAMESPACE_END