Skip to content
Snippets Groups Projects
XIncludeUtils.cpp 34 KiB
Newer Older
/*
 * 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.
 */

/*
 * $Id$
 */

#include <xercesc/xinclude/XIncludeUtils.hpp>
#include <xercesc/xinclude/XIncludeLocation.hpp>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/util/TransService.hpp>
#include <xercesc/util/XMLUri.hpp>
#include <xercesc/util/XMLMsgLoader.hpp>
#include <xercesc/util/XMLResourceIdentifier.hpp>
#include <xercesc/util/BinInputStream.hpp>
#include <xercesc/util/OutOfMemoryException.hpp>
#include <xercesc/internal/XMLInternalErrorHandler.hpp>
#include <xercesc/parsers/XercesDOMParser.hpp>
#include <xercesc/sax/InputSource.hpp>
#include <xercesc/framework/URLInputSource.hpp>

XERCES_CPP_NAMESPACE_BEGIN

XIncludeUtils::XIncludeUtils(XMLErrorReporter *errorReporter){
	fErrorReporter = errorReporter;
	fIncludeHistoryHead = NULL;
}

XIncludeUtils::~XIncludeUtils(){
	freeInclusionHistory();
}

// ---------------------------------------------------------------------------
//  Generic function to parse a dom node performing any Xinclude's it ecounters,
//   storing its results in parsedDocument, which is expected to be a real
//   document. sourceNode is the current location in parsedDocument, and
//   all xinclude manipulation is done in place (i.e. source is manipulated).
// ---------------------------------------------------------------------------
bool 
XIncludeUtils::parseDOMNodeDoingXInclude(DOMNode *sourceNode, DOMDocument *parsedDocument, XMLEntityHandler* entityResolver){
	int included = 0;
    if (sourceNode) {
		/* create the list of child elements here, since it gets changed during the parse */
		RefVectorOf<DOMNode> children(10, false);
		for (DOMNode *child = sourceNode->getFirstChild(); child != NULL; child = child->getNextSibling()){
			children.addElement(child);
		}
		
		if (sourceNode->getNodeType() == DOMNode::ELEMENT_NODE){
			if (isXIIncludeDOMNode(sourceNode)){
				/* once we do an include on the source element, it is unsafe to do the include
				   on the children, since they will have been changed by the top level include */
				bool success = doDOMNodeXInclude(sourceNode, parsedDocument, entityResolver);

				//popFromCurrentInclusionHistoryStack(NULL);
				/* return here as we do not want to fall through to the parsing of the children below
				   - they should have been replaced by the XInclude */
				return success;
			} else if (isXIFallbackDOMNode(sourceNode)){
				/* This must be a fallback element that is not a child of an include element.
				   This is defined as a fatal error */
				XIncludeUtils::reportError(sourceNode, XMLErrs::XIncludeOrphanFallback,
					NULL, parsedDocument->getDocumentURI());
				return false;
			}
		}
		
		/* to have got here, we must not have found an xinclude element in the current element, so
		   need to walk the entire child list parsing for each. An xinclude in  a
		   node does not affect a peer, so we can simply parse each child in turn */
		for (unsigned int i = 0; i < children.size(); i++){
			parseDOMNodeDoingXInclude(children.elementAt(i), parsedDocument, entityResolver);
		}
	}
	return (included)?true:false;
}

// ---------------------------------------------------------------------------
// utility func to extract a DOMNodes Base attr value if present 
// ---------------------------------------------------------------------------
static const XMLCh *
getBaseAttrValue(DOMNode *node){
	if (node->getNodeType() == DOMNode::ELEMENT_NODE){
		DOMElement *elem = (DOMElement *)node;
		if(elem->hasAttributes()) {
            /* get all the attributes of the node */
            DOMNamedNodeMap *pAttributes = elem->getAttributes();
            unsigned int nSize = pAttributes->getLength();
            for(unsigned int i=0;i<nSize;++i) {
                DOMAttr *pAttributeNode = (DOMAttr*) pAttributes->item(i);
                /* get attribute name */
				if (XMLString::equals(pAttributeNode->getName(), XIncludeUtils::fgXIBaseAttrName)){
					/*if (namespace == XMLUni::fgXMLString){

					}*/
					return pAttributeNode->getValue();
				}
            }
        }
	}
	return NULL;
}

