| /* |
| * The Apache Software License, Version 1.1 |
| * |
| * |
| * Copyright (c) 1999 The Apache Software Foundation. All rights |
| * reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The end-user documentation included with the redistribution, |
| * if any, must include the following acknowledgment: |
| * "This product includes software developed by the |
| * Apache Software Foundation (http://www.apache.org/)." |
| * Alternately, this acknowledgment may appear in the software itself, |
| * if and wherever such third-party acknowledgments normally appear. |
| * |
| * 4. The names "Xalan" and "Apache Software Foundation" must |
| * not be used to endorse or promote products derived from this |
| * software without prior written permission. For written |
| * permission, please contact apache@apache.org. |
| * |
| * 5. Products derived from this software may not be called "Apache", |
| * nor may "Apache" appear in their name, without prior written |
| * permission of the Apache Software Foundation. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR |
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
| * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation and was |
| * originally based on software copyright (c) 1999, International |
| * Business Machines, Inc., http://www.ibm.com. For more |
| * information on the Apache Software Foundation, please see |
| * <http://www.apache.org/>. |
| * |
| * $ Id: $ |
| * |
| */ |
| |
| #include "Stylesheet.hpp" |
| |
| |
| |
| #include <algorithm> |
| |
| |
| |
| #include <dom/DOM_Node.hpp> |
| #include <dom/DOM_NamedNodeMap.hpp> |
| #include <sax/AttributeList.hpp> |
| |
| |
| |
| #include <DOMSupport/DOMServices.hpp> |
| #include <PlatformSupport/STLHelper.hpp> |
| #include <PlatformSupport/StringTokenizer.hpp> |
| #include <XMLSupport/XMLParserLiaison.hpp> |
| #include <XPath/ElementPrefixResolverProxy.hpp> |
| #include <XPath/XObject.hpp> |
| #include <XPath/XPath.hpp> |
| |
| |
| |
| #include "Constants.hpp" |
| #include "ElemAttributeSet.hpp" |
| #include "ElemTemplate.hpp" |
| #include "ElemTemplateElement.hpp" |
| #include "ElemVariable.hpp" |
| #include "KeyTable.hpp" |
| #include "StylesheetConstructionContext.hpp" |
| #include "StylesheetExecutionContext.hpp" |
| #include "StylesheetRoot.hpp" |
| |
| |
| |
| const Stylesheet::NamespaceVectorType Stylesheet::s_emptyNamespace; |
| |
| |
| |
| Stylesheet::Stylesheet( |
| StylesheetRoot& root, |
| const DOMString& baseIdentifier, |
| StylesheetConstructionContext& constructionContext) : |
| UnimplementedDocument(), |
| UnimplementedElement(&root), |
| m_stylesheetRoot(root), |
| m_baseIdent(baseIdentifier), |
| m_document(), |
| m_key_tables(), |
| m_keyDeclarations(), |
| m_needToBuildKeysTable(false), |
| m_imports(), |
| m_defaultATXpath(0), |
| m_namespaces(), |
| m_namespaceDecls(), |
| m_tablesAreInvalid(true), |
| m_isWrapperless(false), |
| m_wrapperlessTemplate(0), |
| m_extensionNamespaces(), |
| m_firstTemplate(0), |
| m_includeStack(), |
| m_defaultSpace(true), |
| m_whitespacePreservingElements(), |
| m_whitespaceStrippingElements(), |
| m_namedTemplates(), |
| m_topLevelVariables(), |
| m_XSLTVerDeclared(1.0L), |
| m_isRoot(&root == this ? true: false), |
| m_patternTable(), |
| m_attributeSets() |
| { |
| const XMLURL* const url = constructionContext.getURLFromString(m_baseIdent); |
| |
| if (url != 0) |
| { |
| m_includeStack.push_back(url); |
| } |
| |
| // Register the java namespace as being implemented by the xslt-javaclass |
| // engine. Note that there's no real code per se for this extension as the |
| // functions carry the object on which to call etc. and all the logic of |
| // breaking that up is in the xslt-javaclass engine. |
| /* |
| @@ LATER |
| ExtensionFunctionHandler doesn't exist yet |
| string uri("http://xml.apache.org"); |
| ExtensionFunctionHandler fh = |
| new ExtensionFunctionHandler (uri, 0, "xslt-javaclass", 0, 0); |
| XMLParserLiaisonDefault xld = |
| reinterpret_cast<XMLParserLiaisonDefault> m_processor.m_parserLiaison; |
| xld.addExtensionNamespace (uri, fh); |
| */ |
| } |
| |
| |
| |
| Stylesheet::~Stylesheet() |
| { |
| // Clean up all entries in the vector. |
| std::for_each(m_imports.begin(), |
| m_imports.end(), |
| DeleteFunctor<Stylesheet>()); |
| |
| // Clean up the key table vector |
| std::for_each(m_key_tables.begin(), |
| m_key_tables.end(), |
| DeleteFunctor<KeyTable>()); |
| |
| // Clean up the match pattern vector |
| PatternTableMapType::iterator it = m_patternTable.begin(); |
| |
| for ( ; it != m_patternTable.end(); it++) |
| { |
| PatternTableListType& theList = (*it).second; |
| |
| std::for_each(theList.begin(), |
| theList.end(), |
| DeleteFunctor<MatchPattern2>()); |
| } |
| } |
| |
| |
| |
| /** |
| * Process the xsl:key element. |
| * |
| * (Notes to myself) |
| * What we need to do is: |
| * 1) As this function is called, build a table of KeyDeclarations. |
| * 2) During either XML processing, or upon request, walk the XML |
| * document tree, and build a hash table: |
| * a) keyed by name, |
| * b) each with a value of a hashtable, keyed by the value returned by |
| * the use attribute, |
| * c) each with a value that is a nodelist. |
| * Thus, for a given key or keyref, look up hashtable by name, |
| * look up the nodelist by the given reference. |
| */ |
| void |
| Stylesheet::processKeyElement( |
| ElemTemplateElement* nsContext, |
| const AttributeList& atts, |
| StylesheetConstructionContext& constructionContext) |
| // throws XSLProcessorException |
| { |
| const XMLCh* nameAttr = 0; |
| XPath *matchAttr = 0; |
| XPath *useAttr = 0; |
| |
| int nAttrs = atts.getLength(); |
| for(int i = 0; i < nAttrs; i++) |
| { |
| const DOMString aname = atts.getName(i); |
| if (equals(aname, Constants::ATTRNAME_NAME)) |
| nameAttr = atts.getValue(i); |
| else if(equals(aname, Constants::ATTRNAME_MATCH)) |
| { |
| matchAttr = |
| constructionContext.createMatchPattern(DOMString(atts.getValue(i)), |
| *nsContext); |
| } |
| else if(equals(aname, Constants::ATTRNAME_USE)) |
| useAttr = constructionContext.createXPath(atts.getValue(i), |
| *nsContext); |
| else |
| constructionContext.error( |
| DOMString("xsl:key, unrecognized keyword '")+Constants::ATTRNAME_NAME+DOMString("'!")); |
| } |
| if(0 == nameAttr) |
| constructionContext.error(DOMString("xsl:key requires a ")+Constants::ATTRNAME_NAME+" attribute!"); |
| |
| if(0 == matchAttr) |
| constructionContext.error(DOMString("xsl:key requires a ")+Constants::ATTRNAME_MATCH+DOMString(" attribute!")); |
| |
| if(0 == useAttr) |
| constructionContext.error(DOMString("xsl:key requires a ")+Constants::ATTRNAME_USE+DOMString(" attribute!")); |
| |
| m_keyDeclarations.push_back(KeyDeclaration(nameAttr, *matchAttr, *useAttr)); |
| m_needToBuildKeysTable = true; |
| } |
| |
| /** |
| * Push the namespace declarations from the current attribute |
| * list onto the namespace stack. |
| */ |
| void Stylesheet::pushNamespaces(const AttributeList& atts) |
| { |
| int nAttrs = atts.getLength(); |
| NamespaceVectorType namespaces; |
| for(int i = 0; i < nAttrs; i++) |
| { |
| const DOMString aname = atts.getName(i); |
| const DOMString value = atts.getValue(i); |
| bool isPrefix = startsWith(aname, "xmlns:"); |
| if (equals(aname, "xmlns") || isPrefix) |
| { |
| DOMString p = isPrefix ? substring(aname,6) : DOMString(); |
| NameSpace ns(p, value); |
| namespaces.push_back(ns); |
| } |
| } |
| m_namespaces.push_back(namespaces); |
| } |
| |
| void Stylesheet::popNamespaces() |
| { |
| assert(m_namespaces.size()); |
| m_namespaces.pop_back(); |
| } |
| |
| /** |
| * See if this is a xmlns attribute or in a non-XSLT. |
| * |
| * @param attrName Qualified name of attribute. |
| * @param atts The attribute list where the element comes from (not used at |
| * this time). |
| * @param which The index into the attribute list (not used at this time). |
| * @return True if this attribute should not be flagged as an error. |
| */ |
| bool Stylesheet::isAttrOK( |
| const DOMString& attrName, |
| const AttributeList& /* atts */, |
| int /* which */, |
| StylesheetConstructionContext& constructionContext) const |
| { |
| bool attrOK = equals(attrName, "xmlns") || startsWith(attrName, "xmlns:"); |
| |
| if(!attrOK) |
| { |
| const int indexOfNSSep = indexOf(attrName, ':'); |
| |
| if(indexOfNSSep >= 0) |
| { |
| const DOMString prefix = substring(attrName, 0, indexOfNSSep); |
| const DOMString ns = getNamespaceForPrefixFromStack(prefix); |
| |
| attrOK = indexOf(ns, constructionContext.getXSLNameSpaceURLPre()) >= 0; |
| } |
| } |
| |
| return attrOK; |
| } |
| |
| |
| |
| /** |
| * Get the namespace from a qualified name. |
| */ |
| DOMString Stylesheet::getNamespaceFromStack(const DOMString& nodeName) const |
| { |
| int indexOfNSSep = indexOf(nodeName, ':'); |
| DOMString prefix = (indexOfNSSep >= 0) ? |
| substring(nodeName, 0, indexOfNSSep) : DOMString(); |
| return getNamespaceForPrefixFromStack(prefix); |
| } |
| |
| /** |
| * Get the namespace from a prefix. |
| */ |
| DOMString Stylesheet::getNamespaceForPrefix(const DOMString& prefix) const |
| { |
| return QName::getNamespaceForPrefix(m_namespaceDecls, prefix); |
| } |
| |
| /** |
| * Get the namespace from a prefix. |
| */ |
| DOMString Stylesheet::getNamespaceForPrefixFromStack(const DOMString& prefix) const |
| { |
| return QName::getNamespaceForPrefix(m_namespaces, prefix); |
| } |
| |
| bool Stylesheet::getYesOrNo( |
| const DOMString& aname, |
| const DOMString& val, |
| StylesheetConstructionContext& constructionContext) const |
| { |
| if(val.equals(Constants::ATTRVAL_YES)) |
| return true; |
| else if(val.equals(Constants::ATTRVAL_NO)) |
| return false; |
| else |
| constructionContext.error(val+" is unknown value for "+aname); |
| return false; |
| } |
| |
| /** |
| * Add a template to the template list. |
| */ |
| void Stylesheet::addTemplate(ElemTemplate *tmpl) |
| { |
| int pos = 0; |
| if(0 == m_firstTemplate) |
| m_firstTemplate = tmpl; |
| else |
| { |
| // @@ JMD: was: can't make this an UnimplementedElement because there's not a |
| // setNextSibling method -- is this right ?? |
| ElemTemplateElement* next = m_firstTemplate; |
| while(0 != next) |
| { |
| if(0 == next->getNextSibling()) |
| { |
| next->setNextSibling(tmpl); |
| tmpl->setNextSibling(0); // just to play it safe. |
| break; |
| } |
| pos++; |
| |
| next = const_cast<const ElemTemplateElement*>(next)->getNextSibling(); |
| } |
| } |
| |
| if(tmpl->getName().isEmpty() == false) |
| { |
| m_namedTemplates.insert(std::make_pair(tmpl->getName(), tmpl)); |
| } |
| |
| const XPath* xp = tmpl->getMatchPattern(); |
| |
| if(0 != xp) |
| { |
| std::vector<DOMString> strings; |
| xp->getTargetElementStrings(strings); |
| /* Each string has a list of pattern tables associated with it; if the |
| * string is not in the map, then create a list of pattern tables with one |
| * entry for the string, otherwise add to the existing pattern table list |
| * for that string |
| * NOTE: C++ version uses a map keyed on string to a vector of match patterns |
| * while the java version uses a map to a linked list |
| */ |
| if(0 != strings.size()) |
| { |
| int nTargets = strings.size(); |
| for(int stringIndex = 0; stringIndex < nTargets; stringIndex++) |
| { |
| DOMString& target = strings[stringIndex]; |
| MatchPattern2* newMatchPat = |
| new MatchPattern2(xp->getExpression().getCurrentPattern(), |
| *xp, *tmpl, pos, target, *this); |
| |
| // See if there's already one there |
| PatternTableMapType::iterator it = |
| m_patternTable.find(target); |
| // New one |
| if(it == m_patternTable.end()) |
| { |
| PatternTableListType patternlist; |
| patternlist.push_back(newMatchPat); |
| m_patternTable.insert(PatternTableMapType::value_type(target, patternlist)); |
| } |
| else |
| { |
| // Add it to the end of the list |
| PatternTableListType& patternlist = (*it).second; |
| patternlist.push_back(newMatchPat); |
| } |
| } |
| } |
| } |
| } |
| |
| |
| |
| // JMD: removed buildStylesheetTables |
| |
| // JMD: removed initIncludes |
| |
| // JMD: removed initImports |
| |
| |
| /** |
| * Locate a macro via the "name" attribute. |
| * @exception XSLProcessorException thrown if the active ProblemListener and XMLParserLiaison decide |
| * the error condition is severe enough to halt processing. |
| */ |
| ElemTemplateElement* |
| Stylesheet::findNamedTemplate( |
| const DOMString& name, |
| StylesheetExecutionContext& executionContext) const |
| //throws XSLProcessorException |
| { |
| QName qname(name, m_namespaces); |
| |
| return findNamedTemplate(qname, executionContext); |
| } |
| |
| |
| /** |
| * Locate a macro via the "name" attribute. |
| * @exception XSLProcessorException thrown if the active ProblemListener and XMLParserLiaison decide |
| * the error condition is severe enough to halt processing. |
| */ |
| ElemTemplateElement* |
| Stylesheet::findNamedTemplate( |
| const QName& qname, |
| StylesheetExecutionContext& executionContext) const |
| //throws XSLProcessorException |
| { |
| ElemTemplateElement *namedTemplate = 0; |
| |
| ElemTemplateElementMapType::const_iterator it = m_namedTemplates.find(qname); |
| // Look for the template in the imports |
| if(it == m_namedTemplates.end()) |
| { |
| const int nImports = m_imports.size(); |
| for(int i = 0; i < nImports; i++) |
| { |
| const Stylesheet* const stylesheet = m_imports[i]; |
| namedTemplate = stylesheet->findNamedTemplate(qname, executionContext); |
| if(0 != namedTemplate) |
| break; |
| } |
| } |
| else |
| namedTemplate = (*it).second; |
| |
| if(0 == namedTemplate) |
| executionContext.warn(DOMString("Could not find macro def named: ") + |
| qname.getLocalPart()); |
| |
| return namedTemplate; |
| } |
| |
| |
| |
| XObject* |
| Stylesheet::getTopLevelVariable( |
| const DOMString& name, |
| StylesheetExecutionContext& executionContext) const |
| { |
| XObject* theResult = executionContext.getTopLevelVariable(name); |
| |
| if(0 == theResult) |
| { |
| const int nImports = m_imports.size(); |
| |
| for(int i = 0; i < nImports; i++) |
| { |
| Stylesheet* const stylesheet = m_imports[i]; |
| assert(stylesheet != 0); |
| |
| theResult = stylesheet->getTopLevelVariable(name, executionContext); |
| |
| if(0 != theResult) |
| { |
| break; |
| } |
| } |
| } |
| |
| if(0 == theResult) |
| { |
| executionContext.warn(DOMString("Could not find variable def for: ") + name); |
| } |
| |
| return theResult; |
| } |
| |
| |
| ElemTemplateElement* Stylesheet::findTemplate( |
| StylesheetExecutionContext& executionContext, |
| const DOM_Node& sourceTree, |
| const DOM_Node& targetNode) const |
| { |
| Stylesheet* theDummy; |
| |
| return findTemplate(executionContext, sourceTree, targetNode, QName(), false, theDummy); |
| } |
| |
| |
| |
| ElemTemplateElement* |
| Stylesheet::findTemplate( |
| StylesheetExecutionContext& executionContext, |
| const DOM_Node& sourceTree, |
| const DOM_Node& targetNode, |
| const QName& mode, |
| bool useImports, |
| const Stylesheet*& foundStylesheet) const |
| /* |
| throws XSLProcessorException, |
| java.net.MalformedURLException, |
| java.io.FileNotFoundException, |
| java.io.IOException |
| */ |
| { |
| bool usedWildcard = false; |
| if(m_isWrapperless) return m_wrapperlessTemplate; |
| |
| const ElemTemplateElement* bestMatchedRule = 0; |
| const MatchPattern2* bestMatchedPattern = 0; // Syncs with bestMatchedRule |
| |
| PatternTableVectorType conflicts; |
| |
| if(useImports == false) |
| { |
| //odd that this variable is only set, never read |
| double highScore = XPath::s_MatchScoreNone; |
| |
| // Points to the current list of match patterns. Note |
| // that this may point to more than one table. |
| const PatternTableListType* matchPatternList = 0; |
| int targetNodeType = targetNode.getNodeType(); |
| |
| switch(targetNodeType) |
| { |
| case DOM_Node::ELEMENT_NODE: |
| { |
| //java: DOMString targetName = m_processor->getParserLiaison().getLocalNameOfNode(targetNode); |
| DOMString targetName = DOMServices::getLocalNameOfNode(targetNode); |
| matchPatternList = locateMatchPatternList2(targetName, true); |
| } |
| break; |
| |
| case DOM_Node::PROCESSING_INSTRUCTION_NODE: |
| case DOM_Node::ATTRIBUTE_NODE: |
| matchPatternList = locateMatchPatternList2(targetNode.getNodeName(), true); |
| break; |
| |
| case DOM_Node::CDATA_SECTION_NODE: |
| case DOM_Node::TEXT_NODE: |
| matchPatternList = locateMatchPatternList2(XPath::PSEUDONAME_TEXT, false); |
| break; |
| |
| case DOM_Node::COMMENT_NODE: |
| matchPatternList = locateMatchPatternList2(XPath::PSEUDONAME_COMMENT, false); |
| break; |
| |
| case DOM_Node::DOCUMENT_NODE: |
| matchPatternList = locateMatchPatternList2(XPath::PSEUDONAME_ROOT, false); |
| break; |
| |
| case DOM_Node::DOCUMENT_FRAGMENT_NODE: |
| matchPatternList = locateMatchPatternList2(XPath::PSEUDONAME_ANY, false); |
| break; |
| |
| default: |
| { |
| matchPatternList = locateMatchPatternList2(targetNode.getNodeName(), false); |
| } |
| } |
| |
| if (matchPatternList != 0) |
| { |
| DOMString prevPat; |
| |
| // These are iterators into the current table. |
| // Note that we may re-seat these iterators to |
| // point into a different table, if we have |
| // to match wildcards. |
| PatternTableListType::const_iterator theCurrentEntry = |
| matchPatternList->begin(); |
| |
| PatternTableListType::const_iterator theTableEnd = |
| matchPatternList->end(); |
| |
| while(theCurrentEntry != theTableEnd) |
| { |
| const MatchPattern2* matchPat = *theCurrentEntry; |
| |
| const ElemTemplate* rule = &matchPat->getTemplate(); |
| |
| // We'll be needing to match rules according to what |
| // mode we're in. |
| const QName& ruleMode = rule->getMode(); |
| |
| // The logic here should be that if we are not in a mode AND |
| // the rule does not have a node, then go ahead. |
| // OR if we are in a mode, AND the rule has a node, |
| // AND the rules match, then go ahead. |
| |
| bool haveMode = !mode.isEmpty(); |
| bool haveRuleMode = !ruleMode.isEmpty(); |
| |
| if ( (!haveMode && !haveRuleMode) || (haveMode && haveRuleMode && ruleMode.equals(mode))) |
| { |
| const DOMString patterns = matchPat->getPattern(); |
| |
| if((!isEmpty(patterns)) && |
| !(!isEmpty(prevPat) && equals(prevPat, patterns))) |
| { |
| prevPat = patterns; |
| |
| const XPath& xpath = matchPat->getExpression(); |
| |
| double score = |
| xpath.getMatchScore(targetNode, executionContext.getXPathExecutionContext()); |
| |
| if(XPath::s_MatchScoreNone != score) |
| { |
| const double priorityVal = rule->getPriority(); |
| const double priorityOfRule |
| = (XPath::s_MatchScoreNone != priorityVal) |
| ? priorityVal : score; |
| |
| matchPat->setPriority(priorityOfRule); |
| const double priorityOfBestMatched = |
| (0 != bestMatchedPattern) ? |
| bestMatchedPattern->getPriority() : |
| XPath::s_MatchScoreNone; |
| |
| if(priorityOfRule > priorityOfBestMatched) |
| { |
| conflicts.clear(); |
| highScore = score; |
| bestMatchedRule = rule; |
| bestMatchedPattern = matchPat; |
| } |
| else if(priorityOfRule == priorityOfBestMatched) |
| { |
| addObjectIfNotFound(bestMatchedPattern, conflicts); |
| conflicts.push_back(matchPat); |
| highScore = score; |
| bestMatchedRule = rule; |
| bestMatchedPattern = matchPat; |
| } |
| } |
| } // end if(0 != length(patterns)) |
| } // end if if(targetModeString.equals(mode)) |
| |
| theCurrentEntry++; |
| |
| // We also have to consider wildcard matches. |
| if(theCurrentEntry == theTableEnd && |
| equals(matchPat->getTargetString(), "*") == false |
| && (DOM_Node::ELEMENT_NODE == targetNodeType || |
| DOM_Node::ATTRIBUTE_NODE == targetNodeType || |
| DOM_Node::PROCESSING_INSTRUCTION_NODE == targetNodeType) |
| ) |
| { |
| { |
| assert(usedWildcard==false); // Should only be here once ?? |
| usedWildcard = true; |
| PatternTableMapType::const_iterator theTableIterator = |
| m_patternTable.find("*"); |
| |
| assert(m_patternTable.size()); |
| if (theTableIterator != m_patternTable.end()) |
| { |
| // Re-seat the iterators... |
| theCurrentEntry = (*theTableIterator).second.begin(); |
| theTableEnd = (*theTableIterator).second.end(); |
| } |
| } |
| } |
| } // end while |
| } // end if (matchPatternList != 0) |
| } // end if(useImports == false) |
| |
| // @@ JMD: Here we are using the imports anyway if bestMatchedRule is zero, |
| // instead of just doing if (useImports) {...} else. Is this right ?? |
| // Does this assume that bestMatchedRule will always be non-zero exit the |
| // if clause, and, if so, is it an error if it's not ? |
| // else |
| if(0 == bestMatchedRule) |
| { |
| const int nImports = m_imports.size(); |
| |
| for(int i = 0; i < nImports; i++) |
| { |
| const Stylesheet* const stylesheet = |
| m_imports[i]; |
| |
| bestMatchedRule = stylesheet->findTemplate(executionContext, |
| sourceTree, |
| targetNode, |
| mode, |
| false, |
| foundStylesheet); |
| if(0 != bestMatchedRule) |
| break; |
| } |
| } |
| |
| const int nConflicts = conflicts.size(); |
| if(nConflicts > 0) |
| { |
| const bool quietConflictWarnings = executionContext.getQuietConflictWarnings(); |
| DOMString conflictsString = (quietConflictWarnings == false) |
| ? "Specificity conflicts found: " : DOMString(); |
| for(int i = 0; i < nConflicts; i++) |
| { |
| const MatchPattern2* const conflictPat = conflicts[i]; |
| if(0 != i) |
| { |
| if(quietConflictWarnings == false) |
| { |
| conflictsString += ", "; |
| } |
| // Find the furthest one towards the bottom of the document. |
| if(conflictPat->getPositionInStylesheet() > |
| bestMatchedPattern->getPositionInStylesheet()) |
| { |
| bestMatchedPattern = conflictPat; |
| } |
| } |
| else |
| { |
| bestMatchedPattern = conflictPat; |
| } |
| |
| if(quietConflictWarnings == false) |
| { |
| conflictsString += DOMString("\"") + conflictPat->getPattern() + DOMString("\""); |
| } |
| } |
| |
| bestMatchedRule = &bestMatchedPattern->getTemplate(); |
| |
| if(quietConflictWarnings == false) |
| { |
| conflictsString += " "; |
| conflictsString += "Last found in stylesheet will be used."; |
| executionContext.warn(conflictsString); |
| } |
| } |
| |
| if((0 != bestMatchedPattern) && (0 != foundStylesheet)) |
| { |
| foundStylesheet = &bestMatchedPattern->getStylesheet(); |
| } |
| |
| return const_cast<ElemTemplateElement *>(bestMatchedRule); |
| } |
| |
| void Stylesheet::addObjectIfNotFound( |
| const MatchPattern2* thePattern, |
| PatternTableVectorType& theVector) |
| { |
| const int n = theVector.size(); |
| bool addIt = true; |
| for(int i = 0; i < n; i++) |
| { |
| if(theVector[i] == thePattern) |
| { |
| addIt = false; |
| break; |
| } |
| } |
| if(addIt == true) |
| { |
| theVector.push_back(thePattern); |
| } |
| } |
| |
| |
| const Stylesheet::PatternTableListType* |
| Stylesheet::locateMatchPatternList2(DOM_Node sourceNode) const |
| { |
| assert(sourceNode != 0); |
| |
| const PatternTableListType* matchPatternList = 0; |
| |
| switch(sourceNode.getNodeType()) |
| { |
| case DOM_Node::ELEMENT_NODE: |
| { |
| // String targetName = m_parserLiaison.getExpandedElementName((Element)targetNode); |
| const DOMString targetName = |
| DOMServices::getLocalNameOfNode(sourceNode); |
| matchPatternList = locateMatchPatternList2(targetName, true); |
| } |
| break; |
| |
| case DOM_Node::PROCESSING_INSTRUCTION_NODE: |
| case DOM_Node::ATTRIBUTE_NODE: |
| matchPatternList = locateMatchPatternList2(sourceNode.getNodeName(), true); |
| break; |
| |
| case DOM_Node::CDATA_SECTION_NODE: |
| case DOM_Node::TEXT_NODE: |
| matchPatternList = locateMatchPatternList2(Constants::PSEUDONAME_TEXT, false); |
| break; |
| |
| case DOM_Node::COMMENT_NODE: |
| matchPatternList = locateMatchPatternList2(Constants::PSEUDONAME_COMMENT, false); |
| break; |
| |
| case DOM_Node::DOCUMENT_NODE: |
| matchPatternList = locateMatchPatternList2(Constants::PSEUDONAME_ROOT, false); |
| break; |
| |
| default: |
| matchPatternList = locateMatchPatternList2(sourceNode.getNodeName(), false); |
| break; |
| } |
| |
| return matchPatternList; |
| } |
| |
| /** |
| * Given an element type, locate the start of a linked list of |
| * possible tmpl matches. |
| */ |
| const Stylesheet::PatternTableListType* |
| Stylesheet::locateMatchPatternList2( |
| const DOMString& sourceElementType, |
| bool tryWildCard) const |
| { |
| const PatternTableListType* theMatchList = 0; |
| |
| PatternTableMapType::const_iterator i = |
| m_patternTable.find(sourceElementType); |
| |
| if (i != m_patternTable.end()) |
| { |
| theMatchList = &(*i).second; |
| assert(theMatchList != 0); |
| } |
| else if(tryWildCard == true) |
| { |
| i = m_patternTable.find("*"); |
| if (i != m_patternTable.end()) |
| { |
| theMatchList = &(*i).second; |
| assert(theMatchList != 0); |
| } |
| } |
| return theMatchList; |
| } |
| |
| |
| |
| |
| /** |
| * Given a valid element key, return the corresponding node list. |
| */ |
| const NodeRefListBase* |
| Stylesheet::getNodeSetByKey( |
| const DOM_Node& doc, |
| const DOMString& name, |
| const DOMString& ref, |
| const PrefixResolver& resolver, |
| XPathExecutionContext& executionContext) const |
| { |
| const NodeRefListBase *nl = 0; |
| |
| if(0 != m_keyDeclarations.size()) |
| { |
| bool foundDoc = false; |
| const int nKeyTables = m_key_tables.size(); |
| for(int i = 0; i < nKeyTables; i++) |
| { |
| const KeyTable* const kt = m_key_tables[i]; |
| if(doc == kt->getDocKey()) |
| { |
| foundDoc = true; |
| nl = kt->getNodeSetByKey(name, ref); |
| break; |
| } |
| } |
| if((0 == nl) && !foundDoc && m_needToBuildKeysTable) |
| { |
| KeyTable* const kt = |
| new KeyTable(doc, |
| doc, |
| resolver, |
| m_keyDeclarations, |
| executionContext); |
| |
| m_key_tables.push_back(kt); |
| |
| if(doc == kt->getDocKey()) |
| { |
| foundDoc = true; |
| nl = kt->getNodeSetByKey(name, ref); |
| } |
| } |
| } |
| |
| // If the nodelist is null at this point, it should |
| // mean there wasn't an xsl:key declared with the |
| // given name. So go up the import hierarchy and |
| // see if one of the imported stylesheets declared it. |
| if(0 == nl) |
| { |
| const int nImports = m_imports.size(); |
| |
| for(int i = 0; i < nImports; i++) |
| { |
| const Stylesheet* const stylesheet = m_imports[i]; |
| |
| nl = stylesheet->getNodeSetByKey(doc, name, ref, resolver, executionContext); |
| |
| if(0 != nl) |
| break; |
| } |
| } |
| |
| return nl; |
| } |
| |
| |
| |
| /** |
| * Construct a match pattern from a pattern and template. |
| * @param pat For now a Nodelist that contains old-style element patterns. |
| * @param template The node that contains the template for this pattern. |
| * @param isMatchPatternsOnly tells if pat param contains only match |
| * patterns (for compatibility with old syntax). |
| */ |
| Stylesheet::MatchPattern2::MatchPattern2( |
| const DOMString& pat, |
| const XPath& exp, |
| const ElemTemplate& theTemplate, |
| int posInStylesheet, |
| const DOMString& targetString, |
| const Stylesheet& stylesheet) : |
| m_stylesheet(stylesheet), |
| m_targetString(targetString), |
| m_expression(exp), |
| m_posInStylesheet(posInStylesheet), |
| m_priority(XPath::s_MatchScoreNone), |
| m_pattern(pat), |
| m_template(theTemplate) |
| { |
| } |
| |
| |
| Stylesheet::MatchPattern2::~MatchPattern2() |
| { |
| } |
| |
| |
| void Stylesheet::addExtensionNamespace (const DOMString& uri, ExtensionNSHandler* nsh) |
| { |
| m_extensionNamespaces.insert(std::make_pair(uri, nsh)); |
| /* |
| @@ JMD: Not in XMLParserLiaisonDefault |
| XMLParserLiaisonDefault xld = |
| ((XMLParserLiaisonDefault)m_XSLProcessor->m_parserLiaison); |
| xld.addExtensionNamespace (uri, (ExtensionFunctionHandler) nsh); |
| */ |
| } |
| |
| |
| |
| void Stylesheet::pushTopLevelVariables( |
| StylesheetExecutionContext& executionContext, |
| ParamVectorType& topLevelParams) const |
| { |
| // try |
| { |
| int i, nImports = m_imports.size(); |
| for(i = 0; i < nImports; i++) |
| { |
| const Stylesheet* const stylesheet = m_imports[i]; |
| |
| stylesheet->pushTopLevelVariables(executionContext, topLevelParams); |
| } |
| |
| const int nVars = m_topLevelVariables.size(); |
| |
| for(i = 0; i < nVars; i++) |
| { |
| ElemVariable* const var = m_topLevelVariables[i]; |
| |
| bool isParam = |
| Constants::ELEMNAME_PARAMVARIABLE == var->getXSLToken(); |
| |
| if(isParam == true) |
| { |
| isParam = false; |
| |
| const int n = topLevelParams.size(); |
| |
| for(int k = 0; k < n; k++) |
| { |
| Arg& a = topLevelParams[k]; |
| |
| if(a.getName().equals(var->getName())) |
| { |
| isParam = true; |
| |
| XObject *pXO = 0; |
| |
| const DOMString& expr = a.getExpression(); |
| |
| if(length(expr) != 0) |
| { |
| pXO = executionContext.executeXPath(expr, |
| executionContext.getRootDocument(), |
| DOM_UnimplementedElement(const_cast<Stylesheet*>(this))); |
| |
| a.setXObjectPtr(pXO); |
| a.setExpression(0); |
| } |
| |
| executionContext.pushVariable(a.getName(), |
| pXO, |
| DOM_UnimplementedElement(const_cast<Stylesheet*>(this))); |
| break; |
| } |
| } |
| } |
| else |
| { |
| const DOM_Document doc = executionContext.getRootDocument(); |
| |
| var->execute(executionContext, |
| doc, |
| doc, |
| QName()); |
| } |
| } |
| } |
| /* |
| catch(Exception e) |
| { |
| // Turn it into a runtime exception. |
| throw new XSLProcessorException(e); |
| } |
| */ |
| } |
| |
| /** |
| * Add an attribute set to the list. |
| */ |
| void Stylesheet::addAttributeSet( |
| const QName& /*qname */, |
| ElemAttributeSet* attrSet) |
| { |
| m_attributeSets.push_back(attrSet); |
| } |
| |
| /** |
| * Add the attributes from the named attribute sets to the attribute list. |
| * TODO: Error handling for: "It is an error if there are two attribute sets |
| * with the same expanded-name and with equal import precedence and that both |
| * contain the same attribute unless there is a definition of the attribute |
| * set with higher import precedence that also contains the attribute." |
| */ |
| void Stylesheet::applyAttrSets( |
| const QNameVectorType& attributeSetsNames, |
| StylesheetExecutionContext& executionContext, |
| const DOM_Node& sourceTree, |
| const DOM_Node& sourceNode, |
| const QName& mode) const |
| { |
| /* |
| java: Difference from Java code is we have map of qnames to attribute sets |
| instead of a vector where we look for the matching qname in the vector |
| @@ Is this right ?? |
| */ |
| const int nNames = attributeSetsNames.size(); |
| |
| if(0 != nNames) |
| { |
| int i; |
| // Process up the import chain... |
| const int nImports = m_imports.size(); |
| for(i = 0; i < nImports; i++) |
| { |
| const Stylesheet* const stylesheet = m_imports[i]; |
| |
| stylesheet->applyAttrSets(attributeSetsNames, |
| executionContext, sourceTree, sourceNode, mode); |
| } |
| |
| for(i = 0; i < nNames; i++) |
| { |
| const QName& qname = attributeSetsNames[i]; |
| const int nSets = m_attributeSets.size(); |
| |
| for(int k = 0; k < nSets; k++) |
| { |
| const ElemAttributeSet* const attrSet = m_attributeSets[k]; |
| |
| if(qname.equals(attrSet->getQName())) |
| attrSet->execute(executionContext, sourceTree, sourceNode, mode); |
| } |
| } |
| } |
| } |
| |
| |
| |
| const Stylesheet::NamespaceVectorType& Stylesheet::getNamespaceDecls() const |
| { |
| return m_namespaceDecls; |
| } |
| |
| void Stylesheet::setNamespaceDecls(const NamespaceVectorType& ns) |
| { |
| m_namespaceDecls = ns; |
| } |
| |
| |
| short |
| Stylesheet::getNodeType() |
| { |
| return DOM_Node::DOCUMENT_NODE; |
| } |
| |
| |
| |
| const Stylesheet::NamespaceVectorType& |
| Stylesheet::getCurrentNamespace() const |
| { |
| if (m_namespaces.size() > 0) |
| return m_namespaces.back(); |
| else |
| return s_emptyNamespace; |
| } |
| |
| |
| //////////////////////////////////////////////////////////////////// |
| // JAVA Code not implemented |
| //////////////////////////////////////////////////////////////////// |
| |
| #ifdef JAVANOTIMPLEMENTED |
| |
| /** |
| * Extension to be used when serializing to disk. |
| */ |
| const std::string STYLESHEET_EXT(".lxc"); |
| |
| /** |
| * Read the stylesheet from a serialization stream. |
| */ |
| private void readObject(ObjectInputStream stream) |
| throws IOException |
| { |
| try |
| { |
| stream.defaultReadObject(); |
| } |
| catch(ClassNotFoundException cnfe) |
| { |
| throw new XSLProcessorException(cnfe); |
| } |
| m_includeStack.clear(); |
| } |
| |
| private void writeObject(ObjectOutputStream stream) |
| throws IOException |
| { |
| stream.defaultWriteObject(); |
| } |
| |
| #endif // JAVANOTIMPLEMENTED |
| |
| /* |
| * $ Log: $ |
| */ |