| /* |
| * 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/util/XMLInitializer.hpp> |
| #include <xercesc/internal/XMLInternalErrorHandler.hpp> |
| #include <xercesc/parsers/XercesDOMParser.hpp> |
| #include <xercesc/sax/InputSource.hpp> |
| #include <xercesc/framework/URLInputSource.hpp> |
| |
| namespace XERCES_CPP_NAMESPACE { |
| |
| // --------------------------------------------------------------------------- |
| // Local static data |
| // --------------------------------------------------------------------------- |
| static XMLMsgLoader* gMsgLoader = 0; |
| |
| void XMLInitializer::initializeXInclude() |
| { |
| gMsgLoader = XMLPlatformUtils::loadMsgSet(XMLUni::fgXMLErrDomain); |
| |
| if (!gMsgLoader) |
| XMLPlatformUtils::panic(PanicHandler::Panic_CantLoadMsgDomain); |
| } |
| |
| void XMLInitializer::terminateXInclude() |
| { |
| delete gMsgLoader; |
| gMsgLoader = 0; |
| } |
| |
| // --------------------------------------------------------------------------- |
| // XIncludeUtils: Constructors and Destructor |
| // --------------------------------------------------------------------------- |
| |
| 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){ |
| 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 (XMLSize_t i = 0; i < children.size(); i++){ |
| parseDOMNodeDoingXInclude(children.elementAt(i), parsedDocument, entityResolver); |
| } |
| } |
| return 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(); |
| XMLSize_t nSize = pAttributes->getLength(); |
| for(XMLSize_t 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(); |
| XMLSize_t nSize = pAttributes->getLength(); |
| for(XMLSize_t 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){ |
| hrefLoc.prependPath(includeBase); |
| } |
| |
| 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; |
| } |
| |
| RefVectorOf<DOMNode> delayedProcessing(12,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()); |
| |
| if (includeParent == NULL){ |
| includeParent = parsedDocument; |
| } |
| |
| // we could be getting errors trying to insert elements at the root of the document, so we should use replaceChild; |
| // in order to handle multiple nodes, add them to a document fragment and use that to replace the original node |
| 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()){ |
| DOMDocumentFragment* frag = parsedDocument->createDocumentFragment(); |
| 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 = frag->appendChild(newNode); |
| // don't process the node now, wait until it is placed in the final position |
| delayedProcessing.addElement(newChild); |
| //parseDOMNodeDoingXInclude(newChild, parsedDocument, entityResolver); |
| } |
| includeParent->replaceChild(frag, xincludeNode); |
| frag->release(); |
| |
| for(XMLSize_t i=0;i<delayedProcessing.size();i++) |
| { |
| DOMNode* childNode=delayedProcessing.elementAt(i); |
| parseDOMNodeDoingXInclude(childNode, 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()); |
| |
| DOMDocumentFragment* frag = parsedDocument->createDocumentFragment(); |
| /* 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(); |
| XMLSize_t nSize = pAttributes->getLength(); |
| for(XMLSize_t 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 = frag->appendChild(newNode); |
| // don't process the node now, wait until it is placed in the final position |
| delayedProcessing.addElement(newChild); |
| //parseDOMNodeDoingXInclude(newChild, parsedDocument, entityResolver); |
| } |
| includeParent->replaceChild(frag, xincludeNode); |
| frag->release(); |
| |
| for(XMLSize_t i=0;i<delayedProcessing.size();i++) |
| { |
| DOMNode* childNode=delayedProcessing.elementAt(i); |
| parseDOMNodeDoingXInclude(childNode, parsedDocument, entityResolver); |
| } |
| 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, |
| href, href); |
| return NULL; |
| } |
| |
| /* 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); |
| |
| DOMDocument *includedNode = NULL; |
| try { |
| 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; |
| } |
| |
| DOMText * |
| 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 XMLSize_t 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); |
| |
| XMLSize_t nRead, nOffset=0; |
| 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()); |
| } |
| |
| /*static*/ bool |
| XIncludeUtils::isXIIncludeDOMNode(DOMNode *node){ |
| const XMLCh *nodeName = node->getLocalName(); |
| const XMLCh *namespaceURI = node->getNamespaceURI(); |
| |
| return isXIIncludeElement(nodeName, namespaceURI); |
| } |
| |
| /*static*/ bool |
| XIncludeUtils::isXIFallbackDOMNode(DOMNode *node){ |
| const XMLCh *nodeName = node->getLocalName(); |
| const XMLCh *namespaceURI = node->getNamespaceURI(); |
| |
| return isXIFallbackElement(nodeName, namespaceURI); |
| } |
| |
| /*static*/ bool |
| 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; |
| } |
| |
| /*static*/ bool |
| 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*/){ |
| return false; |
| } |
| |
| bool |
| 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 (historyCursor == fIncludeHistoryHead){ |
| fIncludeHistoryHead = NULL; |
| } else { |
| penultimateCursor->next = NULL; |
| } |
| |
| 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 XMLFileLoc lineNum = 0; |
| const XMLFileLoc colNum = 0; |
| |
| if (fErrorReporter) |
| { |
| // Load the message into a local for display |
| const XMLSize_t msgSize = 1023; |
| XMLCh errText[msgSize + 1]; |
| |
| if (errorMsg == NULL){ |
| if (gMsgLoader->loadMsg(errorType, errText, msgSize)) |
| { |
| // <TBD> Probably should load a default msg here |
| } |
| } else { |
| if (gMsgLoader->loadMsg(errorType, errText, msgSize, errorMsg)) |
| { |
| // <TBD> Probably should load a default msg here |
| } |
| } |
| |
| fErrorReporter->error(errorType |
| , XMLUni::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 |
| }; |
| |
| } |