// ---------------------------------------------------------------------------
//  This method assumes that currentNode is an xinclude element and parses 
//   it accordingly, acting on what it finds.
// ---------------------------------------------------------------------------
bool
XIncludeUtils::doDOMNodeXInclude(DOMNode *xincludeNode, DOMDocument *parsedDocument, XMLEntityHandler* entityResolver){
	bool modifiedNode = false;
	/* the relevant attributes to look for */
	const XMLCh *href = NULL;
	const XMLCh *parse = NULL;
	const XMLCh *xpointer = NULL;
	const XMLCh *encoding = NULL;
	const XMLCh *accept = NULL;
	const XMLCh *acceptlanguage = NULL;
	DOMNode *includeParent = xincludeNode->getParentNode();

	
	if(xincludeNode->hasAttributes()) {
		/* get all the attributes of the node */
		DOMNamedNodeMap *pAttributes = xincludeNode->getAttributes();
		unsigned int nSize = pAttributes->getLength();
		for(unsigned int i=0;i<nSize;++i) {
			DOMAttr *pAttributeNode = (DOMAttr*) pAttributes->item(i);
			const XMLCh *attrName = pAttributeNode->getName();
	        /* check each attribute against the potential useful names */
			if (XMLString::equals(attrName, XIncludeUtils::fgXIIncludeHREFAttrName)){
				href = pAttributeNode->getValue();
			} else if (XMLString::equals(attrName, XIncludeUtils::fgXIIncludeParseAttrName)){
				parse = pAttributeNode->getValue();
			} else if (XMLString::equals(attrName, XIncludeUtils::fgXIIncludeXPointerAttrName)){
				xpointer = pAttributeNode->getValue();
			} else if (XMLString::equals(attrName, XIncludeUtils::fgXIIncludeEncodingAttrName)){
				encoding = pAttributeNode->getValue();
			} else if (XMLString::equals(attrName, XIncludeUtils::fgXIIncludeAcceptAttrName)){
				accept = pAttributeNode->getValue();
			} else if (XMLString::equals(attrName, XIncludeUtils::fgXIIncludeAcceptLanguageAttrName)){
				acceptlanguage = pAttributeNode->getValue();
			} else {
				/* if any other attribute is in the xi namespace, it's an error */
				const XMLCh *attrNamespaceURI = pAttributeNode->getNamespaceURI();
				if (attrNamespaceURI && XMLString::equals(attrNamespaceURI, XIncludeUtils::fgXIIIncludeNamespaceURI)){
				} else {
					/* ignore - any other attribute is allowed according to spec,
					   and must be ignored */
				}
			}
		}
	}
    // 3.1 xi:include Element
    // The children property of the xi:include element may include a single xi:fallback element; 
    // the appearance of more than one xi:fallback element, an xi:include element, 
    // or any other element from the XInclude namespace is a fatal error. 
	DOMNode *child;
    DOMElement *fallback = NULL;
	for (child = xincludeNode->getFirstChild(); child != 0; child=child->getNextSibling()){
        if(child->getNodeType()!=DOMNode::ELEMENT_NODE)
            continue;
		if ( isXIFallbackDOMNode(child) ){
			if (fallback != NULL){
				/* fatal error - there are more than one fallback children */
				XIncludeUtils::reportError(xincludeNode, XMLErrs::XIncludeMultipleFallbackElems,
					parsedDocument->getDocumentURI(), parsedDocument->getDocumentURI());
				return false;
			}
			fallback = (DOMElement*)child;
		}
        else if(isXIIncludeDOMNode(child) || XMLString::equals(child->getNamespaceURI(), XIncludeUtils::fgXIIIncludeNamespaceURI)) {
			/* fatal error - an xi element different from xi:fallback is a child of xi:include */
			XIncludeUtils::reportError(xincludeNode, XMLErrs::XIncludeDisallowedChild,
				child->getNodeName(), parsedDocument->getDocumentURI());
			return false;
        }
	}

	if (href == NULL){
		/* this is an unrecoverable error until we have xpointer support -
		   if there is an xpointer, the current document is assumed 
		   however, there is no xpointer support yet */
		XIncludeUtils::reportError(xincludeNode, XMLErrs::XIncludeNoHref,
			NULL, parsedDocument->getDocumentURI());
		return false;
	} 

	/* set up the accept and accept-language values */
	if (accept != NULL){

	}

	if (parse == NULL){
		/* use the default, as specified */
		parse = XIncludeUtils::fgXIIncludeParseAttrXMLValue;
	}

	if (xpointer != NULL){
		/* not supported yet */
		/* Note that finding an xpointer attr along with parse="text" is a Fatal Error
		 *  - http://www.w3.org/TR/xinclude/#include-location */
		XIncludeUtils::reportError(xincludeNode, XMLErrs::XIncludeXPointerNotSupported,
			NULL, href);
		return false;
	}

	/* set up the href according to what has gone before */
	XIncludeLocation hrefLoc(href);
	XIncludeLocation relativeLocation(href);
	const XMLCh *includeBase = xincludeNode->getBaseURI();
	if (includeBase != NULL){
	}

	if (getBaseAttrValue(xincludeNode) != NULL){
		relativeLocation.prependPath(getBaseAttrValue(xincludeNode));	
	}

	/*  Take the relevant action - we need to retrieve the target as a whole before
	    we can know if it was successful or not, therefore the do* methods do
	    not modify the parsedDocument. Swapping the results in is left to the
	    caller (i.e. here) */
	DOMText *includedText = NULL;
	DOMDocument *includedDoc = NULL;
	if (XMLString::equals(parse, XIncludeUtils::fgXIIncludeParseAttrXMLValue)){
		/* including a XML element */
		includedDoc = doXIncludeXMLFileDOM(hrefLoc.getLocation(), relativeLocation.getLocation(), xincludeNode, parsedDocument, entityResolver);
	} else if (XMLString::equals(parse, XIncludeUtils::fgXIIncludeParseAttrTextValue)){
		/* including a text value */
		includedText = doXIncludeTEXTFileDOM(hrefLoc.getLocation(), relativeLocation.getLocation(), encoding, xincludeNode, parsedDocument, entityResolver);
	} else {
		/* invalid parse attribute value - fatal error according to the specification */
		XIncludeUtils::reportError(xincludeNode, XMLErrs::XIncludeInvalidParseVal,
			parse, parsedDocument->getDocumentURI());
		return false;
	}
	
	if (includedDoc == NULL && includedText == NULL){
		/* there was an error - this is now a resource error 
		   let's see if there is a fallback */
		XIncludeUtils::reportError(xincludeNode, XMLErrs::XIncludeIncludeFailedResourceError,
			hrefLoc.getLocation(), parsedDocument->getDocumentURI());
			includeParent = parsedDocument;
		}

		if (fallback){
            /* baseURI fixups - see http://www.w3.org/TR/xinclude/#base for details. */
            XMLUri parentURI(includeParent->getBaseURI());
            XMLUri includedURI(fallback->getBaseURI());

			if (fallback->hasChildNodes()){
				DOMNode *child = fallback->getFirstChild();
				/* add the content of the fallback element, and remove the fallback elem itself */
				for ( ; child != NULL ; child=child->getNextSibling()){
					if (child->getNodeType() == DOMNode::DOCUMENT_TYPE_NODE){
						continue;
					}
					DOMNode *newNode = parsedDocument->importNode(child, true);
                    /* if the paths differ we need to add a base attribute */
                    if (newNode->getNodeType()==DOMNode::ELEMENT_NODE && !XMLString::equals(parentURI.getPath(), includedURI.getPath())){
                        if (getBaseAttrValue(newNode) == NULL){
                            /* need to calculate the proper path difference to get the relativePath */
                            ((DOMElement*)newNode)->setAttribute(fgXIBaseAttrName, getBaseAttrValue(fallback->getParentNode()));
                        } else {
                            /* the included node has base of its own which takes precedence */
                            XIncludeLocation xil(getBaseAttrValue(newNode));
                            if (getBaseAttrValue(fallback->getParentNode()) != NULL){
                                /* prepend any specific base modification of the xinclude node */
                                xil.prependPath(getBaseAttrValue(fallback->getParentNode()));
                            }
                            ((DOMElement*)newNode)->setAttribute(fgXIBaseAttrName, xil.getLocation());
                        }
                    }
                    DOMNode *newChild = includeParent->insertBefore(newNode, xincludeNode);
					parseDOMNodeDoingXInclude(newChild, parsedDocument, entityResolver);
				modifiedNode = true;
			} else {
				/* empty fallback element - simply remove it! */
				includeParent->removeChild(xincludeNode);
				modifiedNode = true;
			}
		} else {
			XIncludeUtils::reportError(xincludeNode, XMLErrs::XIncludeIncludeFailedNoFallback,
				parsedDocument->getDocumentURI(), parsedDocument->getDocumentURI());
			return false;
		}
	} else {
        if (includedDoc){
            /* record the successful include while we process the children */
            addDocumentURIToCurrentInclusionHistoryStack(hrefLoc.getLocation());

            /* need to import the document prolog here */
            DOMNode *child = includedDoc->getFirstChild();
            for (; child != NULL; child = child->getNextSibling()) {
                if (child->getNodeType() == DOMNode::DOCUMENT_TYPE_NODE)
                    continue;
                // check for NOTATION or ENTITY clash
                if(child->getNodeType()==DOMNode::ELEMENT_NODE && includedDoc->getDoctype()!=NULL) {
		            DOMNamedNodeMap *pAttributes = child->getAttributes();
		            unsigned int nSize = pAttributes->getLength();
		            for(unsigned int i=0;i<nSize;++i) {
			            DOMAttr *pAttributeNode = (DOMAttr*) pAttributes->item(i);
                        const DOMTypeInfo * typeInfo=pAttributeNode->getSchemaTypeInfo();
                        if(typeInfo && XMLString::equals(typeInfo->getTypeNamespace(), XMLUni::fgInfosetURIName)) {
                            if(XMLString::equals(typeInfo->getTypeName(), XMLUni::fgNotationString)) {
                                const XMLCh* notationName=pAttributeNode->getNodeValue();
                                DOMNotation* notat=(DOMNotation*)includedDoc->getDoctype()->getNotations()->getNamedItem(notationName);
                                // ensure we have a DTD
                                if(parsedDocument->getDoctype()==NULL)
                                    parsedDocument->insertBefore(parsedDocument->createDocumentType(parsedDocument->getDocumentElement()->getNodeName(), NULL,NULL), parsedDocument->getFirstChild());
                                DOMNotation* myNotation=(DOMNotation*)parsedDocument->getDoctype()->getNotations()->getNamedItem(notationName);
                                if(myNotation==NULL)
                                {
                                    // it's missing, add it
                                    parsedDocument->getDoctype()->getNotations()->setNamedItem(parsedDocument->importNode(notat, true));
                                else if(XMLString::equals(myNotation->getPublicId(), notat->getPublicId()) && 
                                        XMLString::equals(myNotation->getSystemId(), notat->getSystemId()) && 
                                        XMLString::equals(myNotation->getBaseURI(), notat->getBaseURI()))
                                {
                                    // it's duplicate, ignore it
                                }
                                else
                                {
                                    // it's a conflict, report it
			                        XIncludeUtils::reportError(xincludeNode, XMLErrs::XIncludeConflictingNotation,
				                        notationName, parsedDocument->getDocumentURI());
                                }
                            }
                            else if(XMLString::equals(typeInfo->getTypeName(), XMLUni::fgEntityString)) {
                                const XMLCh* entityName=pAttributeNode->getNodeValue();
                                DOMEntity* ent=(DOMEntity*)includedDoc->getDoctype()->getEntities()->getNamedItem(entityName);
                                // ensure we have a DTD
                                if(parsedDocument->getDoctype()==NULL)
                                    parsedDocument->insertBefore(parsedDocument->createDocumentType(parsedDocument->getDocumentElement()->getNodeName(), NULL,NULL), parsedDocument->getFirstChild());
                                DOMEntity* myEnt=(DOMEntity*)parsedDocument->getDoctype()->getEntities()->getNamedItem(entityName);
                                if(myEnt==NULL)
                                {
                                    // it's missing, add it
                                    parsedDocument->getDoctype()->getEntities()->setNamedItem(parsedDocument->importNode(ent, true));
                                }
                                else if(XMLString::equals(myEnt->getPublicId(), ent->getPublicId()) && 
                                        XMLString::equals(myEnt->getSystemId(), ent->getSystemId()) && 
                                        XMLString::equals(myEnt->getBaseURI(), ent->getBaseURI()))
                                {
                                    // it's duplicate, ignore it
                                }
                                else
                                {
                                    // it's a conflict, report it
			                        XIncludeUtils::reportError(xincludeNode, XMLErrs::XIncludeConflictingEntity,
				                        entityName, parsedDocument->getDocumentURI());
                                }
                            }
                        }
                    }
                }
                DOMNode *newNode = parsedDocument->importNode(child, true);
                DOMNode *newChild = includeParent->insertBefore(newNode, xincludeNode);
                parseDOMNodeDoingXInclude(newChild, parsedDocument, entityResolver);
            }
            includeParent->removeChild(xincludeNode);
            popFromCurrentInclusionHistoryStack(NULL);
            modifiedNode = true;
        } else if (includedText){
            includeParent->replaceChild(includedText, xincludeNode);
            modifiedNode = true;
        }
	}

	if (includedDoc)
		includedDoc->release();

	return modifiedNode;
}

DOMDocument *
XIncludeUtils::doXIncludeXMLFileDOM(const XMLCh *href, 
                                    const XMLCh *relativeHref, 
                                    DOMNode *includeNode, 
                                    DOMDocument *parsedDocument, 
                                    XMLEntityHandler* entityResolver){
    if (XIncludeUtils::isInCurrentInclusionHistoryStack(href)){
         /* including something back up the current history */
         XIncludeUtils::reportError(parsedDocument, XMLErrs::XIncludeCircularInclusionLoop,
              href, href);
         return NULL;
    }

    if (XMLString::equals(href, parsedDocument->getBaseURI())){
        /* trying to include itself */
        XIncludeUtils::reportError(parsedDocument, XMLErrs::XIncludeCircularInclusionDocIncludesSelf, 
    }

    /* Instantiate the DOM parser. */
    XercesDOMParser parser;
    parser.setDoNamespaces(true);
    /* don't want to recurse the xi processing here */
    parser.setDoXInclude(false);
    /* create the schema info nodes, so that we can detect conflicting notations */
    parser.setCreateSchemaInfo(true);
    XMLInternalErrorHandler xierrhandler;
    parser.setErrorHandler(&xierrhandler);
        InputSource* is=NULL;
        Janitor<InputSource> janIS(is);
        if(entityResolver) {
            XMLResourceIdentifier resIdentifier(XMLResourceIdentifier::ExternalEntity,
                                                relativeHref,
                                                NULL,
                                                NULL,
                                                includeNode->getBaseURI());
            is=entityResolver->resolveEntity(&resIdentifier);
            janIS.reset(is);
        }
        if(janIS.get()!=NULL)
            parser.parse(*janIS.get());
        else
            parser.parse(href);
        /* need to be able to release the parser but keep the document */
        if (!xierrhandler.getSawError() && !xierrhandler.getSawFatal())
            includedNode = parser.adoptDocument();
    }
    catch (const XMLException& /*toCatch*/)
    {
        XIncludeUtils::reportError(parsedDocument, XMLErrs::XIncludeResourceErrorWarning,
              href, href);
    }
    catch (const DOMException& /*toCatch*/)
    {
        XIncludeUtils::reportError(parsedDocument, XMLErrs::XIncludeResourceErrorWarning,
              href, href);
    }
    catch (...)
    {
        XIncludeUtils::reportError(parsedDocument, XMLErrs::XIncludeResourceErrorWarning,
             href, href);
    }

    //addDocumentURIToCurrentInclusionHistoryStack(href);

    if(includedNode != NULL){
        /* baseURI fixups - see http://www.w3.org/TR/xinclude/#base for details. */
        DOMElement *topLevelElement = includedNode->getDocumentElement();
        if (topLevelElement && topLevelElement->getNodeType() == DOMNode::ELEMENT_NODE ){
            XMLUri parentURI(includeNode->getBaseURI());
            XMLUri includedURI(includedNode->getBaseURI());

            /* if the paths differ we need to add a base attribute */
            if (!XMLString::equals(parentURI.getPath(), includedURI.getPath())){
                if (getBaseAttrValue(topLevelElement) == NULL){
                    /* need to calculate the proper path difference to get the relativePath */
                    topLevelElement->setAttribute(fgXIBaseAttrName, relativeHref);
                } else {
                    /* the included node has base of its own which takes precedence */
                    XIncludeLocation xil(getBaseAttrValue(topLevelElement));
                    if (getBaseAttrValue(includeNode) != NULL){
                        /* prepend any specific base modification of the xinclude node */
                        xil.prependPath(getBaseAttrValue(includeNode));
                    }
                    topLevelElement->setAttribute(fgXIBaseAttrName, xil.getLocation());
                }
            }
        }
    }
    return includedNode;                                                                                                                                                                                                                            
XIncludeUtils::doXIncludeTEXTFileDOM(const XMLCh *href, 
                                     const XMLCh *relativeHref, 
                                     const XMLCh *encoding, 
                            		 DOMNode *includeNode,
                                     DOMDocument *parsedDocument, 
                                     XMLEntityHandler* entityResolver){
	if (encoding == NULL)
		/* "UTF-8" is stipulated default by spec */
		encoding = XMLUni::fgUTF8EncodingString;

	XMLTransService::Codes failReason;
	XMLTranscoder* transcoder = XMLPlatformUtils::fgTransService->makeNewTranscoderFor(encoding, failReason, 16*1024);
    Janitor<XMLTranscoder> janTranscoder(transcoder);
	if (failReason){
		XIncludeUtils::reportError(parsedDocument, XMLErrs::XIncludeCannotOpenFile, href, href);
		return NULL;
    }

	//addDocumentURIToCurrentInclusionHistoryStack(href);

    InputSource* is=NULL;
    Janitor<InputSource> janIS(is);
    if(entityResolver) {
        XMLResourceIdentifier resIdentifier(XMLResourceIdentifier::ExternalEntity,
                                            relativeHref,
                                            NULL,
                                            NULL,
                                            includeNode->getBaseURI());
        is=entityResolver->resolveEntity(&resIdentifier);
        janIS.reset(is);
    }
    if(janIS.get()==NULL)
        janIS.reset(new URLInputSource(href));
    if(janIS.get()==NULL) {
	    XIncludeUtils::reportError(parsedDocument, XMLErrs::XIncludeCannotOpenFile,
		    href, href);
        return NULL;
    }
    BinInputStream* stream=janIS.get()->makeStream();
    if(stream==NULL) {
	    XIncludeUtils::reportError(parsedDocument, XMLErrs::XIncludeCannotOpenFile,
		    href, href);
        return NULL;
    }
    Janitor<BinInputStream> janStream(stream);
    const int maxToRead=16*1024;
    XMLByte* buffer=(XMLByte*)XMLPlatformUtils::fgMemoryManager->allocate(maxToRead * sizeof(XMLByte));
    if(buffer==NULL)
        throw OutOfMemoryException();
    ArrayJanitor<XMLByte> janBuffer(buffer, XMLPlatformUtils::fgMemoryManager);
    XMLCh* xmlChars=(XMLCh*)XMLPlatformUtils::fgMemoryManager->allocate(maxToRead*2*sizeof(XMLCh));
    if(xmlChars==NULL)
        throw OutOfMemoryException();
    ArrayJanitor<XMLCh> janUniBuffer(xmlChars, XMLPlatformUtils::fgMemoryManager);
    unsigned char *charSizes = (unsigned char *)XMLPlatformUtils::fgMemoryManager->allocate(maxToRead * sizeof(unsigned char));
    if(charSizes==NULL)
        throw OutOfMemoryException();
    ArrayJanitor<unsigned char> janCharSizes(charSizes, XMLPlatformUtils::fgMemoryManager);

    XMLBuffer repository;
    while((nRead=stream->readBytes(buffer+nOffset, maxToRead-nOffset))>0){
        XMLSize_t bytesEaten=0;
	    XMLSize_t nCount = transcoder->transcodeFrom(buffer, nRead, xmlChars, maxToRead*2, bytesEaten, charSizes);
        repository.append(xmlChars, nCount);
        if(bytesEaten<nRead) {
            nOffset=nRead-bytesEaten;
            memmove(buffer, buffer+bytesEaten, nRead-bytesEaten);
        }
    return parsedDocument->createTextNode(repository.getRawBuffer());
XIncludeUtils::isXIIncludeDOMNode(DOMNode *node){
	const XMLCh *nodeName = node->getLocalName();
	const XMLCh *namespaceURI = node->getNamespaceURI();

	return isXIIncludeElement(nodeName, namespaceURI);
}

XIncludeUtils::isXIFallbackDOMNode(DOMNode *node){
	const XMLCh *nodeName = node->getLocalName();
	const XMLCh *namespaceURI = node->getNamespaceURI();

	return isXIFallbackElement(nodeName, namespaceURI);
}

XIncludeUtils::isXIIncludeElement(const XMLCh *name, const XMLCh *namespaceURI){
	if (namespaceURI == NULL || name == NULL){
		/* no namespaces not supported */
		return false;
	}
	if (XMLString::equals(name, fgXIIncludeQName) 
		&& XMLString::equals(namespaceURI, fgXIIIncludeNamespaceURI)){
		return true;
	}
	return false;
}
	
XIncludeUtils::isXIFallbackElement(const XMLCh *name, const XMLCh *namespaceURI){
	if (namespaceURI == NULL || name == NULL){
		/* no namespaces not supported */
		return false;
	}
	if (XMLString::equals(name, fgXIFallbackQName) 
		&& XMLString::equals(namespaceURI, fgXIIIncludeNamespaceURI)){
		return true;
	}
	return false;
}

/* 4.1.1 */
const XMLCh *
XIncludeUtils::getEscapedHRefAttrValue(const XMLCh * /*hrefAttrValue*/, bool & /*needsDeallocating*/){
	XMLCh *escapedAttr = NULL;
	return escapedAttr;
}

/* 4.1.2 */
bool 
XIncludeUtils::setContentNegotiation(const XMLCh * /*acceptAttrValue*/, const XMLCh * /*acceptLangAttrValue*/){
XIncludeUtils::checkTextIsValidForInclude(XMLCh * /*includeChars*/){
	return false;
}

// ========================================================
// the stack utilities are slightly convoluted debug versions, they
// will be pared down for the release code 
// ========================================================
static XIncludeHistoryNode *
getTopOfCurrentInclusionHistoryStack(XIncludeHistoryNode *head){
	XIncludeHistoryNode *historyCursor = head;
	if (historyCursor == NULL){
		return NULL;
	}
	while (historyCursor->next != NULL){
		historyCursor = historyCursor->next;
	}
	return historyCursor;
}

bool 
XIncludeUtils::addDocumentURIToCurrentInclusionHistoryStack(const XMLCh *URItoAdd){
	XIncludeHistoryNode *newNode = (XIncludeHistoryNode *)XMLPlatformUtils::fgMemoryManager->allocate(sizeof(XIncludeHistoryNode));
	if (newNode == NULL){
		return false;
	}
	newNode->URI = XMLString::replicate(URItoAdd);
	newNode->next = NULL; 

	if (fIncludeHistoryHead == NULL){
		fIncludeHistoryHead = newNode;
		return true;	
	}
	XIncludeHistoryNode *topNode = getTopOfCurrentInclusionHistoryStack(fIncludeHistoryHead);
	topNode->next = newNode;
	return true;
}

bool 
XIncludeUtils::isInCurrentInclusionHistoryStack(const XMLCh *toFind){
	XIncludeHistoryNode *historyCursor = fIncludeHistoryHead;
	/* walk the list */
	while (historyCursor != NULL){		
		if (XMLString::equals(toFind, historyCursor->URI)){
			return true;
		}
		historyCursor = historyCursor->next;
	}
	return false;
}

XIncludeHistoryNode *
XIncludeUtils::popFromCurrentInclusionHistoryStack(const XMLCh * /*toPop*/){
	XIncludeHistoryNode *historyCursor = fIncludeHistoryHead;
	XIncludeHistoryNode *penultimateCursor = historyCursor;

	if (fIncludeHistoryHead == NULL){
		return NULL;	
	}

	while (historyCursor->next != NULL){
		penultimateCursor = historyCursor;
		historyCursor = historyCursor->next;
	}
	
	if (penultimateCursor == fIncludeHistoryHead){
		historyCursor = fIncludeHistoryHead;
		fIncludeHistoryHead = NULL;
	} else {
		penultimateCursor->next = NULL;
	}

	// XERCES_STD_QUALIFIER cerr << "poppinURIofStack " << XMLString::transcode(historyCursor->URI) << XERCES_STD_QUALIFIER endl;
	XMLString::release(&(historyCursor->URI));
	XMLPlatformUtils::fgMemoryManager->deallocate((void *)historyCursor);
	return NULL;
}

void 
XIncludeUtils::freeInclusionHistory(){
	XIncludeHistoryNode *historyCursor = XIncludeUtils::fIncludeHistoryHead;
	while (historyCursor != NULL){
		XIncludeHistoryNode *next = historyCursor->next;
		/* XMLString::release(&(historyCursor->URI));
		   XMLPlatformUtils::fgMemoryManager->deallocate((void *)historyCursor); */
		historyCursor = next;
	}
	XIncludeUtils::fIncludeHistoryHead = NULL;
}

bool 
XIncludeUtils::reportError(const DOMNode* const    /*errorNode*/
                              , XMLErrs::Codes errorType
                              , const XMLCh*   const    errorMsg
							  , const XMLCh * const href)
{
    bool toContinueProcess = true;   /* default value for no error handler */

    const XMLCh* const        			systemId = href;
    const XMLCh* const        			publicId = href;
    /* TODO - look these up somehow? */
    const unsigned long                 lineNum = 0;
    const unsigned long                 colNum = 0;

    if (fErrorReporter)
    {
    	// Load the message into a local for display
        const XMLSize_t msgSize = 1023;
        XMLCh errText[msgSize + 1];

		/* TODO - investigate whether this is complete */
		XMLMsgLoader  *errMsgLoader = XMLPlatformUtils::loadMsgSet(XMLUni::fgXMLErrDomain);
		if (errorMsg == NULL){
        	if (errMsgLoader->loadMsg(errorType, errText, msgSize))
        	{
                	// <TBD> Probably should load a default msg here
        	}
		} else {
			if (errMsgLoader->loadMsg(errorType, errText, msgSize, errorMsg))
        	{
                	// <TBD> Probably should load a default msg here
        	}
		}
    	
        fErrorReporter->error(errorType
        					  , XMLUni::fgXMLErrDomain    //fgXMLErrDomain
        					  , XMLErrs::errorType(errorType)
        					  , errText
        					  , systemId
        					  , publicId
        					  , lineNum
        					  , colNum);
    }

    if (XMLErrs::isFatal(errorType))
        fErrorCount++;

    return toContinueProcess;
}

/* TODO - declared in this file for convenience, prob ought to be moved out to 
   util/XMLUni.cpp before releasing */
const XMLCh XIncludeUtils::fgXIIncludeQName[] =
{
	chLatin_i, chLatin_n, chLatin_c, chLatin_l, chLatin_u, chLatin_d, chLatin_e, chNull
};
const XMLCh XIncludeUtils::fgXIFallbackQName[] =
{
	chLatin_f, chLatin_a, chLatin_l, chLatin_l, chLatin_b, chLatin_a, chLatin_c, chLatin_k, chNull
};
const XMLCh XIncludeUtils::fgXIIncludeHREFAttrName[] =
{
	chLatin_h, chLatin_r, chLatin_e, chLatin_f, chNull
};
const XMLCh XIncludeUtils::fgXIIncludeParseAttrName[] =
{
	chLatin_p, chLatin_a, chLatin_r, chLatin_s, chLatin_e, chNull
};
const XMLCh XIncludeUtils::fgXIIncludeXPointerAttrName[] =
{
	chLatin_x, chLatin_p, chLatin_o, chLatin_i, chLatin_n, chLatin_t, chLatin_e, chLatin_r, chNull
};
const XMLCh XIncludeUtils::fgXIIncludeEncodingAttrName[] =
{
	 chLatin_e, chLatin_n, chLatin_c, chLatin_o, chLatin_d, chLatin_i, chLatin_n, chLatin_g, chNull
};
const XMLCh XIncludeUtils::fgXIIncludeAcceptAttrName[] =
{
	 chLatin_a, chLatin_c, chLatin_c, chLatin_e, chLatin_p, chLatin_t, chNull
};
const XMLCh XIncludeUtils::fgXIIncludeAcceptLanguageAttrName[] =
{
	 chLatin_a, chLatin_c, chLatin_c, chLatin_e, chLatin_p, chLatin_t, chDash, chLatin_l, chLatin_a,
		 chLatin_n, chLatin_g, chLatin_u, chLatin_a, chLatin_g, chLatin_e, chNull
};
const XMLCh XIncludeUtils::fgXIIncludeParseAttrXMLValue[] =
{
	chLatin_x, chLatin_m, chLatin_l, chNull	
};
const XMLCh XIncludeUtils::fgXIIncludeParseAttrTextValue[] =
{
	chLatin_t, chLatin_e, chLatin_x, chLatin_t, chNull
};
const XMLCh XIncludeUtils::fgXIIIncludeNamespaceURI[] =
{
	/* http://www.w3.org/2001/XInclude */
	chLatin_h, chLatin_t, chLatin_t, chLatin_p, chColon, chForwardSlash
    ,   chForwardSlash, chLatin_w, chLatin_w, chLatin_w, chPeriod
    ,   chLatin_w, chDigit_3, chPeriod, chLatin_o, chLatin_r, chLatin_g
    ,   chForwardSlash, chDigit_2, chDigit_0, chDigit_0, chDigit_1
	,	chForwardSlash, chLatin_X, chLatin_I, chLatin_n, chLatin_c, chLatin_l
	,	chLatin_u, chLatin_d, chLatin_e, chNull
};
const XMLCh XIncludeUtils::fgXIBaseAttrName[] =
{
	chLatin_x, chLatin_m, chLatin_l, chColon, chLatin_b, chLatin_a, chLatin_s, chLatin_e, chNull
};

XERCES_CPP_NAMESPACE_END