Implement xsl:import. This dosn't implement any import precedence but gives us the infrastructure to do so.

b=78068 r=peterv sr=shaver
This commit is contained in:
sicking%bigfoot.com 2001-09-09 16:09:57 +00:00
parent 9d44bea1f1
commit 1112d5188a
10 changed files with 287 additions and 210 deletions

View File

@ -65,7 +65,7 @@ const short URIUtils::PATH_MODE = 4;
* found
**/
istream* URIUtils::getInputStream
(String& href, String& errMsg)
(const String& href, String& errMsg)
{
istream* inStream = 0;

View File

@ -78,7 +78,7 @@ public:
static istream* getInputStream
(String& href, String& errMsg);
(const String& href, String& errMsg);
/**
* Returns the document base of the href argument

View File

@ -65,16 +65,12 @@ XMLParser::~XMLParser()
} //-- ~XMLParser
Document* XMLParser::getDocumentFromURI(const String& href,
const String& baseUri,
Document* aLoader,
String& errMsg)
{
String documentURL;
URIUtils::resolveHref(href, baseUri, documentURL);
#ifndef TX_EXE
nsCOMPtr<nsIURI> documentURI;
nsresult rv = NS_NewURI(getter_AddRefs(documentURI), documentURL.getConstNSString());
nsresult rv = NS_NewURI(getter_AddRefs(documentURI), href.getConstNSString());
NS_ENSURE_SUCCESS(rv, NULL);
nsCOMPtr<nsISyncLoader> loader = do_CreateInstance(TRANSFORMIIX_SYNCLOADER_CONTRACTID, &rv);
@ -92,11 +88,11 @@ Document* XMLParser::getDocumentFromURI(const String& href,
return new Document(theDocument);
#else
istream* xslInput = URIUtils::getInputStream(documentURL, errMsg);
istream* xslInput = URIUtils::getInputStream(href, errMsg);
Document* resultDoc = 0;
if ( xslInput ) {
resultDoc = parse(*xslInput, documentURL);
resultDoc = parse(*xslInput, href);
delete xslInput;
}
if (!resultDoc) {

View File

@ -67,7 +67,7 @@ class XMLParser
XMLParser();
~XMLParser();
Document* getDocumentFromURI(const String& href, const String& baseUri, Document* aLoader, String& errMsg);
Document* getDocumentFromURI(const String& href, Document* aLoader, String& errMsg);
#ifdef TX_EXE
Document* parse(istream& inputStream, const String& uri);
const String& getErrorString();

View File

@ -42,6 +42,7 @@
#include "VariableBinding.h"
#include "ExprResult.h"
#include "Names.h"
#include "XMLParser.h"
#ifndef TX_EXE
// #include "nslog.h"
// #define PRINTF NS_LOG_PRINTF(XPATH)
@ -91,19 +92,6 @@ ProcessorState::~ProcessorState() {
delete (NamedMap*) variableSets.pop();
}
//-- delete includes
StringList* keys = includes.keys();
StringListIterator* iter = keys->iterator();
while (iter->hasNext()) {
String* key = iter->next();
TxObjectWrapper* objWrapper
= (TxObjectWrapper*)includes.remove(*key);
delete (Document*)objWrapper->object;
delete objWrapper;
}
delete iter;
delete keys;
//-- clean up XSLT actions stack
while (currentAction) {
XSLTAction* item = currentAction;
@ -112,6 +100,18 @@ ProcessorState::~ProcessorState() {
item->prev = 0;
delete item;
}
// Delete all importFrames
txListIterator iter(&importFrames);
while (iter.hasNext())
delete (ImportFrame*)iter.next();
// Make sure that xslDocument and mSourceDocument isn't deleted along with
// the rest of the documents in the loadedDocuments hash
loadedDocuments.remove(xslDocument->getBaseURI());
loadedDocuments.remove(mSourceDocument->getBaseURI());
} //-- ~ProcessorState
@ -160,18 +160,6 @@ void ProcessorState::addErrorObserver(ErrorObserver& errorObserver) {
errorObservers.add(&errorObserver);
} //-- addErrorObserver
/**
* Adds the given XSL document to the list of includes
* The href is used as a key for the include, to prevent
* including the same document more than once
**/
void ProcessorState::addInclude(const String& href, Document* xslDocument) {
TxObjectWrapper* objWrapper = new TxObjectWrapper();
objWrapper->object = xslDocument;
includes.put(href, objWrapper);
} //-- addInclude
/**
* Adds the given template to the list of templates to process
* @param xslTemplate, the Element to add as a template
@ -307,6 +295,78 @@ Node* ProcessorState::copyNode(Node* node) {
return 0;
} //-- copyNode
/*
* Retrieve the document designated by the URI uri, using baseUri as base URI.
* Parses it as an XML document, and returns it. If a fragment identifier is
* supplied, the element with seleced id is returned.
* The returned document is owned by the ProcessorState
*
* @param uri the URI of the document to retrieve
* @param baseUri the base URI used to resolve the URI if uri is relative
* @return loaded document or element pointed to by fragment identifier. If
* loading or parsing fails NULL will be returned.
*/
Node* ProcessorState::retrieveDocument(const String& uri, const String& baseUri)
{
String absUrl, frag, docUrl;
URIUtils::resolveHref(uri, baseUri, absUrl);
URIUtils::getFragmentIdentifier(absUrl, frag);
URIUtils::getDocumentURI(absUrl, docUrl);
// try to get already loaded document
Document* xmlDoc = (Document*)loadedDocuments.get(docUrl);
if (!xmlDoc) {
// open URI
String errMsg;
XMLParser xmlParser;
NS_ASSERTION(currentAction && currentAction->node,
"missing currentAction->node");
Document* loaderDoc;
if (currentAction->node->getNodeType() == Node::DOCUMENT_NODE)
loaderDoc = (Document*)currentAction->node;
else
loaderDoc = currentAction->node->getOwnerDocument();
xmlDoc = xmlParser.getDocumentFromURI(docUrl, loaderDoc, errMsg);
if (!xmlDoc) {
String err("Couldn't load document '");
err.append(docUrl);
err.append("': ");
err.append(errMsg);
recieveError(err, ErrorObserver::WARNING);
return NULL;
}
// add to list of documents
loadedDocuments.put(docUrl, xmlDoc);
}
// return element with supplied id if supplied
if (frag.length())
return xmlDoc->getElementById(frag);
return xmlDoc;
}
/*
* Return stack of urls of currently entered stylesheets
*/
Stack* ProcessorState::getEnteredStylesheets()
{
return &enteredStylesheets;
}
/*
* Return list of import containers
*/
List* ProcessorState::getImportFrames()
{
return &importFrames;
}
/**
* Finds a template for the given Node. Only templates with
* a mode attribute equal to the given mode will be searched.
@ -394,19 +454,6 @@ Stack* ProcessorState::getDefaultNSURIStack() {
return &defaultNameSpaceURIStack;
} //-- getDefaultNSURIStack
/**
* @return the included xsl document that was associated with the
* given href, or null if no document is found
**/
Document* ProcessorState::getInclude(const String& href) {
TxObjectWrapper* objWrapper = (TxObjectWrapper*)includes.get(href);
Document* doc = 0;
if (objWrapper) {
doc = (Document*) objWrapper->object;
}
return doc;
} //-- getInclude(String)
Expr* ProcessorState::getExpr(const String& pattern) {
// NS_IMPL_LOG(XPATH)
// PRINTF("Resolving XPath Expr %s",pattern.toCharArray());
@ -668,30 +715,6 @@ void ProcessorState::shouldStripSpace(String& names, MBool shouldStrip) {
} //-- stripSpace
/**
* Adds a document to set of loaded documents
**/
void ProcessorState::addLoadedDocument(Document* doc, String& url) {
String docUrl;
URIUtils::getDocumentURI(url, docUrl);
loadedDocuments.put(docUrl, doc);
}
/**
* Returns a loaded document given it's url. NULL if no such doc exists
**/
Document* ProcessorState::getLoadedDocument(String& url) {
String docUrl;
URIUtils::getDocumentURI(url, docUrl);
if ((mMainStylesheetURL.length() > 0) && docUrl.isEqual(mMainStylesheetURL)) {
return xslDocument;
}
else if ((mMainSourceURL.length() > 0) && docUrl.isEqual(mMainSourceURL)) {
return mSourceDocument;
}
return (Document*)loadedDocuments.get(docUrl);
}
/**
* Adds the supplied xsl:key to the set of keys
**/
@ -987,11 +1010,11 @@ void ProcessorState::initialize() {
//-- determine xsl properties
Element* element = NULL;
if (mSourceDocument) {
mMainSourceURL = mSourceDocument->getBaseURI();
loadedDocuments.put(mSourceDocument->getBaseURI(), mSourceDocument);
}
if (xslDocument) {
element = xslDocument->getDocumentElement();
mMainStylesheetURL = xslDocument->getBaseURI();
loadedDocuments.put(xslDocument->getBaseURI(), xslDocument);
}
if ( element ) {

View File

@ -79,14 +79,6 @@ public:
**/
void addErrorObserver(ErrorObserver& errorObserver);
/**
* Adds the given XSL document to the list of includes
* The href is used as a key for the include, to prevent
* including the same document more than once
**/
void addInclude(const String& href, Document* xslDocument);
/**
* Adds the given template to the list of templates to process
* @param xslTemplate, the Element to add as a template
@ -126,12 +118,6 @@ public:
**/
Stack* getDefaultNSURIStack();
/**
* @return the included xsl document that was associated with the
* given href, or null if no document is found
**/
Document* getInclude(const String& href);
/**
* Returns the template associated with the given name, or
* null if not template is found
@ -179,6 +165,45 @@ public:
String& getXSLNamespace();
/**
* Retrieve the document designated by the URI uri, using baseUri as base URI.
* Parses it as an XML document, and returns it. If a fragment identifier is
* supplied, the element with seleced id is returned.
* The returned document is owned by the ProcessorState
*
* @param uri the URI of the document to retrieve
* @param baseUri the base URI used to resolve the URI if uri is relative
* @return loaded document or element pointed to by fragment identifier. If
* loading or parsing fails NULL will be returned.
**/
Node* retrieveDocument(const String& uri, const String& baseUri);
/*
* Return stack of urls of currently entered stylesheets
*/
Stack* getEnteredStylesheets();
/**
* Contain information that is import precedence dependant.
**/
struct ImportFrame {
// The following stuff is missing here:
// ImportFrame(?) for xsl:apply-imports
// Nametests for xsl:strip-space and xsl:preserve-space
// Template rules
// Names templates
// Namespace aliases (xsl:namespace-alias)
// Named attribute sets
// Toplevel variables/parameters
// Output specifier (xsl:output)
};
/**
* Return list of import containers
**/
List* getImportFrames();
/**
* Finds a template for the given Node. Only templates without
* a mode attribute will be searched.
@ -249,16 +274,6 @@ public:
**/
void shouldStripSpace(String& names, MBool shouldStrip);
/**
* Adds a document to set of loaded documents
**/
void addLoadedDocument(Document* doc, String& location);
/**
* Returns a loaded document given it's url. NULL if no such doc exists
**/
Document* getLoadedDocument(String& url);
/**
* Adds the supplied xsl:key to the set of keys
**/
@ -367,10 +382,14 @@ private:
List errorObservers;
/**
* A map for included stylesheets
* (used for deletion when processing is done)
* Stack of URIs for currently entered stylesheets
**/
NamedMap includes;
Stack enteredStylesheets;
/**
* List of import containers. Sorted by ascending import precedence
**/
List importFrames;
/**
* A map for named attribute sets
@ -409,7 +428,8 @@ private:
NodeSet templates;
/**
* the set of loaded documents
* The set of loaded documents. This includes both document() loaded
* documents and xsl:include/xsl:import'ed documents.
**/
NamedMap loadedDocuments;
@ -423,8 +443,6 @@ private:
Stack nodeSetStack;
Document* mSourceDocument;
Document* xslDocument;
String mMainSourceURL;
String mMainStylesheetURL;
Document* resultDocument;
NamedMap exprHash;
NamedMap patternExprHash;

View File

@ -108,6 +108,7 @@ XSLTProcessor::XSLTProcessor() {
xslTypes.put(ELEMENT, new XSLType(XSLType::ELEMENT));
xslTypes.put(FOR_EACH, new XSLType(XSLType::FOR_EACH));
xslTypes.put(IF, new XSLType(XSLType::IF));
xslTypes.put(IMPORT, new XSLType(XSLType::IMPORT));
xslTypes.put(INCLUDE, new XSLType(XSLType::INCLUDE));
xslTypes.put(KEY, new XSLType(XSLType::KEY));
xslTypes.put(MESSAGE, new XSLType(XSLType::MESSAGE));
@ -394,13 +395,17 @@ Document* XSLTProcessor::process(istream& xmlInput, String& xmlFilename) {
**/
void XSLTProcessor::processTopLevel(Document* aSource,
Document* aStylesheet,
ListIterator* importFrame,
ProcessorState* aPs)
{
NS_ASSERTION(aStylesheet, "processTopLevel called without stylesheet");
if (!aStylesheet)
return;
processTopLevel(aSource, aStylesheet->getDocumentElement(), aPs);
processTopLevel(aSource,
aStylesheet->getDocumentElement(),
importFrame,
aPs);
}
/**
@ -408,6 +413,7 @@ void XSLTProcessor::processTopLevel(Document* aSource,
**/
void XSLTProcessor::processTopLevel(Document* aSource,
Element* aStylesheet,
ListIterator* importFrame,
ProcessorState* aPs)
{
// Index templates and process top level xsl elements
@ -416,8 +422,39 @@ void XSLTProcessor::processTopLevel(Document* aSource,
return;
NS_ASSERTION(aSource, "processTopLevel called without source document");
MBool importsDone = MB_FALSE;
Node* node = aStylesheet->getFirstChild();
while (node && !importsDone) {
if (node->getNodeType() == Node::ELEMENT_NODE) {
Element* element = (Element*)node;
String name = element->getNodeName();
switch (getElementType(name, aPs)) {
case XSLType::IMPORT :
{
String href;
URIUtils::resolveHref(element->getAttribute(HREF_ATTR),
element->getBaseURI(),
href);
importFrame->addAfter(new ProcessorState::ImportFrame);
importFrame->next();
processInclude(href, aSource, importFrame, aPs);
importFrame->previous();
break;
}
default:
importsDone = MB_TRUE;
break;
}
}
if (!importsDone)
node = node->getNextSibling();
}
while (node) {
if (node->getNodeType() == Node::ELEMENT_NODE) {
Element* element = (Element*)node;
@ -439,41 +476,20 @@ void XSLTProcessor::processTopLevel(Document* aSource,
bindVariable(name, exprResult, MB_TRUE, aPs);
break;
}
case XSLType::IMPORT :
{
notifyError("xsl:import only allowed at top of stylesheet");
break;
}
case XSLType::INCLUDE :
{
String href;
URIUtils::resolveHref(element->getAttribute(HREF_ATTR),
element->getBaseURI(),
href);
String href = element->getAttribute(HREF_ATTR);
//-- Read in XSL document
if (aPs->getInclude(href)) {
/* XXX this is wrong, it's allowed to include one stylesheet multiple
times but we should build some sort of stack to make sure that we
don't have circular inclusions */
String err("stylesheet already included: ");
err.append(href);
notifyError(err, ErrorObserver::WARNING);
break;
}
String errMsg;
XMLParser xmlParser;
Document* xslDoc = xmlParser.getDocumentFromURI(href, element->getBaseURI(), aStylesheet->getOwnerDocument(), errMsg);
if (!xslDoc) {
String err("error including XSL stylesheet: ");
err.append(href);
err.append("; ");
err.append(errMsg);
notifyError(err);
}
else {
//-- add stylesheet to list of includes
aPs->addInclude(href, xslDoc);
processTopLevel(aSource, xslDoc, aPs);
}
processInclude(href, aSource, importFrame, aPs);
break;
}
case XSLType::KEY :
{
@ -567,6 +583,64 @@ void XSLTProcessor::processTopLevel(Document* aSource,
} //-- process(Document, ProcessorState)
/*
* Processes an include or import stylesheet
* @param aHref URI of stylesheet to process
* @param aSource source document
* @param aImportFrame current importFrame iterator
* @param aPs current ProcessorState
*/
void XSLTProcessor::processInclude(String& aHref,
Document* aSource,
ListIterator* aImportFrame,
ProcessorState* aPs)
{
// make sure the include isn't included yet
StackIterator* iter = aPs->getEnteredStylesheets()->iterator();
if (!iter) {
// XXX report out of memory
return;
}
while(iter->hasNext()) {
if (((String*)iter->next())->isEqual(aHref)) {
String err("Stylesheet includes itself. URI: ");
err.append(aHref);
notifyError(err);
delete iter;
return;
}
}
aPs->getEnteredStylesheets()->push(&aHref);
delete iter;
// Load XSL document
Node* stylesheet = aPs->retrieveDocument(aHref, NULL_STRING);
if (!stylesheet) {
String err("Unable to load included stylesheet ");
err.append(aHref);
notifyError(err);
aPs->getEnteredStylesheets()->pop();
return;
}
switch(stylesheet->getNodeType()) {
case Node::DOCUMENT_NODE :
processTopLevel(aSource, (Document*)stylesheet, aImportFrame, aPs);
break;
case Node::ELEMENT_NODE :
processTopLevel(aSource, (Element*)stylesheet, aImportFrame, aPs);
break;
default:
// This should never happen
String err("Unsupported fragment identifier");
notifyError(err);
break;
}
aPs->getEnteredStylesheets()->pop();
}
#ifdef TX_EXE
/**
* Processes the given XML Document using the given XSL document
@ -597,7 +671,10 @@ Document* XSLTProcessor::process
//- index templates and process top level xsl elements -/
//-------------------------------------------------------/
processTopLevel(&xmlDocument, &xslDocument, &ps);
ListIterator importFrame(ps.getImportFrames());
importFrame.addAfter(new ProcessorState::ImportFrame);
importFrame.next();
processTopLevel(&xmlDocument, &xslDocument, &importFrame, &ps);
//----------------------------------------/
//- Process root of XML source document -/
@ -640,7 +717,10 @@ void XSLTProcessor::process
//- index templates and process top level xsl elements -/
//-------------------------------------------------------/
processTopLevel(&xmlDocument, &xslDocument, &ps);
ListIterator importFrame(ps.getImportFrames());
importFrame.addAfter(new ProcessorState::ImportFrame);
importFrame.next();
processTopLevel(&xmlDocument, &xslDocument, &importFrame, &ps);
//----------------------------------------/
//- Process root of XML source document -/
@ -1946,7 +2026,10 @@ XSLTProcessor::TransformDocument(nsIDOMNode* aSourceDOM,
//------------------------------------------------------/
//- index templates and process top level xsl elements -/
//------------------------------------------------------/
processTopLevel(sourceDocument, xslDocument, ps);
ListIterator importFrame(ps->getImportFrames());
importFrame.addAfter(new ProcessorState::ImportFrame);
importFrame.next();
processTopLevel(sourceDocument, xslDocument, &importFrame, ps);
//---------------------------------------/
//- Process root of XML source document -/

View File

@ -343,8 +343,27 @@ private:
**/
void processDefaultTemplate(Node* node, ProcessorState* ps, String* mode);
void processTopLevel(Document* aSource, Document* aStylesheet, ProcessorState* aPs);
void processTopLevel(Document* aSource, Element* aStylesheet, ProcessorState* aPs);
void processTopLevel(Document* aSource,
Document* aStylesheet,
ListIterator* importFrame,
ProcessorState* aPs);
void processTopLevel(Document* aSource,
Element* aStylesheet,
ListIterator* importFrame,
ProcessorState* aPs);
/*
* Processes an include or import stylesheet
* @param aHref URI of stylesheet to process
* @param aSource source document
* @param aImportFrame current importFrame iterator
* @param aPs current ProcessorState
*/
void processInclude(String& aHref,
Document* aSource,
ListIterator* aImportFrame,
ProcessorState* aPs);
ExprResult* processVariable(Node* node, Element* xslVariable, ProcessorState* ps);
@ -375,6 +394,7 @@ public:
COPY_OF,
ELEMENT,
IF,
IMPORT,
INCLUDE,
KEY,
FOR_EACH,

View File

@ -37,9 +37,7 @@
*/
#include "XSLTFunctions.h"
#include "XMLParser.h"
#include "XMLDOMUtils.h"
#include "URIUtils.h"
#include "Names.h"
/*
@ -108,23 +106,23 @@ ExprResult* DocumentFunctionCall::evaluate(Node* context, ContextState* cs)
if (!baseURISet) {
// if the second argument wasn't specified, use
// the baseUri of node itself
retrieveDocument(uriStr, node->getBaseURI(), *nodeSet, cs);
nodeSet->add(mProcessorState->retrieveDocument(uriStr, node->getBaseURI()));
}
else {
retrieveDocument(uriStr, baseURI, *nodeSet, cs);
nodeSet->add(mProcessorState->retrieveDocument(uriStr, baseURI));
}
}
}
else {
// The first argument is not a NodeSet
String uriStr;
evaluateToString(param1, context, cs, uriStr);
exprResult1->stringValue(uriStr);
if (!baseURISet) {
Node* xsltElement = mProcessorState->peekAction();
retrieveDocument(uriStr, xsltElement->getBaseURI(), *nodeSet, cs);
nodeSet->add(mProcessorState->retrieveDocument(uriStr, xsltElement->getBaseURI()));
}
else {
retrieveDocument(uriStr, baseURI, *nodeSet, cs);
nodeSet->add(mProcessorState->retrieveDocument(uriStr, baseURI));
}
}
delete exprResult1;
@ -133,63 +131,3 @@ ExprResult* DocumentFunctionCall::evaluate(Node* context, ContextState* cs)
return nodeSet;
}
/**
* Retrieve the document designated by the URI uri, using baseUri as base URI if
* necessary, parses it as an XML document, and append the resulting document node
* to resultNodeSet.
*
* @param uri the URI of the document to retrieve
* @param baseUri the base URI used to resolve the URI if uri is relative
* @param resultNodeSet the NodeSet to append the document to
* @param cs the ContextState, used for reporting errors
*/
void DocumentFunctionCall::retrieveDocument(const String& uri,
const String& baseUri,
NodeSet& resultNodeSet,
ContextState* cs)
{
String absUrl, frag;
Document* xmlDoc;
URIUtils::resolveHref(uri, baseUri, absUrl);
URIUtils::getFragmentIdentifier(absUrl, frag);
// try to get already loaded document
xmlDoc = mProcessorState->getLoadedDocument(absUrl);
if (!xmlDoc) {
// open URI
String errMsg;
XMLParser xmlParser;
Node* xsltElement;
xsltElement = mProcessorState->peekAction();
if (!xsltElement) {
// no xslt element
return;
}
xmlDoc = xmlParser.getDocumentFromURI(absUrl, "", xsltElement->getOwnerDocument(), errMsg);
if (!xmlDoc) {
String err("error in document() function: ");
err.append(errMsg);
cs->recieveError(err);
return;
}
// add to ProcessorState list of documents
mProcessorState->addLoadedDocument(xmlDoc, absUrl);
}
// append the document or the fragment to resultNodeSet
if (frag.length() > 0) {
Node* node = xmlDoc->getElementById(frag);
if (node) {
resultNodeSet.add(node);
}
}
else {
resultNodeSet.add(xmlDoc);
}
}

View File

@ -62,7 +62,6 @@ public:
virtual ExprResult* evaluate(Node* context, ContextState* cs);
private:
void retrieveDocument(const String& uri,const String& baseUri, NodeSet &resultNodeSet, ContextState* cs);
ProcessorState* mProcessorState;
};