| /* |
| * 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$ |
| */ |
| |
| |
| // --------------------------------------------------------------------------- |
| // Includes |
| // --------------------------------------------------------------------------- |
| #include <xercesc/internal/SGXMLScanner.hpp> |
| #include <xercesc/util/RuntimeException.hpp> |
| #include <xercesc/util/UnexpectedEOFException.hpp> |
| #include <xercesc/util/XMLUri.hpp> |
| #include <xercesc/framework/LocalFileInputSource.hpp> |
| #include <xercesc/framework/URLInputSource.hpp> |
| #include <xercesc/framework/XMLDocumentHandler.hpp> |
| #include <xercesc/framework/XMLEntityHandler.hpp> |
| #include <xercesc/framework/XMLPScanToken.hpp> |
| #include <xercesc/framework/MemoryManager.hpp> |
| #include <xercesc/framework/XMLGrammarPool.hpp> |
| #include <xercesc/framework/psvi/PSVIElement.hpp> |
| #include <xercesc/framework/psvi/PSVIHandler.hpp> |
| #include <xercesc/framework/psvi/PSVIAttributeList.hpp> |
| #include <xercesc/framework/psvi/XSAnnotation.hpp> |
| #include <xercesc/internal/EndOfEntityException.hpp> |
| #include <xercesc/validators/common/ContentLeafNameTypeVector.hpp> |
| #include <xercesc/validators/schema/SchemaValidator.hpp> |
| #include <xercesc/validators/schema/TraverseSchema.hpp> |
| #include <xercesc/validators/schema/XSDDOMParser.hpp> |
| #include <xercesc/validators/schema/SubstitutionGroupComparator.hpp> |
| #include <xercesc/validators/schema/XMLSchemaDescriptionImpl.hpp> |
| #include <xercesc/validators/schema/identity/IdentityConstraintHandler.hpp> |
| #include <xercesc/validators/schema/identity/IC_Selector.hpp> |
| #include <xercesc/validators/schema/identity/ValueStore.hpp> |
| #include <xercesc/util/OutOfMemoryException.hpp> |
| #include <xercesc/util/XMLStringTokenizer.hpp> |
| |
| XERCES_CPP_NAMESPACE_BEGIN |
| |
| inline XMLAttDefList& getAttDefList(ComplexTypeInfo* currType, XMLElementDecl* elemDecl); |
| |
| |
| typedef JanitorMemFunCall<SGXMLScanner> CleanupType; |
| typedef JanitorMemFunCall<ReaderMgr> ReaderMgrResetType; |
| |
| |
| // --------------------------------------------------------------------------- |
| // SGXMLScanner: Constructors and Destructor |
| // --------------------------------------------------------------------------- |
| SGXMLScanner::SGXMLScanner( XMLValidator* const valToAdopt |
| , GrammarResolver* const grammarResolver |
| , MemoryManager* const manager) : |
| |
| XMLScanner(valToAdopt, grammarResolver, manager) |
| , fSeeXsi(false) |
| , fGrammarType(Grammar::UnKnown) |
| , fElemStateSize(16) |
| , fElemState(0) |
| , fElemLoopState(0) |
| , fContent(1023, manager) |
| , fEntityTable(0) |
| , fRawAttrList(0) |
| , fRawAttrColonListSize(32) |
| , fRawAttrColonList(0) |
| , fSchemaGrammar(0) |
| , fSchemaValidator(0) |
| , fICHandler(0) |
| , fElemNonDeclPool(0) |
| , fElemCount(0) |
| , fAttDefRegistry(0) |
| , fUndeclaredAttrRegistry(0) |
| , fPSVIAttrList(0) |
| , fModel(0) |
| , fPSVIElement(0) |
| , fErrorStack(0) |
| , fSchemaInfoList(0) |
| , fCachedSchemaInfoList(0) |
| { |
| CleanupType cleanup(this, &SGXMLScanner::cleanUp); |
| |
| try |
| { |
| commonInit(); |
| } |
| catch(const OutOfMemoryException&) |
| { |
| // Don't cleanup when out of memory, since executing the |
| // code can cause problems. |
| cleanup.release(); |
| |
| throw; |
| } |
| |
| cleanup.release(); |
| } |
| |
| SGXMLScanner::SGXMLScanner( XMLDocumentHandler* const docHandler |
| , DocTypeHandler* const docTypeHandler |
| , XMLEntityHandler* const entityHandler |
| , XMLErrorReporter* const errHandler |
| , XMLValidator* const valToAdopt |
| , GrammarResolver* const grammarResolver |
| , MemoryManager* const manager) : |
| |
| XMLScanner(docHandler, docTypeHandler, entityHandler, errHandler, valToAdopt, grammarResolver, manager) |
| , fSeeXsi(false) |
| , fGrammarType(Grammar::UnKnown) |
| , fElemStateSize(16) |
| , fElemState(0) |
| , fElemLoopState(0) |
| , fContent(1023, manager) |
| , fEntityTable(0) |
| , fRawAttrList(0) |
| , fRawAttrColonListSize(32) |
| , fRawAttrColonList(0) |
| , fSchemaGrammar(0) |
| , fSchemaValidator(0) |
| , fICHandler(0) |
| , fElemNonDeclPool(0) |
| , fElemCount(0) |
| , fAttDefRegistry(0) |
| , fUndeclaredAttrRegistry(0) |
| , fPSVIAttrList(0) |
| , fModel(0) |
| , fPSVIElement(0) |
| , fErrorStack(0) |
| , fSchemaInfoList(0) |
| , fCachedSchemaInfoList(0) |
| { |
| CleanupType cleanup(this, &SGXMLScanner::cleanUp); |
| |
| try |
| { |
| commonInit(); |
| } |
| catch(const OutOfMemoryException&) |
| { |
| // Don't cleanup when out of memory, since executing the |
| // code can cause problems. |
| cleanup.release(); |
| |
| throw; |
| } |
| |
| cleanup.release(); |
| } |
| |
| SGXMLScanner::~SGXMLScanner() |
| { |
| cleanUp(); |
| } |
| |
| // --------------------------------------------------------------------------- |
| // XMLScanner: Getter methods |
| // --------------------------------------------------------------------------- |
| NameIdPool<DTDEntityDecl>* SGXMLScanner::getEntityDeclPool() |
| { |
| return 0; |
| } |
| |
| const NameIdPool<DTDEntityDecl>* SGXMLScanner::getEntityDeclPool() const |
| { |
| return 0; |
| } |
| |
| // --------------------------------------------------------------------------- |
| // SGXMLScanner: Main entry point to scan a document |
| // --------------------------------------------------------------------------- |
| void SGXMLScanner::scanDocument(const InputSource& src) |
| { |
| // Bump up the sequence id for this parser instance. This will invalidate |
| // any previous progressive scan tokens. |
| fSequenceId++; |
| |
| ReaderMgrResetType resetReaderMgr(&fReaderMgr, &ReaderMgr::reset); |
| |
| try |
| { |
| // Reset the scanner and its plugged in stuff for a new run. This |
| // resets all the data structures, creates the initial reader and |
| // pushes it on the stack, and sets up the base document path. |
| scanReset(src); |
| |
| // If we have a document handler, then call the start document |
| if (fDocHandler) |
| fDocHandler->startDocument(); |
| |
| // Scan the prolog part, which is everything before the root element |
| // including the DTD subsets. |
| scanProlog(); |
| |
| // If we got to the end of input, then its not a valid XML file. |
| // Else, go on to scan the content. |
| if (fReaderMgr.atEOF()) |
| { |
| emitError(XMLErrs::EmptyMainEntity); |
| } |
| else |
| { |
| // Scan content, and tell it its not an external entity |
| if (scanContent()) |
| { |
| // Do post-parse validation if required |
| if (fValidate) |
| { |
| // We handle ID reference semantics at this level since |
| // its required by XML 1.0. |
| checkIDRefs(); |
| |
| // Then allow the validator to do any extra stuff it wants |
| // fValidator->postParseValidation(); |
| } |
| |
| // That went ok, so scan for any miscellaneous stuff |
| if (!fReaderMgr.atEOF()) |
| scanMiscellaneous(); |
| } |
| } |
| |
| // If we have a document handler, then call the end document |
| if (fDocHandler) |
| fDocHandler->endDocument(); |
| } |
| // NOTE: |
| // |
| // In all of the error processing below, the emitError() call MUST come |
| // before the flush of the reader mgr, or it will fail because it tries |
| // to find out the position in the XML source of the error. |
| catch(const XMLErrs::Codes) |
| { |
| // This is a 'first failure' exception, so fall through |
| } |
| catch(const XMLValid::Codes) |
| { |
| // This is a 'first fatal error' type exit, so fall through |
| } |
| catch(const XMLException& excToCatch) |
| { |
| // Emit the error and catch any user exception thrown from here. Make |
| // sure in all cases we flush the reader manager. |
| fInException = true; |
| try |
| { |
| if (excToCatch.getErrorType() == XMLErrorReporter::ErrType_Warning) |
| emitError |
| ( |
| XMLErrs::XMLException_Warning |
| , excToCatch.getCode() |
| , excToCatch.getMessage() |
| ); |
| else if (excToCatch.getErrorType() >= XMLErrorReporter::ErrType_Fatal) |
| emitError |
| ( |
| XMLErrs::XMLException_Fatal |
| , excToCatch.getCode() |
| , excToCatch.getMessage() |
| ); |
| else |
| emitError |
| ( |
| XMLErrs::XMLException_Error |
| , excToCatch.getCode() |
| , excToCatch.getMessage() |
| ); |
| } |
| catch(const OutOfMemoryException&) |
| { |
| // This is a special case for out-of-memory |
| // conditions, because resetting the ReaderMgr |
| // can be problematic. |
| resetReaderMgr.release(); |
| |
| throw; |
| } |
| } |
| catch(const OutOfMemoryException&) |
| { |
| // This is a special case for out-of-memory |
| // conditions, because resetting the ReaderMgr |
| // can be problematic. |
| resetReaderMgr.release(); |
| |
| throw; |
| } |
| } |
| |
| |
| bool SGXMLScanner::scanNext(XMLPScanToken& token) |
| { |
| // Make sure this token is still legal |
| if (!isLegalToken(token)) |
| ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::Scan_BadPScanToken, fMemoryManager); |
| |
| // Find the next token and remember the reader id |
| XMLSize_t orgReader; |
| XMLTokens curToken; |
| |
| ReaderMgrResetType resetReaderMgr(&fReaderMgr, &ReaderMgr::reset); |
| |
| bool retVal = true; |
| |
| try |
| { |
| while (true) |
| { |
| // We have to handle any end of entity exceptions that happen here. |
| // We could be at the end of X nested entities, each of which will |
| // generate an end of entity exception as we try to move forward. |
| try |
| { |
| curToken = senseNextToken(orgReader); |
| break; |
| } |
| catch(const EndOfEntityException& toCatch) |
| { |
| // Send an end of entity reference event |
| if (fDocHandler) |
| fDocHandler->endEntityReference(toCatch.getEntity()); |
| } |
| } |
| |
| if (curToken == Token_CharData) |
| { |
| scanCharData(fCDataBuf); |
| } |
| else if (curToken == Token_EOF) |
| { |
| if (!fElemStack.isEmpty()) |
| { |
| const ElemStack::StackElem* topElem = fElemStack.popTop(); |
| emitError |
| ( |
| XMLErrs::EndedWithTagsOnStack |
| , topElem->fThisElement->getFullName() |
| ); |
| } |
| |
| retVal = false; |
| } |
| else |
| { |
| // Its some sort of markup |
| bool gotData = true; |
| switch(curToken) |
| { |
| case Token_CData : |
| // Make sure we are within content |
| if (fElemStack.isEmpty()) |
| emitError(XMLErrs::CDATAOutsideOfContent); |
| scanCDSection(); |
| break; |
| |
| case Token_Comment : |
| scanComment(); |
| break; |
| |
| case Token_EndTag : |
| scanEndTag(gotData); |
| break; |
| |
| case Token_PI : |
| scanPI(); |
| break; |
| |
| case Token_StartTag : |
| scanStartTag(gotData); |
| break; |
| |
| default : |
| fReaderMgr.skipToChar(chOpenAngle); |
| break; |
| } |
| |
| if (orgReader != fReaderMgr.getCurrentReaderNum()) |
| emitError(XMLErrs::PartialMarkupInEntity); |
| |
| // If we hit the end, then do the miscellaneous part |
| if (!gotData) |
| { |
| // Do post-parse validation if required |
| if (fValidate) |
| { |
| // We handle ID reference semantics at this level since |
| // its required by XML 1.0. |
| checkIDRefs(); |
| |
| // Then allow the validator to do any extra stuff it wants |
| // fValidator->postParseValidation(); |
| } |
| |
| // That went ok, so scan for any miscellaneous stuff |
| scanMiscellaneous(); |
| |
| if (toCheckIdentityConstraint()) |
| fICHandler->endDocument(); |
| |
| if (fDocHandler) |
| fDocHandler->endDocument(); |
| } |
| } |
| } |
| // NOTE: |
| // |
| // In all of the error processing below, the emitError() call MUST come |
| // before the flush of the reader mgr, or it will fail because it tries |
| // to find out the position in the XML source of the error. |
| catch(const XMLErrs::Codes) |
| { |
| // This is a 'first failure' exception, so return failure |
| retVal = false; |
| } |
| catch(const XMLValid::Codes) |
| { |
| // This is a 'first fatal error' type exit, so return failure |
| retVal = false; |
| } |
| catch(const XMLException& excToCatch) |
| { |
| // Emit the error and catch any user exception thrown from here. Make |
| // sure in all cases we flush the reader manager. |
| fInException = true; |
| try |
| { |
| if (excToCatch.getErrorType() == XMLErrorReporter::ErrType_Warning) |
| emitError |
| ( |
| XMLErrs::XMLException_Warning |
| , excToCatch.getCode() |
| , excToCatch.getMessage() |
| ); |
| else if (excToCatch.getErrorType() >= XMLErrorReporter::ErrType_Fatal) |
| emitError |
| ( |
| XMLErrs::XMLException_Fatal |
| , excToCatch.getCode() |
| , excToCatch.getMessage() |
| ); |
| else |
| emitError |
| ( |
| XMLErrs::XMLException_Error |
| , excToCatch.getCode() |
| , excToCatch.getMessage() |
| ); |
| } |
| catch(const OutOfMemoryException&) |
| { |
| // This is a special case for out-of-memory |
| // conditions, because resetting the ReaderMgr |
| // can be problematic. |
| resetReaderMgr.release(); |
| |
| throw; |
| } |
| |
| retVal = false; |
| } |
| catch(const OutOfMemoryException&) |
| { |
| // This is a special case for out-of-memory |
| // conditions, because resetting the ReaderMgr |
| // can be problematic. |
| resetReaderMgr.release(); |
| |
| throw; |
| } |
| |
| // If we are not at the end, release the object that will |
| // reset the ReaderMgr. |
| if (retVal) |
| resetReaderMgr.release(); |
| |
| return retVal; |
| } |
| |
| // --------------------------------------------------------------------------- |
| // SGXMLScanner: Private scanning methods |
| // --------------------------------------------------------------------------- |
| |
| // This method is called from scanStartTag() to handle the very raw initial |
| // scan of the attributes. It just fills in the passed collection with |
| // key/value pairs for each attribute. No processing is done on them at all. |
| XMLSize_t |
| SGXMLScanner::rawAttrScan(const XMLCh* const elemName |
| , RefVectorOf<KVStringPair>& toFill |
| , bool& isEmpty) |
| { |
| // Keep up with how many attributes we've seen so far, and how many |
| // elements are available in the vector. This way we can reuse old |
| // elements until we run out and then expand it. |
| XMLSize_t attCount = 0; |
| XMLSize_t curVecSize = toFill.size(); |
| |
| // Assume it is not empty |
| isEmpty = false; |
| |
| // We loop until we either see a /> or >, handling key/value pairs util |
| // we get there. We place them in the passed vector, which we will expand |
| // as required to hold them. |
| while (true) |
| { |
| // Get the next character, which should be non-space |
| XMLCh nextCh = fReaderMgr.peekNextChar(); |
| |
| // If the next character is not a slash or closed angle bracket, |
| // then it must be whitespace, since whitespace is required |
| // between the end of the last attribute and the name of the next |
| // one. |
| // |
| if (attCount) |
| { |
| if ((nextCh != chForwardSlash) && (nextCh != chCloseAngle)) |
| { |
| if (fReaderMgr.getCurrentReader()->isWhitespace(nextCh)) |
| { |
| // Ok, skip by them and get another char |
| fReaderMgr.getNextChar(); |
| fReaderMgr.skipPastSpaces(); |
| nextCh = fReaderMgr.peekNextChar(); |
| } |
| else |
| { |
| // Emit the error but keep on going |
| emitError(XMLErrs::ExpectedWhitespace); |
| } |
| } |
| } |
| |
| // Ok, here we first check for any of the special case characters. |
| // If its not one, then we do the normal case processing, which |
| // assumes that we've hit an attribute value, Otherwise, we do all |
| // the special case checks. |
| if (!fReaderMgr.getCurrentReader()->isSpecialStartTagChar(nextCh)) |
| { |
| // Assume its going to be an attribute, so get a name from |
| // the input. |
| int colonPosition; |
| if (!fReaderMgr.getQName(fAttNameBuf, &colonPosition)) |
| { |
| if (fAttNameBuf.isEmpty()) |
| emitError(XMLErrs::ExpectedAttrName); |
| else |
| emitError(XMLErrs::InvalidAttrName, fAttNameBuf.getRawBuffer()); |
| fReaderMgr.skipPastChar(chCloseAngle); |
| return attCount; |
| } |
| |
| const XMLCh* curAttNameBuf = fAttNameBuf.getRawBuffer(); |
| |
| // And next must be an equal sign |
| if (!scanEq()) |
| { |
| static const XMLCh tmpList[] = |
| { |
| chSingleQuote, chDoubleQuote, chCloseAngle |
| , chOpenAngle, chForwardSlash, chNull |
| }; |
| |
| emitError(XMLErrs::ExpectedEqSign); |
| |
| // Try to sync back up by skipping forward until we either |
| // hit something meaningful. |
| const XMLCh chFound = fReaderMgr.skipUntilInOrWS(tmpList); |
| |
| if ((chFound == chCloseAngle) || (chFound == chForwardSlash)) |
| { |
| // Jump back to top for normal processing of these |
| continue; |
| } |
| else if ((chFound == chSingleQuote) |
| || (chFound == chDoubleQuote) |
| || fReaderMgr.getCurrentReader()->isWhitespace(chFound)) |
| { |
| // Just fall through assuming that the value is to follow |
| } |
| else if (chFound == chOpenAngle) |
| { |
| // Assume a malformed tag and that new one is starting |
| emitError(XMLErrs::UnterminatedStartTag, elemName); |
| return attCount; |
| } |
| else |
| { |
| // Something went really wrong |
| return attCount; |
| } |
| } |
| |
| // Next should be the quoted attribute value. We just do a simple |
| // and stupid scan of this value. The only thing we do here |
| // is to expand entity references. |
| if (!basicAttrValueScan(curAttNameBuf, fAttValueBuf)) |
| { |
| static const XMLCh tmpList[] = |
| { |
| chCloseAngle, chOpenAngle, chForwardSlash, chNull |
| }; |
| |
| emitError(XMLErrs::ExpectedAttrValue); |
| |
| // It failed, so lets try to get synced back up. We skip |
| // forward until we find some whitespace or one of the |
| // chars in our list. |
| const XMLCh chFound = fReaderMgr.skipUntilInOrWS(tmpList); |
| |
| if ((chFound == chCloseAngle) |
| || (chFound == chForwardSlash) |
| || fReaderMgr.getCurrentReader()->isWhitespace(chFound)) |
| { |
| // Just fall through and process this attribute, though |
| // the value will be "". |
| } |
| else if (chFound == chOpenAngle) |
| { |
| // Assume a malformed tag and that new one is starting |
| emitError(XMLErrs::UnterminatedStartTag, elemName); |
| return attCount; |
| } |
| else |
| { |
| // Something went really wrong |
| return attCount; |
| } |
| } |
| |
| // And now lets add it to the passed collection. If we have not |
| // filled it up yet, then we use the next element. Else we add |
| // a new one. |
| KVStringPair* curPair = 0; |
| if (attCount >= curVecSize) |
| { |
| curPair = new (fMemoryManager) KVStringPair |
| ( |
| curAttNameBuf |
| , fAttNameBuf.getLen() |
| , fAttValueBuf.getRawBuffer() |
| , fAttValueBuf.getLen() |
| , fMemoryManager |
| ); |
| toFill.addElement(curPair); |
| } |
| else |
| { |
| curPair = toFill.elementAt(attCount); |
| curPair->set |
| ( |
| curAttNameBuf |
| , fAttNameBuf.getLen() |
| , fAttValueBuf.getRawBuffer() |
| , fAttValueBuf.getLen() |
| ); |
| } |
| if (attCount >= fRawAttrColonListSize) { |
| resizeRawAttrColonList(); |
| } |
| fRawAttrColonList[attCount] = colonPosition; |
| |
| // And bump the count of attributes we've gotten |
| attCount++; |
| |
| // And go to the top again for another attribute |
| continue; |
| } |
| |
| // It was some special case character so do all of the checks and |
| // deal with it. |
| if (!nextCh) |
| ThrowXMLwithMemMgr(UnexpectedEOFException, XMLExcepts::Gen_UnexpectedEOF, fMemoryManager); |
| |
| if (nextCh == chForwardSlash) |
| { |
| fReaderMgr.getNextChar(); |
| isEmpty = true; |
| if (!fReaderMgr.skippedChar(chCloseAngle)) |
| emitError(XMLErrs::UnterminatedStartTag, elemName); |
| break; |
| } |
| else if (nextCh == chCloseAngle) |
| { |
| fReaderMgr.getNextChar(); |
| break; |
| } |
| else if (nextCh == chOpenAngle) |
| { |
| // Check for this one specially, since its going to be common |
| // and it is kind of auto-recovering since we've already hit the |
| // next open bracket, which is what we would have seeked to (and |
| // skipped this whole tag.) |
| emitError(XMLErrs::UnterminatedStartTag, elemName); |
| break; |
| } |
| else if ((nextCh == chSingleQuote) || (nextCh == chDoubleQuote)) |
| { |
| // Check for this one specially, which is probably a missing |
| // attribute name, e.g. ="value". Just issue expected name |
| // error and eat the quoted string, then jump back to the |
| // top again. |
| emitError(XMLErrs::ExpectedAttrName); |
| fReaderMgr.getNextChar(); |
| fReaderMgr.skipQuotedString(nextCh); |
| fReaderMgr.skipPastSpaces(); |
| continue; |
| } |
| } |
| |
| return attCount; |
| } |
| |
| |
| // This method will kick off the scanning of the primary content of the |
| // document, i.e. the elements. |
| bool SGXMLScanner::scanContent() |
| { |
| // Go into a loop until we hit the end of the root element, or we fall |
| // out because there is no root element. |
| // |
| // We have to do kind of a deeply nested double loop here in order to |
| // avoid doing the setup/teardown of the exception handler on each |
| // round. Doing it this way we only do it when an exception actually |
| // occurs. |
| bool gotData = true; |
| bool inMarkup = false; |
| while (gotData) |
| { |
| try |
| { |
| while (gotData) |
| { |
| // Sense what the next top level token is. According to what |
| // this tells us, we will call something to handle that kind |
| // of thing. |
| XMLSize_t orgReader; |
| const XMLTokens curToken = senseNextToken(orgReader); |
| |
| // Handle character data and end of file specially. Char data |
| // is not markup so we don't want to handle it in the loop |
| // below. |
| if (curToken == Token_CharData) |
| { |
| // Scan the character data and call appropriate events. Let |
| // him use our local character data buffer for efficiency. |
| scanCharData(fCDataBuf); |
| continue; |
| } |
| else if (curToken == Token_EOF) |
| { |
| // The element stack better be empty at this point or we |
| // ended prematurely before all elements were closed. |
| if (!fElemStack.isEmpty()) |
| { |
| const ElemStack::StackElem* topElem = fElemStack.popTop(); |
| emitError |
| ( |
| XMLErrs::EndedWithTagsOnStack |
| , topElem->fThisElement->getFullName() |
| ); |
| } |
| |
| // Its the end of file, so clear the got data flag |
| gotData = false; |
| continue; |
| } |
| |
| // We are in some sort of markup now |
| inMarkup = true; |
| |
| // According to the token we got, call the appropriate |
| // scanning method. |
| switch(curToken) |
| { |
| case Token_CData : |
| // Make sure we are within content |
| if (fElemStack.isEmpty()) |
| emitError(XMLErrs::CDATAOutsideOfContent); |
| scanCDSection(); |
| break; |
| |
| case Token_Comment : |
| scanComment(); |
| break; |
| |
| case Token_EndTag : |
| scanEndTag(gotData); |
| break; |
| |
| case Token_PI : |
| scanPI(); |
| break; |
| |
| case Token_StartTag : |
| scanStartTag(gotData); |
| break; |
| |
| default : |
| fReaderMgr.skipToChar(chOpenAngle); |
| break; |
| } |
| |
| if (orgReader != fReaderMgr.getCurrentReaderNum()) |
| emitError(XMLErrs::PartialMarkupInEntity); |
| |
| // And we are back out of markup again |
| inMarkup = false; |
| } |
| } |
| catch(const EndOfEntityException& toCatch) |
| { |
| // If we were in some markup when this happened, then its a |
| // partial markup error. |
| if (inMarkup) |
| emitError(XMLErrs::PartialMarkupInEntity); |
| |
| // Send an end of entity reference event |
| if (fDocHandler) |
| fDocHandler->endEntityReference(toCatch.getEntity()); |
| |
| inMarkup = false; |
| } |
| } |
| |
| // It went ok, so return success |
| return true; |
| } |
| |
| |
| void SGXMLScanner::scanEndTag(bool& gotData) |
| { |
| // Assume we will still have data until proven otherwise. It will only |
| // ever be false if this is the end of the root element. |
| gotData = true; |
| |
| // Check if the element stack is empty. If so, then this is an unbalanced |
| // element (i.e. more ends than starts, perhaps because of bad text |
| // causing one to be skipped.) |
| if (fElemStack.isEmpty()) |
| { |
| emitError(XMLErrs::MoreEndThanStartTags); |
| fReaderMgr.skipPastChar(chCloseAngle); |
| ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::Scan_UnbalancedStartEnd, fMemoryManager); |
| } |
| |
| // Pop the stack of the element we are supposed to be ending. Remember |
| // that we don't own this. The stack just keeps them and reuses them. |
| unsigned int uriId = (fDoNamespaces) |
| ? fElemStack.getCurrentURI() : fEmptyNamespaceId; |
| |
| // Make sure that its the end of the element that we expect |
| const XMLCh *elemName = fElemStack.getCurrentSchemaElemName(); |
| const ElemStack::StackElem* topElem = fElemStack.topElement(); |
| if (!fReaderMgr.skippedStringLong(elemName)) |
| { |
| emitError |
| ( |
| XMLErrs::ExpectedEndOfTagX |
| , elemName |
| ); |
| fReaderMgr.skipPastChar(chCloseAngle); |
| fElemStack.popTop(); |
| return; |
| } |
| |
| fPSVIElemContext.fErrorOccurred = fErrorStack->pop(); |
| |
| // Make sure we are back on the same reader as where we started |
| if (topElem->fReaderNum != fReaderMgr.getCurrentReaderNum()) |
| emitError(XMLErrs::PartialTagMarkupError); |
| |
| // Skip optional whitespace |
| fReaderMgr.skipPastSpaces(); |
| |
| // Make sure we find the closing bracket |
| if (!fReaderMgr.skippedChar(chCloseAngle)) |
| { |
| emitError |
| ( |
| XMLErrs::UnterminatedEndTag |
| , topElem->fThisElement->getFullName() |
| ); |
| } |
| |
| if (fValidate && topElem->fThisElement->isDeclared()) |
| { |
| fPSVIElemContext.fCurrentTypeInfo = ((SchemaValidator*) fValidator)->getCurrentTypeInfo(); |
| if(!fPSVIElemContext.fCurrentTypeInfo) |
| fPSVIElemContext.fCurrentDV = ((SchemaValidator*) fValidator)->getCurrentDatatypeValidator(); |
| else |
| fPSVIElemContext.fCurrentDV = 0; |
| if (fPSVIHandler) |
| { |
| fPSVIElemContext.fNormalizedValue = ((SchemaValidator*) fValidator)->getNormalizedValue(); |
| |
| if (XMLString::equals(fPSVIElemContext.fNormalizedValue, XMLUni::fgZeroLenString)) |
| fPSVIElemContext.fNormalizedValue = 0; |
| |
| } |
| } |
| else |
| { |
| fPSVIElemContext.fCurrentDV = 0; |
| fPSVIElemContext.fCurrentTypeInfo = 0; |
| fPSVIElemContext.fNormalizedValue = 0; |
| } |
| |
| // If validation is enabled, then lets pass him the list of children and |
| // this element and let him validate it. |
| DatatypeValidator* psviMemberType = 0; |
| if (fValidate) |
| { |
| XMLSize_t failure; |
| bool res = fValidator->checkContent |
| ( |
| topElem->fThisElement |
| , topElem->fChildren |
| , topElem->fChildCount |
| , &failure |
| ); |
| |
| if (!res) |
| { |
| // One of the elements is not valid for the content. NOTE that |
| // if no children were provided but the content model requires |
| // them, it comes back with a zero value. But we cannot use that |
| // to index the child array in this case, and have to put out a |
| // special message. |
| if (!topElem->fChildCount) |
| { |
| fValidator->emitError |
| ( |
| XMLValid::EmptyNotValidForContent |
| , topElem->fThisElement->getFormattedContentModel() |
| ); |
| } |
| else if (failure >= topElem->fChildCount) |
| { |
| fValidator->emitError |
| ( |
| XMLValid::NotEnoughElemsForCM |
| , topElem->fThisElement->getFormattedContentModel() |
| ); |
| } |
| else |
| { |
| fValidator->emitError |
| ( |
| XMLValid::ElementNotValidForContent |
| , topElem->fChildren[failure]->getRawName() |
| , topElem->fThisElement->getFormattedContentModel() |
| ); |
| } |
| |
| } |
| |
| // update PSVI info |
| if (((SchemaValidator*) fValidator)->getErrorOccurred()) |
| fPSVIElemContext.fErrorOccurred = true; |
| else if (fPSVIElemContext.fCurrentDV && fPSVIElemContext.fCurrentDV->getType() == DatatypeValidator::Union) |
| psviMemberType = fValidationContext->getValidatingMemberType(); |
| if (fPSVIHandler) |
| { |
| fPSVIElemContext.fIsSpecified = ((SchemaValidator*) fValidator)->getIsElemSpecified(); |
| if(fPSVIElemContext.fIsSpecified) |
| fPSVIElemContext.fNormalizedValue = ((SchemaElementDecl *)topElem->fThisElement)->getDefaultValue(); |
| } |
| |
| // call matchers and de-activate context |
| if (toCheckIdentityConstraint()) |
| { |
| fICHandler->deactivateContext |
| ( |
| (SchemaElementDecl *) topElem->fThisElement |
| , fContent.getRawBuffer() |
| , fValidationContext |
| , fPSVIElemContext.fCurrentDV |
| ); |
| } |
| |
| } |
| |
| // QName dv needed topElem to resolve URIs on the checkContent |
| fElemStack.popTop(); |
| |
| // See if it was the root element, to avoid multiple calls below |
| const bool isRoot = fElemStack.isEmpty(); |
| |
| if (fPSVIHandler) |
| { |
| endElementPSVI |
| ( |
| (SchemaElementDecl*)topElem->fThisElement, psviMemberType |
| ); |
| } |
| // now we can reset the datatype buffer, since the |
| // application has had a chance to copy the characters somewhere else |
| ((SchemaValidator *)fValidator)->clearDatatypeBuffer(); |
| |
| // If we have a doc handler, tell it about the end tag |
| if (fDocHandler) |
| { |
| if (fGrammarType == Grammar::SchemaGrammarType) { |
| if (topElem->fPrefixColonPos != -1) |
| fPrefixBuf.set(elemName, topElem->fPrefixColonPos); |
| else |
| fPrefixBuf.reset(); |
| } |
| else { |
| fPrefixBuf.set(topElem->fThisElement->getElementName()->getPrefix()); |
| } |
| fDocHandler->endElement |
| ( |
| *topElem->fThisElement |
| , uriId |
| , isRoot |
| , fPrefixBuf.getRawBuffer() |
| ); |
| } |
| |
| if (!isRoot) |
| { |
| // update error information |
| fErrorStack->push((fErrorStack->size() && fErrorStack->pop()) || fPSVIElemContext.fErrorOccurred); |
| } |
| |
| // If this was the root, then done with content |
| gotData = !isRoot; |
| |
| if (gotData) { |
| |
| // Restore the grammar |
| fGrammar = fElemStack.getCurrentGrammar(); |
| fGrammarType = fGrammar->getGrammarType(); |
| fValidator->setGrammar(fGrammar); |
| |
| // Restore the validation flag |
| fValidate = fElemStack.getValidationFlag(); |
| } |
| } |
| |
| |
| // This method handles the high level logic of scanning the DOCType |
| // declaration. This calls the DTDScanner and kicks off both the scanning of |
| // the internal subset and the scanning of the external subset, if any. |
| // |
| // When we get here the '<!DOCTYPE' part has already been scanned, which is |
| // what told us that we had a doc type decl to parse. |
| void SGXMLScanner::scanDocTypeDecl() |
| { |
| // Just skips over it |
| // REVISIT: Should we issue a warning |
| static const XMLCh doctypeIE[] = |
| { |
| chOpenSquare, chCloseAngle, chNull |
| }; |
| XMLCh nextCh = fReaderMgr.skipUntilIn(doctypeIE); |
| |
| if (nextCh == chOpenSquare) |
| fReaderMgr.skipPastChar(chCloseSquare); |
| |
| fReaderMgr.skipPastChar(chCloseAngle); |
| } |
| |
| // This method is called to scan a start tag when we are processing |
| // namespaces. This method is called after we've scanned the < of a |
| // start tag. So we have to get the element name, then scan the attributes, |
| // after which we are either going to see >, />, or attributes followed |
| // by one of those sequences. |
| bool SGXMLScanner::scanStartTag(bool& gotData) |
| { |
| // Assume we will still have data until proven otherwise. It will only |
| // ever be false if this is the root and its empty. |
| gotData = true; |
| |
| // Reset element content |
| fContent.reset(); |
| |
| // The current position is after the open bracket, so we need to read in |
| // in the element name. |
| int prefixColonPos; |
| if (!fReaderMgr.getQName(fQNameBuf, &prefixColonPos)) |
| { |
| if (fQNameBuf.isEmpty()) |
| emitError(XMLErrs::ExpectedElementName); |
| else |
| emitError(XMLErrs::InvalidElementName, fQNameBuf.getRawBuffer()); |
| fReaderMgr.skipToChar(chOpenAngle); |
| return false; |
| } |
| |
| // See if its the root element |
| const bool isRoot = fElemStack.isEmpty(); |
| |
| // Skip any whitespace after the name |
| fReaderMgr.skipPastSpaces(); |
| |
| // First we have to do the rawest attribute scan. We don't do any |
| // normalization of them at all, since we don't know yet what type they |
| // might be (since we need the element decl in order to do that.) |
| const XMLCh* qnameRawBuf = fQNameBuf.getRawBuffer(); |
| bool isEmpty; |
| XMLSize_t attCount = rawAttrScan |
| ( |
| qnameRawBuf |
| , *fRawAttrList |
| , isEmpty |
| ); |
| |
| // save the contentleafname and currentscope before addlevel, for later use |
| ContentLeafNameTypeVector* cv = 0; |
| XMLContentModel* cm = 0; |
| unsigned int currentScope = Grammar::TOP_LEVEL_SCOPE; |
| bool laxThisOne = false; |
| if (!isRoot) |
| { |
| // schema validator will have correct type if validating |
| SchemaElementDecl* tempElement = (SchemaElementDecl*) |
| fElemStack.topElement()->fThisElement; |
| SchemaElementDecl::ModelTypes modelType = tempElement->getModelType(); |
| ComplexTypeInfo *currType = 0; |
| |
| if (fValidate) |
| { |
| currType = ((SchemaValidator*)fValidator)->getCurrentTypeInfo(); |
| if (currType) |
| modelType = (SchemaElementDecl::ModelTypes)currType->getContentType(); |
| else // something must have gone wrong |
| modelType = SchemaElementDecl::Any; |
| } |
| else |
| { |
| currType = tempElement->getComplexTypeInfo(); |
| } |
| |
| if ((modelType == SchemaElementDecl::Mixed_Simple) |
| || (modelType == SchemaElementDecl::Mixed_Complex) |
| || (modelType == SchemaElementDecl::Children)) |
| { |
| cm = currType->getContentModel(); |
| cv = cm->getContentLeafNameTypeVector(); |
| currentScope = fElemStack.getCurrentScope(); |
| } |
| else if (modelType == SchemaElementDecl::Any) { |
| laxThisOne = true; |
| } |
| } |
| |
| // Now, since we might have to update the namespace map for this element, |
| // but we don't have the element decl yet, we just tell the element stack |
| // to expand up to get ready. |
| XMLSize_t elemDepth = fElemStack.addLevel(); |
| fElemStack.setValidationFlag(fValidate); |
| fElemStack.setPrefixColonPos(prefixColonPos); |
| |
| // Check if there is any external schema location specified, and if we are at root, |
| // go through them first before scanning those specified in the instance document |
| if (isRoot |
| && (fExternalSchemaLocation || fExternalNoNamespaceSchemaLocation)) { |
| |
| if (fExternalSchemaLocation) |
| parseSchemaLocation(fExternalSchemaLocation, true); |
| if (fExternalNoNamespaceSchemaLocation) |
| resolveSchemaGrammar(fExternalNoNamespaceSchemaLocation, XMLUni::fgZeroLenString, true); |
| } |
| |
| // Make an initial pass through the list and find any xmlns attributes or |
| // schema attributes. |
| if (attCount) |
| scanRawAttrListforNameSpaces(attCount); |
| |
| // Resolve the qualified name to a URI and name so that we can look up |
| // the element decl for this element. We have now update the prefix to |
| // namespace map so we should get the correct element now. |
| unsigned int uriId = resolveQNameWithColon |
| ( |
| qnameRawBuf |
| , fPrefixBuf |
| , ElemStack::Mode_Element |
| , prefixColonPos |
| ); |
| |
| //if schema, check if we should lax or skip the validation of this element |
| bool parentValidation = fValidate; |
| if (cv) { |
| QName element(fPrefixBuf.getRawBuffer(), &qnameRawBuf[prefixColonPos + 1], uriId, fMemoryManager); |
| // elementDepth will be > 0, as cv is only constructed if element is not |
| // root. |
| laxThisOne = laxElementValidation(&element, cv, cm, elemDepth - 1); |
| } |
| |
| // Look up the element now in the grammar. This will get us back a |
| // generic element decl object. We tell him to fault one in if he does |
| // not find it. |
| XMLElementDecl* elemDecl = 0; |
| bool wasAdded = false; |
| const XMLCh* nameRawBuf = &qnameRawBuf[prefixColonPos + 1]; |
| const XMLCh* original_uriStr = fGrammar->getTargetNamespace(); |
| |
| if (uriId != fEmptyNamespaceId) { |
| |
| // Check in current grammar before switching if necessary |
| elemDecl = fGrammar->getElemDecl |
| ( |
| uriId |
| , nameRawBuf |
| , qnameRawBuf |
| , currentScope |
| ); |
| if(!elemDecl) |
| { |
| // look in the list of undeclared elements, as would have been done |
| // before we made grammars stateless: |
| elemDecl = fElemNonDeclPool->getByKey(nameRawBuf, uriId, currentScope); |
| } |
| // this is initialized correctly only if there is |
| // no element decl. The other uses in this scope will only |
| // be encountered if there continues to be no element decl--which |
| // implies that this will have been initialized correctly. |
| unsigned int orgGrammarUri = uriId; |
| if (!elemDecl && ( orgGrammarUri = fURIStringPool->getId(original_uriStr)) != uriId) { |
| // not found, switch to the specified grammar |
| const XMLCh* uriStr = getURIText(uriId); |
| bool errorCondition = !switchGrammar(uriStr) && fValidate; |
| if (errorCondition && !laxThisOne) |
| { |
| fValidator->emitError |
| ( |
| XMLValid::GrammarNotFound |
| ,uriStr |
| ); |
| } |
| |
| elemDecl = fGrammar->getElemDecl |
| ( |
| uriId |
| , nameRawBuf |
| , qnameRawBuf |
| , currentScope |
| ); |
| } |
| |
| if (!elemDecl && currentScope != Grammar::TOP_LEVEL_SCOPE) { |
| // if not found, then it may be a reference, try TOP_LEVEL_SCOPE |
| elemDecl = fGrammar->getElemDecl |
| ( |
| uriId |
| , nameRawBuf |
| , qnameRawBuf |
| , Grammar::TOP_LEVEL_SCOPE |
| ); |
| if(!elemDecl) |
| { |
| // look in the list of undeclared elements, as would have been done |
| // before we made grammars stateless: |
| elemDecl = fElemNonDeclPool->getByKey(nameRawBuf, uriId, (int)Grammar::TOP_LEVEL_SCOPE); |
| } |
| if(!elemDecl) { |
| // still not found in specified uri |
| // try emptyNamespace see if element should be un-qualified. |
| // Use a temp variable until we decide this is the case |
| XMLElementDecl* tempElemDecl = fGrammar->getElemDecl |
| ( |
| fEmptyNamespaceId |
| , nameRawBuf |
| , qnameRawBuf |
| , currentScope |
| ); |
| if (tempElemDecl && tempElemDecl->getCreateReason() != XMLElementDecl::JustFaultIn && fValidate) { |
| fValidator->emitError |
| ( |
| XMLValid::ElementNotUnQualified |
| , qnameRawBuf |
| ); |
| elemDecl = tempElemDecl; |
| } |
| } |
| } |
| |
| if (!elemDecl) { |
| // still not found, fault this in and issue error later |
| // switch back to original grammar first (if necessary) |
| if(orgGrammarUri != uriId) |
| { |
| switchGrammar(original_uriStr); |
| } |
| elemDecl = new (fMemoryManager) SchemaElementDecl |
| ( |
| fPrefixBuf.getRawBuffer() |
| , nameRawBuf |
| , uriId |
| , SchemaElementDecl::Any |
| , Grammar::TOP_LEVEL_SCOPE |
| , fMemoryManager |
| ); |
| elemDecl->setId(fElemNonDeclPool->put((void*)elemDecl->getBaseName(), uriId, currentScope, (SchemaElementDecl*)elemDecl)); |
| wasAdded = true; |
| } |
| } |
| else if (!elemDecl) |
| { |
| //the element has no prefix, |
| //thus it is either a non-qualified element defined in current targetNS |
| //or an element that is defined in the globalNS |
| |
| //try unqualifed first |
| elemDecl = fGrammar->getElemDecl |
| ( |
| uriId |
| , nameRawBuf |
| , qnameRawBuf |
| , currentScope |
| ); |
| if(!elemDecl) |
| { |
| // look in the list of undeclared elements, as would have been done |
| // before we made grammars stateless: |
| elemDecl = fElemNonDeclPool->getByKey(nameRawBuf, uriId, currentScope); |
| } |
| // this is initialized correctly only if there is |
| // no element decl. The other uses in this scope will only |
| // be encountered if there continues to be no element decl--which |
| // implies that this will have been initialized correctly. |
| unsigned int orgGrammarUri = fEmptyNamespaceId; |
| if (!elemDecl && (orgGrammarUri = fURIStringPool->getId(original_uriStr)) != fEmptyNamespaceId) { |
| //not found, switch grammar and try globalNS |
| bool errorCondition = !switchGrammar(XMLUni::fgZeroLenString) && fValidate; |
| if (errorCondition && !laxThisOne) |
| { |
| fValidator->emitError |
| ( |
| XMLValid::GrammarNotFound |
| , XMLUni::fgZeroLenString |
| ); |
| } |
| |
| elemDecl = fGrammar->getElemDecl |
| ( |
| uriId |
| , nameRawBuf |
| , qnameRawBuf |
| , currentScope |
| ); |
| } |
| |
| if (!elemDecl && currentScope != Grammar::TOP_LEVEL_SCOPE) { |
| // if not found, then it may be a reference, try TOP_LEVEL_SCOPE |
| elemDecl = fGrammar->getElemDecl |
| ( |
| uriId |
| , nameRawBuf |
| , qnameRawBuf |
| , Grammar::TOP_LEVEL_SCOPE |
| ); |
| if(!elemDecl) |
| { |
| // look in the list of undeclared elements, as would have been done |
| // before we made grammars stateless: |
| elemDecl = fElemNonDeclPool->getByKey(nameRawBuf, uriId, (int)Grammar::TOP_LEVEL_SCOPE); |
| } |
| if (!elemDecl && orgGrammarUri != fEmptyNamespaceId) { |
| // still Not found in specified uri |
| // go to original Grammar again to see if element needs to be fully qualified. |
| bool errorCondition = !switchGrammar(original_uriStr) && fValidate; |
| if (errorCondition && !laxThisOne) |
| { |
| fValidator->emitError |
| ( |
| XMLValid::GrammarNotFound |
| ,original_uriStr |
| ); |
| } |
| |
| // Use a temp variable until we decide this is the case |
| XMLElementDecl* tempElemDecl = fGrammar->getElemDecl |
| ( |
| orgGrammarUri |
| , nameRawBuf |
| , qnameRawBuf |
| , currentScope |
| ); |
| if (tempElemDecl && tempElemDecl->getCreateReason() != XMLElementDecl::JustFaultIn && fValidate) { |
| fValidator->emitError |
| ( |
| XMLValid::ElementNotQualified |
| , qnameRawBuf |
| ); |
| elemDecl=tempElemDecl; |
| } |
| } |
| } |
| |
| if (!elemDecl) { |
| // still not found, fault this in and issue error later |
| // switch back to original grammar first (if necessary) |
| if(orgGrammarUri != fEmptyNamespaceId) |
| { |
| switchGrammar(original_uriStr); |
| } |
| elemDecl = new (fMemoryManager) SchemaElementDecl |
| ( |
| fPrefixBuf.getRawBuffer() |
| , nameRawBuf |
| , uriId |
| , SchemaElementDecl::Any |
| , Grammar::TOP_LEVEL_SCOPE |
| , fMemoryManager |
| ); |
| elemDecl->setId(fElemNonDeclPool->put((void*)elemDecl->getBaseName(), uriId, currentScope, (SchemaElementDecl*)elemDecl)); |
| wasAdded = true; |
| } |
| } |
| |
| // this info needed for DOMTypeInfo |
| fPSVIElemContext.fErrorOccurred = false; |
| |
| // We do something different here according to whether we found the |
| // element or not. |
| bool bXsiTypeSet= (fValidator)?((SchemaValidator*)fValidator)->getIsXsiTypeSet():false; |
| if (wasAdded) |
| { |
| if (laxThisOne && !bXsiTypeSet) { |
| fValidate = false; |
| fElemStack.setValidationFlag(fValidate); |
| } |
| |
| // If validating then emit an error |
| if (fValidate) |
| { |
| // This is to tell the reuse Validator that this element was |
| // faulted-in, was not an element in the grammar pool originally |
| elemDecl->setCreateReason(XMLElementDecl::JustFaultIn); |
| |
| if(!bXsiTypeSet) |
| { |
| fValidator->emitError |
| ( |
| XMLValid::ElementNotDefined |
| , elemDecl->getFullName() |
| ); |
| fPSVIElemContext.fErrorOccurred = true; |
| } |
| } |
| } |
| else |
| { |
| // If its not marked declared and validating, then emit an error |
| if (!elemDecl->isDeclared()) { |
| if(elemDecl->getCreateReason() == XMLElementDecl::NoReason) { |
| if(!bXsiTypeSet) |
| fPSVIElemContext.fErrorOccurred = true; |
| } |
| if (laxThisOne) { |
| fValidate = false; |
| fElemStack.setValidationFlag(fValidate); |
| } |
| |
| if (fValidate && !bXsiTypeSet) |
| { |
| fValidator->emitError |
| ( |
| XMLValid::ElementNotDefined |
| , elemDecl->getFullName() |
| ); |
| } |
| } |
| } |
| |
| |
| // Now we can update the element stack to set the current element |
| // decl. We expanded the stack above, but couldn't store the element |
| // decl because we didn't know it yet. |
| fElemStack.setElement(elemDecl, fReaderMgr.getCurrentReaderNum()); |
| fElemStack.setCurrentURI(uriId); |
| |
| if (isRoot) |
| { |
| fRootGrammar = fGrammar; |
| fRootElemName = XMLString::replicate(qnameRawBuf, fMemoryManager); |
| } |
| |
| if (fPSVIHandler) |
| { |
| fPSVIElemContext.fElemDepth++; |
| |
| if (elemDecl->isDeclared()) |
| { |
| fPSVIElemContext.fNoneValidationDepth = fPSVIElemContext.fElemDepth; |
| } |
| else |
| { |
| fPSVIElemContext.fFullValidationDepth = fPSVIElemContext.fElemDepth; |
| |
| /****** |
| * While we report an error for historical reasons, this should |
| * actually result in lax assessment - NG. |
| if (isRoot && fValidate) |
| fPSVIElemContext.fErrorOccurred = true; |
| ******/ |
| } |
| } |
| |
| // Validate the element |
| if (fValidate) |
| { |
| fValidator->validateElement(elemDecl); |
| if (((SchemaValidator*) fValidator)->getErrorOccurred()) |
| fPSVIElemContext.fErrorOccurred = true; |
| } |
| |
| // squirrel away the element's QName, so that we can do an efficient |
| // end-tag match |
| fElemStack.setCurrentSchemaElemName(fQNameBuf.getRawBuffer()); |
| |
| ComplexTypeInfo* typeinfo = (fValidate) |
| ? ((SchemaValidator*)fValidator)->getCurrentTypeInfo() |
| : ((SchemaElementDecl*) elemDecl)->getComplexTypeInfo(); |
| |
| if (typeinfo) { |
| currentScope = typeinfo->getScopeDefined(); |
| |
| // switch grammar if the typeinfo has a different grammar (happens when there is xsi:type) |
| XMLCh* typeName = typeinfo->getTypeName(); |
| const int comma = XMLString::indexOf(typeName, chComma); |
| if (comma > 0) { |
| XMLBuffer prefixBuf(comma+1, fMemoryManager); |
| prefixBuf.append(typeName, comma); |
| const XMLCh* uriStr = prefixBuf.getRawBuffer(); |
| |
| bool errorCondition = !switchGrammar(uriStr) && fValidate; |
| if (errorCondition && !laxThisOne) |
| { |
| fValidator->emitError |
| ( |
| XMLValid::GrammarNotFound |
| , prefixBuf.getRawBuffer() |
| ); |
| } |
| } |
| else if (comma == 0) { |
| bool errorCondition = !switchGrammar(XMLUni::fgZeroLenString) && fValidate; |
| if (errorCondition && !laxThisOne) |
| { |
| fValidator->emitError |
| ( |
| XMLValid::GrammarNotFound |
| , XMLUni::fgZeroLenString |
| ); |
| } |
| } |
| } |
| fElemStack.setCurrentScope(currentScope); |
| |
| // Set element next state |
| if (elemDepth >= fElemStateSize) { |
| resizeElemState(); |
| } |
| |
| fElemState[elemDepth] = 0; |
| fElemLoopState[elemDepth] = 0; |
| fElemStack.setCurrentGrammar(fGrammar); |
| |
| // If this is the first element and we are validating, check the root |
| // element. |
| if (!isRoot && parentValidation) |
| { |
| // If the element stack is not empty, then add this element as a |
| // child of the previous top element. If its empty, this is the root |
| // elem and is not the child of anything. |
| fElemStack.addChild(elemDecl->getElementName(), true); |
| } |
| |
| // PSVI handling: must reset this, even if no attributes... |
| if(getPSVIHandler()) |
| fPSVIAttrList->reset(); |
| |
| // Now lets get the fAttrList filled in. This involves faulting in any |
| // defaulted and fixed attributes and normalizing the values of any that |
| // we got explicitly. |
| // |
| // We update the attCount value with the total number of attributes, but |
| // it goes in with the number of values we got during the raw scan of |
| // explictly provided attrs above. |
| attCount = buildAttList(*fRawAttrList, attCount, elemDecl, *fAttrList); |
| |
| if(attCount) |
| { |
| // clean up after ourselves: |
| // clear the map used to detect duplicate attributes |
| fUndeclaredAttrRegistry->removeAll(); |
| } |
| |
| // activate identity constraints |
| if (toCheckIdentityConstraint()) |
| { |
| fICHandler->activateIdentityConstraint |
| ( |
| (SchemaElementDecl*) elemDecl |
| , (int) elemDepth |
| , uriId |
| , fPrefixBuf.getRawBuffer() |
| , *fAttrList |
| , attCount |
| , fValidationContext |
| ); |
| |
| } |
| |
| // Since the element may have default values, call start tag now regardless if it is empty or not |
| // If we have a document handler, then tell it about this start tag |
| if (fDocHandler) |
| { |
| fDocHandler->startElement |
| ( |
| *elemDecl |
| , uriId |
| , fPrefixBuf.getRawBuffer() |
| , *fAttrList |
| , attCount |
| , false |
| , isRoot |
| ); |
| } // may be where we output something... |
| |
| // if we have a PSVIHandler, now's the time to call |
| // its handleAttributesPSVI method: |
| if(fPSVIHandler) |
| { |
| QName *eName = elemDecl->getElementName(); |
| fPSVIHandler->handleAttributesPSVI |
| ( |
| eName->getLocalPart() |
| , fURIStringPool->getValueForId(eName->getURI()) |
| , fPSVIAttrList |
| ); |
| } |
| |
| // If empty, validate content right now if we are validating and then |
| // pop the element stack top. Else, we have to update the current stack |
| // top's namespace mapping elements. |
| if (isEmpty) |
| { |
| // Pop the element stack back off since it'll never be used now |
| fElemStack.popTop(); |
| |
| // reset current type info |
| DatatypeValidator* psviMemberType = 0; |
| if (fGrammarType == Grammar::SchemaGrammarType) |
| { |
| if (fValidate && elemDecl->isDeclared()) |
| { |
| fPSVIElemContext.fCurrentTypeInfo = ((SchemaValidator*) fValidator)->getCurrentTypeInfo(); |
| if(!fPSVIElemContext.fCurrentTypeInfo) |
| fPSVIElemContext.fCurrentDV = ((SchemaValidator*) fValidator)->getCurrentDatatypeValidator(); |
| else |
| fPSVIElemContext.fCurrentDV = 0; |
| if(fPSVIHandler) |
| { |
| fPSVIElemContext.fNormalizedValue = ((SchemaValidator*) fValidator)->getNormalizedValue(); |
| |
| if (XMLString::equals(fPSVIElemContext.fNormalizedValue, XMLUni::fgZeroLenString)) |
| fPSVIElemContext.fNormalizedValue = 0; |
| } |
| } |
| else |
| { |
| fPSVIElemContext.fCurrentDV = 0; |
| fPSVIElemContext.fCurrentTypeInfo = 0; |
| fPSVIElemContext.fNormalizedValue = 0; |
| } |
| } |
| |
| // If validating, then insure that its legal to have no content |
| if (fValidate) |
| { |
| XMLSize_t failure; |
| bool res = fValidator->checkContent(elemDecl, 0, 0, &failure); |
| if (!res) |
| { |
| // REVISIT: in the case of xsi:type, this may |
| // return the wrong string... |
| fValidator->emitError |
| ( |
| XMLValid::ElementNotValidForContent |
| , elemDecl->getFullName() |
| , elemDecl->getFormattedContentModel() |
| ); |
| } |
| |
| if (((SchemaValidator*) fValidator)->getErrorOccurred()) |
| fPSVIElemContext.fErrorOccurred = true; |
| // note that if we're empty, won't be a current DV |
| else |
| { |
| if (fPSVIHandler) |
| { |
| fPSVIElemContext.fIsSpecified = ((SchemaValidator*) fValidator)->getIsElemSpecified(); |
| if(fPSVIElemContext.fIsSpecified) |
| fPSVIElemContext.fNormalizedValue = ((SchemaElementDecl *)elemDecl)->getDefaultValue(); |
| } |
| if (fPSVIElemContext.fCurrentDV && fPSVIElemContext.fCurrentDV->getType() == DatatypeValidator::Union) |
| psviMemberType = fValidationContext->getValidatingMemberType(); |
| } |
| |
| // call matchers and de-activate context |
| if (toCheckIdentityConstraint()) |
| { |
| fICHandler->deactivateContext |
| ( |
| (SchemaElementDecl *) elemDecl |
| , fContent.getRawBuffer() |
| , fValidationContext |
| , fPSVIElemContext.fCurrentDV |
| ); |
| } |
| |
| } |
| else if (fGrammarType == Grammar::SchemaGrammarType) { |
| ((SchemaValidator*)fValidator)->resetNillable(); |
| } |
| |
| if (fPSVIHandler) |
| { |
| endElementPSVI |
| ( |
| (SchemaElementDecl*)elemDecl, psviMemberType |
| ); |
| } |
| |
| // If we have a doc handler, tell it about the end tag |
| if (fDocHandler) |
| { |
| fDocHandler->endElement |
| ( |
| *elemDecl |
| , uriId |
| , isRoot |
| , fPrefixBuf.getRawBuffer() |
| ); |
| } |
| |
| // If the elem stack is empty, then it was an empty root |
| if (isRoot) |
| gotData = false; |
| else |
| { |
| // Restore the grammar |
| fGrammar = fElemStack.getCurrentGrammar(); |
| fGrammarType = fGrammar->getGrammarType(); |
| fValidator->setGrammar(fGrammar); |
| |
| // Restore the validation flag |
| fValidate = fElemStack.getValidationFlag(); |
| } |
| } |
| else // not empty |
| { |
| |
| // send a partial element psvi |
| if (fPSVIHandler) |
| { |
| |
| ComplexTypeInfo* curTypeInfo = 0; |
| DatatypeValidator* curDV = 0; |
| XSTypeDefinition* typeDef = 0; |
| |
| if (fValidate && elemDecl->isDeclared()) |
| { |
| curTypeInfo = ((SchemaValidator*) fValidator)->getCurrentTypeInfo(); |
| |
| if (curTypeInfo) |
| { |
| typeDef = (XSTypeDefinition*) fModel->getXSObject(curTypeInfo); |
| } |
| else |
| { |
| curDV = ((SchemaValidator*) fValidator)->getCurrentDatatypeValidator(); |
| |
| if (curDV) |
| { |
| typeDef = (XSTypeDefinition*) fModel->getXSObject(curDV); |
| } |
| } |
| } |
| |
| fPSVIElement->reset |
| ( |
| PSVIElement::VALIDITY_NOTKNOWN |
| , PSVIElement::VALIDATION_NONE |
| , fRootElemName |
| , ((SchemaValidator*) fValidator)->getIsElemSpecified() |
| , (elemDecl->isDeclared()) ? (XSElementDeclaration*) fModel->getXSObject(elemDecl) : 0 |
| , typeDef |
| , 0 //memberType |
| , fModel |
| , ((SchemaElementDecl*)elemDecl)->getDefaultValue() |
| , 0 |
| , 0 |
| , 0 |
| ); |
| |
| |
| fPSVIHandler->handlePartialElementPSVI |
| ( |
| elemDecl->getBaseName() |
| , fURIStringPool->getValueForId(elemDecl->getURI()) |
| , fPSVIElement |
| ); |
| |
| } |
| |
| fErrorStack->push(fPSVIElemContext.fErrorOccurred); |
| } |
| |
| return true; |
| } |
| |
| |
| // --------------------------------------------------------------------------- |
| // SGXMLScanner: Grammar preparsing |
| // --------------------------------------------------------------------------- |
| Grammar* SGXMLScanner::loadGrammar(const InputSource& src |
| , const short grammarType |
| , const bool toCache) |
| { |
| Grammar* loadedGrammar = 0; |
| |
| ReaderMgrResetType resetReaderMgr(&fReaderMgr, &ReaderMgr::reset); |
| |
| try |
| { |
| fGrammarResolver->cacheGrammarFromParse(false); |
| // if the new grammar has to be cached, better use the already cached |
| // grammars, or the an exception will be thrown when caching an already |
| // cached grammar |
| fGrammarResolver->useCachedGrammarInParse(toCache); |
| fRootGrammar = 0; |
| |
| if (fValScheme == Val_Auto) { |
| fValidate = true; |
| } |
| |
| // Reset some status flags |
| fInException = false; |
| fStandalone = false; |
| fErrorCount = 0; |
| fHasNoDTD = true; |
| fSeeXsi = false; |
| |
| if (grammarType == Grammar::SchemaGrammarType) { |
| loadedGrammar = loadXMLSchemaGrammar(src, toCache); |
| } |
| } |
| // NOTE: |
| // |
| // In all of the error processing below, the emitError() call MUST come |
| // before the flush of the reader mgr, or it will fail because it tries |
| // to find out the position in the XML source of the error. |
| catch(const XMLErrs::Codes) |
| { |
| // This is a 'first failure' exception, so fall through |
| } |
| catch(const XMLValid::Codes) |
| { |
| // This is a 'first fatal error' type exit, so fall through |
| } |
| catch(const XMLException& excToCatch) |
| { |
| // Emit the error and catch any user exception thrown from here. Make |
| // sure in all cases we flush the reader manager. |
| fInException = true; |
| try |
| { |
| if (excToCatch.getErrorType() == XMLErrorReporter::ErrType_Warning) |
| emitError |
| ( |
| XMLErrs::XMLException_Warning |
| , excToCatch.getCode() |
| , excToCatch.getMessage() |
| ); |
| else if (excToCatch.getErrorType() >= XMLErrorReporter::ErrType_Fatal) |
| emitError |
| ( |
| XMLErrs::XMLException_Fatal |
| , excToCatch.getCode() |
| , excToCatch.getMessage() |
| ); |
| else |
| emitError |
| ( |
| XMLErrs::XMLException_Error |
| , excToCatch.getCode() |
| , excToCatch.getMessage() |
| ); |
| } |
| catch(const OutOfMemoryException&) |
| { |
| // This is a special case for out-of-memory |
| // conditions, because resetting the ReaderMgr |
| // can be problematic. |
| resetReaderMgr.release(); |
| |
| throw; |
| } |
| } |
| catch(const OutOfMemoryException&) |
| { |
| // This is a special case for out-of-memory |
| // conditions, because resetting the ReaderMgr |
| // can be problematic. |
| resetReaderMgr.release(); |
| |
| throw; |
| } |
| |
| return loadedGrammar; |
| } |
| |
| void SGXMLScanner::resetCachedGrammar () |
| { |
| fCachedSchemaInfoList->removeAll (); |
| } |
| |
| // --------------------------------------------------------------------------- |
| // SGXMLScanner: Private helper methods |
| // --------------------------------------------------------------------------- |
| // This method handles the common initialization, to avoid having to do |
| // it redundantly in multiple constructors. |
| void SGXMLScanner::commonInit() |
| { |
| // Create the element state array |
| fElemState = (unsigned int*) fMemoryManager->allocate |
| ( |
| fElemStateSize * sizeof(unsigned int) |
| ); //new unsigned int[fElemStateSize]; |
| fElemLoopState = (unsigned int*) fMemoryManager->allocate |
| ( |
| fElemStateSize * sizeof(unsigned int) |
| ); //new unsigned int[fElemStateSize]; |
| |
| // And we need one for the raw attribute scan. This just stores key/ |
| // value string pairs (prior to any processing.) |
| fRawAttrList = new (fMemoryManager) RefVectorOf<KVStringPair>(32, true, fMemoryManager); |
| fRawAttrColonList = (int*) fMemoryManager->allocate |
| ( |
| fRawAttrColonListSize * sizeof(int) |
| ); |
| |
| // Create the Validator and init them |
| fSchemaValidator = new (fMemoryManager) SchemaValidator(0, fMemoryManager); |
| initValidator(fSchemaValidator); |
| |
| // Create IdentityConstraint info |
| fICHandler = new (fMemoryManager) IdentityConstraintHandler(this, fMemoryManager); |
| |
| // Add the default entity entries for the character refs that must always |
| // be present. |
| fEntityTable = new (fMemoryManager) ValueHashTableOf<XMLCh>(11, fMemoryManager); |
| fEntityTable->put((void*) XMLUni::fgAmp, chAmpersand); |
| fEntityTable->put((void*) XMLUni::fgLT, chOpenAngle); |
| fEntityTable->put((void*) XMLUni::fgGT, chCloseAngle); |
| fEntityTable->put((void*) XMLUni::fgQuot, chDoubleQuote); |
| fEntityTable->put((void*) XMLUni::fgApos, chSingleQuote); |
| fElemNonDeclPool = new (fMemoryManager) RefHash3KeysIdPool<SchemaElementDecl>(29, true, 128, fMemoryManager); |
| fAttDefRegistry = new (fMemoryManager) RefHashTableOf<unsigned int, PtrHasher> |
| ( |
| 131, false, fMemoryManager |
| ); |
| fUndeclaredAttrRegistry = new (fMemoryManager) Hash2KeysSetOf<StringHasher>(7, fMemoryManager); |
| fPSVIAttrList = new (fMemoryManager) PSVIAttributeList(fMemoryManager); |
| |
| fSchemaInfoList = new (fMemoryManager) RefHash2KeysTableOf<SchemaInfo>(29, fMemoryManager); |
| fCachedSchemaInfoList = new (fMemoryManager) RefHash2KeysTableOf<SchemaInfo>(29, fMemoryManager); |
| |
| if (fValidator) |
| { |
| if (!fValidator->handlesSchema()) |
| ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::Gen_NoSchemaValidator, fMemoryManager); |
| } |
| else |
| { |
| fValidator = fSchemaValidator; |
| } |
| } |
| |
| void SGXMLScanner::cleanUp() |
| { |
| fMemoryManager->deallocate(fElemState); //delete [] fElemState; |
| fMemoryManager->deallocate(fElemLoopState); //delete [] fElemLoopState; |
| delete fSchemaGrammar; |
| delete fEntityTable; |
| delete fRawAttrList; |
| fMemoryManager->deallocate(fRawAttrColonList); |
| delete fSchemaValidator; |
| delete fICHandler; |
| delete fElemNonDeclPool; |
| delete fAttDefRegistry; |
| delete fUndeclaredAttrRegistry; |
| delete fPSVIAttrList; |
| if (fPSVIElement) |
| delete fPSVIElement; |
| |
| if (fErrorStack) |
| delete fErrorStack; |
| |
| delete fSchemaInfoList; |
| delete fCachedSchemaInfoList; |
| } |
| |
| void SGXMLScanner::resizeElemState() { |
| |
| unsigned int newSize = fElemStateSize * 2; |
| unsigned int* newElemState = (unsigned int*) fMemoryManager->allocate |
| ( |
| newSize * sizeof(unsigned int) |
| ); //new unsigned int[newSize]; |
| unsigned int* newElemLoopState = (unsigned int*) fMemoryManager->allocate |
| ( |
| newSize * sizeof(unsigned int) |
| ); //new unsigned int[newSize]; |
| |
| // Copy the existing values |
| unsigned int index = 0; |
| for (; index < fElemStateSize; index++) |
| { |
| newElemState[index] = fElemState[index]; |
| newElemLoopState[index] = fElemLoopState[index]; |
| } |
| |
| for (; index < newSize; index++) |
| newElemLoopState[index] = newElemState[index] = 0; |
| |
| // Delete the old array and udpate our members |
| fMemoryManager->deallocate(fElemState); //delete [] fElemState; |
| fMemoryManager->deallocate(fElemLoopState); //delete [] fElemLoopState; |
| fElemState = newElemState; |
| fElemLoopState = newElemLoopState; |
| fElemStateSize = newSize; |
| } |
| |
| void SGXMLScanner::resizeRawAttrColonList() { |
| |
| unsigned int newSize = fRawAttrColonListSize * 2; |
| int* newRawAttrColonList = (int*) fMemoryManager->allocate |
| ( |
| newSize * sizeof(int) |
| ); //new int[newSize]; |
| |
| // Copy the existing values |
| unsigned int index = 0; |
| for (; index < fRawAttrColonListSize; index++) |
| newRawAttrColonList[index] = fRawAttrColonList[index]; |
| |
| // Delete the old array and udpate our members |
| fMemoryManager->deallocate(fRawAttrColonList); //delete [] fRawAttrColonList; |
| fRawAttrColonList = newRawAttrColonList; |
| fRawAttrColonListSize = newSize; |
| } |
| |
| // This method is called from scanStartTag() to build up the list of |
| // XMLAttr objects that will be passed out in the start tag callout. We |
| // get the key/value pairs from the raw scan of explicitly provided attrs, |
| // which have not been normalized. And we get the element declaration from |
| // which we will get any defaulted or fixed attribute defs and add those |
| // in as well. |
| XMLSize_t |
| SGXMLScanner::buildAttList(const RefVectorOf<KVStringPair>& providedAttrs |
| , const XMLSize_t attCount |
| , XMLElementDecl* elemDecl |
| , RefVectorOf<XMLAttr>& toFill) |
| { |
| // Ask the element to clear the 'provided' flag on all of the att defs |
| // that it owns, and to return us a boolean indicating whether it has |
| // any defs. |
| DatatypeValidator *currDV = 0; |
| ComplexTypeInfo *currType = 0; |
| |
| if (fValidate) |
| { |
| currType = ((SchemaValidator*)fValidator)->getCurrentTypeInfo(); |
| if (!currType) { |
| currDV = ((SchemaValidator*)fValidator)->getCurrentDatatypeValidator(); |
| } |
| } |
| |
| const bool hasDefs = (currType && fValidate) |
| ? currType->hasAttDefs() |
| : elemDecl->hasAttDefs(); |
| |
| fElemCount++; |
| |
| // If there are no expliclitily provided attributes and there are no |
| // defined attributes for the element, the we don't have anything to do. |
| // So just return zero in this case. |
| if (!hasDefs && !attCount) |
| return 0; |
| |
| // Keep up with how many attrs we end up with total |
| XMLSize_t retCount = 0; |
| |
| // And get the current size of the output vector. This lets us use |
| // existing elements until we fill it, then start adding new ones. |
| const XMLSize_t curAttListSize = toFill.size(); |
| |
| // We need a buffer into which raw scanned attribute values will be |
| // normalized. |
| XMLBufBid bbNormal(&fBufMgr); |
| XMLBuffer& normBuf = bbNormal.getBuffer(); |
| |
| XMLBufBid bbPrefix(&fBufMgr); |
| XMLBuffer& prefixBuf = bbPrefix.getBuffer(); |
| |
| // Loop through our explicitly provided attributes, which are in the raw |
| // scanned form, and build up XMLAttr objects. |
| XMLSize_t index; |
| const XMLCh* prefPtr, *suffPtr; |
| for (index = 0; index < attCount; index++) |
| { |
| PSVIItem::VALIDITY_STATE attrValid = PSVIItem::VALIDITY_VALID; |
| PSVIItem::ASSESSMENT_TYPE attrAssessed = PSVIItem::VALIDATION_FULL; |
| const KVStringPair* curPair = providedAttrs.elementAt(index); |
| |
| // We have to split the name into its prefix and name parts. Then |
| // we map the prefix to its URI. |
| const XMLCh* const namePtr = curPair->getKey(); |
| |
| const int colonInd = fRawAttrColonList[index]; |
| unsigned int uriId; |
| if (colonInd != -1) |
| { |
| prefixBuf.set(namePtr, colonInd); |
| prefPtr = prefixBuf.getRawBuffer(); |
| suffPtr = namePtr + colonInd + 1; |
| // Map the prefix to a URI id |
| uriId = resolvePrefix(prefPtr, ElemStack::Mode_Attribute); |
| } |
| else |
| { |
| // No colon, so we just have a name with no prefix |
| prefPtr = XMLUni::fgZeroLenString; |
| suffPtr = namePtr; |
| // an empty prefix is always the empty namespace, when dealing with attributes |
| uriId = fEmptyNamespaceId; |
| } |
| |
| // If the uri comes back as the xmlns or xml URI or its just a name |
| // and that name is 'xmlns', then we handle it specially. So set a |
| // boolean flag that lets us quickly below know which we are dealing |
| // with. |
| const bool isNSAttr = (uriId == fEmptyNamespaceId)? |
| XMLString::equals(suffPtr, XMLUni::fgXMLNSString) : |
| (uriId == fXMLNSNamespaceId || XMLString::equals(getURIText(uriId), SchemaSymbols::fgURI_XSI)); |
| |
| // If its not a special case namespace attr of some sort, then we |
| // do normal checking and processing. |
| XMLAttDef::AttTypes attType = XMLAttDef::CData; |
| DatatypeValidator *attrValidator = 0; |
| PSVIAttribute *psviAttr = 0; |
| bool otherXSI = false; |
| |
| if (isNSAttr) |
| { |
| if(!fUndeclaredAttrRegistry->putIfNotPresent(suffPtr, uriId)) |
| { |
| emitError |
| ( |
| XMLErrs::AttrAlreadyUsedInSTag |
| , namePtr |
| , elemDecl->getFullName() |
| ); |
| fPSVIElemContext.fErrorOccurred = true; |
| } |
| else |
| { |
| bool ValueValidate = false; |
| bool tokenizeBuffer = false; |
| |
| if (uriId == fXMLNSNamespaceId) |
| { |
| attrValidator = DatatypeValidatorFactory::getBuiltInRegistry()->get(SchemaSymbols::fgDT_ANYURI); |
| } |
| else if (XMLString::equals(getURIText(uriId), SchemaSymbols::fgURI_XSI)) |
| { |
| if (XMLString::equals(suffPtr, SchemaSymbols::fgATT_NILL)) |
| { |
| attrValidator = DatatypeValidatorFactory::getBuiltInRegistry()->get(SchemaSymbols::fgDT_BOOLEAN); |
| |
| ValueValidate = true; |
| } |
| else if (XMLString::equals(suffPtr, SchemaSymbols::fgXSI_SCHEMALOCATION)) |
| { |
| // use anyURI as the validator |
| // tokenize the data and use the anyURI data for each piece |
| attrValidator = DatatypeValidatorFactory::getBuiltInRegistry()->get(SchemaSymbols::fgDT_ANYURI); |
| //We should validate each value in the schema location however |
| //this lead to a performance degradation of around 4%. Since |
| //the first value of each pair needs to match what is in the |
| //schema document and the second value needs to be valid in |
| //order to open the document we won't validate it. Need to |
| //do performance analysis of the anyuri datatype. |
| //ValueValidate = true; |
| ValueValidate = false; |
| tokenizeBuffer = true; |
| } |
| else if (XMLString::equals(suffPtr, SchemaSymbols::fgXSI_NONAMESPACESCHEMALOCATION)) |
| { |
| attrValidator = DatatypeValidatorFactory::getBuiltInRegistry()->get(SchemaSymbols::fgDT_ANYURI); |
| //We should validate this value however |
| //this lead to a performance degradation of around 4%. Since |
| //the value needs to be valid in |
| //order to open the document we won't validate it. Need to |
| //do performance analysis of the anyuri datatype. |
| //ValueValidate = true; |
| ValueValidate = false; |
| } |
| else if (XMLString::equals(suffPtr, SchemaSymbols::fgXSI_TYPE)) |
| { |
| attrValidator = DatatypeValidatorFactory::getBuiltInRegistry()->get(SchemaSymbols::fgDT_QNAME); |
| |
| ValueValidate = true; |
| } |
| else { |
| otherXSI = true; |
| } |
| } |
| |
| if (!otherXSI) { |
| normalizeAttRawValue |
| ( |
| namePtr |
| , curPair->getValue() |
| , normBuf |
| ); |
| |
| if (fValidate && attrValidator && ValueValidate) |
| { |
| ((SchemaValidator*) fValidator)->normalizeWhiteSpace(attrValidator, normBuf.getRawBuffer(), normBuf, true); |
| |
| ValidationContext* const theContext = |
| getValidationContext(); |
| |
| if (theContext) |
| { |
| try |
| { |
| if (tokenizeBuffer) { |
| XMLStringTokenizer tokenizer(normBuf.getRawBuffer(), fMemoryManager); |
| while (tokenizer.hasMoreTokens()) { |
| attrValidator->validate( |
| tokenizer.nextToken(), |
| theContext, |
| fMemoryManager); |
| } |
| } |
| else { |
| attrValidator->validate( |
| normBuf.getRawBuffer(), |
| theContext, |
| fMemoryManager); |
| } |
| } |
| catch (const XMLException& idve) |
| { |
| fValidator->emitError (XMLValid::DatatypeError, idve.getCode(), idve.getMessage()); |
| } |
| } |
| } |
| |
| if(getPSVIHandler() && fGrammarType == Grammar::SchemaGrammarType) |
| { |
| psviAttr = fPSVIAttrList->getPSVIAttributeToFill(suffPtr, fURIStringPool->getValueForId(uriId)); |
| XSSimpleTypeDefinition *validatingType = (attrValidator) |
| ? (XSSimpleTypeDefinition *)fModel->getXSObject(attrValidator) |
| : 0; |
| // no attribute declarations for these... |
| psviAttr->reset( |
| fRootElemName |
| , PSVIItem::VALIDITY_NOTKNOWN |
| , PSVIItem::VALIDATION_NONE |
| , validatingType |
| , 0 |
| , 0 |
| , false |
| , 0 |
| , attrValidator |
| ); |
| } |
| } |
| } |
| } |
| |
| if (!isNSAttr || otherXSI) |
| { |
| // Some checking for attribute wild card first (for schema) |
| bool laxThisOne = false; |
| bool skipThisOne = false; |
| |
| XMLAttDef* attDefForWildCard = 0; |
| XMLAttDef* attDef = 0; |
| |
| if (fGrammarType == Grammar::SchemaGrammarType) { |
| |
| //retrieve the att def |
| SchemaAttDef* attWildCard = 0; |
| if (currType) { |
| attDef = currType->getAttDef(suffPtr, uriId); |
| attWildCard = currType->getAttWildCard(); |
| } |
| else if (!currDV) { // check explicitly-set wildcard |
| attWildCard = ((SchemaElementDecl*)elemDecl)->getAttWildCard(); |
| } |
| |
| // if not found or faulted in - check for a matching wildcard attribute |
| // if no matching wildcard attribute, check (un)qualifed cases and flag |
| // appropriate errors |
| if (!attDef || (attDef->getCreateReason() == XMLAttDef::JustFaultIn)) { |
| |
| if (attWildCard) { |
| //if schema, see if we should lax or skip the validation of this attribute |
| if (anyAttributeValidation(attWildCard, uriId, skipThisOne, laxThisOne)) { |
| |
| if(!skipThisOne) |
| { |
| SchemaGrammar* sGrammar = (SchemaGrammar*) fGrammarResolver->getGrammar(getURIText(uriId)); |
| if (sGrammar && sGrammar->getGrammarType() == Grammar::SchemaGrammarType) { |
| RefHashTableOf<XMLAttDef>* attRegistry = sGrammar->getAttributeDeclRegistry(); |
| if (attRegistry) { |
| attDefForWildCard = attRegistry->get(suffPtr); |
| } |
| } |
| } |
| } |
| } |
| else if (currType) { |
| // not found, see if the attDef should be qualified or not |
| if (uriId == fEmptyNamespaceId) { |
| attDef = currType->getAttDef(suffPtr, fURIStringPool->getId(fGrammar->getTargetNamespace())); |
| if (fValidate |
| && attDef |
| && attDef->getCreateReason() != XMLAttDef::JustFaultIn) { |
| // the attribute should be qualified |
| fValidator->emitError |
| ( |
| XMLValid::AttributeNotQualified |
| , attDef->getFullName() |
| ); |
| fPSVIElemContext.fErrorOccurred = true; |
| if (getPSVIHandler()) |
| { |
| attrValid = PSVIItem::VALIDITY_INVALID; |
| } |
| } |
| } |
| else { |
| attDef = currType->getAttDef(suffPtr, fEmptyNamespaceId); |
| if (fValidate |
| && attDef |
| && attDef->getCreateReason() != XMLAttDef::JustFaultIn) { |
| // the attribute should be qualified |
| fValidator->emitError |
| ( |
| XMLValid::AttributeNotUnQualified |
| , attDef->getFullName() |
| ); |
| fPSVIElemContext.fErrorOccurred = true; |
| if (getPSVIHandler()) |
| { |
| attrValid = PSVIItem::VALIDITY_INVALID; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| // now need to prepare for duplicate detection |
| if(attDef) |
| { |
| unsigned int *curCountPtr = fAttDefRegistry->get(attDef); |
| if(!curCountPtr) |
| { |
| curCountPtr = getNewUIntPtr(); |
| *curCountPtr = fElemCount; |
| fAttDefRegistry->put(attDef, curCountPtr); |
| } |
| else if(*curCountPtr < fElemCount) |
| *curCountPtr = fElemCount; |
| else |
| { |
| emitError |
| ( |
| XMLErrs::AttrAlreadyUsedInSTag |
| , attDef->getFullName() |
| , elemDecl->getFullName() |
| ); |
| fPSVIElemContext.fErrorOccurred = true; |
| } |
| } |
| else |
| { |
| if(!fUndeclaredAttrRegistry->putIfNotPresent(suffPtr, uriId)) |
| { |
| emitError |
| ( |
| XMLErrs::AttrAlreadyUsedInSTag |
| , namePtr |
| , elemDecl->getFullName() |
| ); |
| fPSVIElemContext.fErrorOccurred = true; |
| } |
| } |
| |
| // if we've found either an attDef or an attDefForWildCard, |
| // then we're doing full validation and it may still be valid. |
| if(!attDef && !attDefForWildCard) |
| { |
| if(!laxThisOne && !skipThisOne) |
| { |
| fPSVIElemContext.fErrorOccurred = true; |
| } |
| if(getPSVIHandler()) |
| { |
| if(!laxThisOne && !skipThisOne) |
| { |
| attrValid = PSVIItem::VALIDITY_INVALID; |
| } |
| else if(laxThisOne) |
| { |
| attrValid = PSVIItem::VALIDITY_NOTKNOWN; |
| attrAssessed = PSVIItem::VALIDATION_PARTIAL; |
| } |
| else |
| { |
| attrValid = PSVIItem::VALIDITY_NOTKNOWN; |
| attrAssessed = PSVIItem::VALIDATION_NONE; |
| } |
| } |
| } |
| |
| bool errorCondition = fValidate && !attDefForWildCard && !attDef; |
| if (errorCondition && !skipThisOne && !laxThisOne) |
| { |
| // |
| // Its not valid for this element, so issue an error if we are |
| // validating. |
| // |
| XMLBufBid bbMsg(&fBufMgr); |
| XMLBuffer& bufMsg = bbMsg.getBuffer(); |
| if (uriId != fEmptyNamespaceId) { |
| XMLBufBid bbURI(&fBufMgr); |
| XMLBuffer& bufURI = bbURI.getBuffer(); |
| |
| getURIText(uriId, bufURI); |
| |
| bufMsg.append(chOpenCurly); |
| bufMsg.append(bufURI.getRawBuffer()); |
| bufMsg.append(chCloseCurly); |
| } |
| bufMsg.append(suffPtr); |
| fValidator->emitError |
| ( |
| XMLValid::AttNotDefinedForElement |
| , bufMsg.getRawBuffer() |
| , elemDecl->getFullName() |
| ); |
| } |
| |
| // Now normalize the raw value since we have the attribute type. We |
| // don't care about the return status here. If it failed, an error |
| // was issued, which is all we care about. |
| if (attDefForWildCard) { |
| normalizeAttValue( |
| attDefForWildCard, namePtr, curPair->getValue(), normBuf |
| ); |
| |
| // If we found an attdef for this one, then lets validate it. |
| const XMLCh* xsNormalized = normBuf.getRawBuffer(); |
| DatatypeValidator* tempDV = ((SchemaAttDef*) attDefForWildCard)->getDatatypeValidator(); |
| if (tempDV && tempDV->getWSFacet() != DatatypeValidator::PRESERVE) |
| { |
| // normalize the attribute according to schema whitespace facet |
| ((SchemaValidator*) fValidator)->normalizeWhiteSpace(tempDV, xsNormalized, fWSNormalizeBuf, true); |
| xsNormalized = fWSNormalizeBuf.getRawBuffer(); |
| if (fNormalizeData && fValidate) { |
| normBuf.set(xsNormalized); |
| } |
| } |
| |
| if (fValidate ) { |
| fValidator->validateAttrValue( |
| attDefForWildCard, xsNormalized, false, elemDecl |
| ); |
| attrValidator = ((SchemaValidator *)fValidator)->getMostRecentAttrValidator(); |
| if(((SchemaValidator *)fValidator)->getErrorOccurred()) |
| { |
| fPSVIElemContext.fErrorOccurred = true; |
| if(getPSVIHandler()) |
| attrValid = PSVIItem::VALIDITY_INVALID; |
| } |
| } |
| else { // no decl; default DOMTypeInfo to anySimpleType |
| attrValidator = DatatypeValidatorFactory::getBuiltInRegistry()->get(SchemaSymbols::fgDT_ANYSIMPLETYPE); |
| } |
| |
| // Save the type for later use |
| attType = attDefForWildCard->getType(); |
| } |
| else { |
| normalizeAttValue( |
| attDef, namePtr, curPair->getValue(), normBuf |
| ); |
| |
| // If we found an attdef for this one, then lets validate it. |
| if (attDef) |
| { |
| const XMLCh* xsNormalized = normBuf.getRawBuffer(); |
| if (fGrammarType == Grammar::SchemaGrammarType) |
| { |
| DatatypeValidator* tempDV = ((SchemaAttDef*) attDef)->getDatatypeValidator(); |
| if (tempDV && tempDV->getWSFacet() != DatatypeValidator::PRESERVE) |
| { |
| // normalize the attribute according to schema whitespace facet |
| ((SchemaValidator*) fValidator)->normalizeWhiteSpace(tempDV, xsNormalized, fWSNormalizeBuf, true); |
| xsNormalized = fWSNormalizeBuf.getRawBuffer(); |
| if (fNormalizeData && fValidate && !skipThisOne) { |
| normBuf.set(xsNormalized); |
| } |
| } |
| } |
| |
| if (fValidate && !skipThisOne) |
| { |
| fValidator->validateAttrValue( |
| attDef, xsNormalized, false, elemDecl |
| ); |
| attrValidator = ((SchemaValidator *)fValidator)->getMostRecentAttrValidator(); |
| if(((SchemaValidator *)fValidator)->getErrorOccurred()) |
| { |
| fPSVIElemContext.fErrorOccurred = true; |
| if(getPSVIHandler()) |
| attrValid = PSVIItem::VALIDITY_INVALID; |
| } |
| } |
| else { |
| attrValidator = DatatypeValidatorFactory::getBuiltInRegistry()->get(SchemaSymbols::fgDT_ANYSIMPLETYPE); |
| } |
| } |
| else { |
| attrValidator = DatatypeValidatorFactory::getBuiltInRegistry()->get(SchemaSymbols::fgDT_ANYSIMPLETYPE); |
| } |
| |
| // Save the type for later use |
| if (attDef) |
| { |
| attType = attDef->getType(); |
| } |
| } |
| |
| // now fill in the PSVIAttributes entry for this attribute: |
| if(getPSVIHandler()) |
| { |
| psviAttr = fPSVIAttrList->getPSVIAttributeToFill(suffPtr, fURIStringPool->getValueForId(uriId)); |
| SchemaAttDef *actualAttDef = 0; |
| if(attDef) |
| actualAttDef = (SchemaAttDef *)attDef; |
| else if (attDefForWildCard) |
| actualAttDef = (SchemaAttDef *)attDefForWildCard; |
| if(actualAttDef) |
| { |
| XSAttributeDeclaration *attrDecl = (XSAttributeDeclaration *)fModel->getXSObject(actualAttDef); |
| DatatypeValidator * attrDataType = actualAttDef->getDatatypeValidator(); |
| XSSimpleTypeDefinition *validatingType = (XSSimpleTypeDefinition *)fModel->getXSObject(attrDataType); |
| if(attrValid != PSVIItem::VALIDITY_VALID) |
| { |
| psviAttr->reset |
| ( |
| fRootElemName |
| , attrValid |
| , attrAssessed |
| , validatingType |
| , 0 |
| , actualAttDef->getValue() |
| , false |
| , attrDecl |
| , 0 |
| ); |
| } |
| else |
| { |
| XSSimpleTypeDefinition *memberType = 0; |
| if(validatingType->getVariety() == XSSimpleTypeDefinition::VARIETY_UNION) |
| memberType = (XSSimpleTypeDefinition *)fModel->getXSObject(attrValidator); |
| psviAttr->reset |
| ( |
| fRootElemName |
| , attrValid |
| , attrAssessed |
| , validatingType |
| , memberType |
| , actualAttDef->getValue() |
| , false |
| , attrDecl |
| , (memberType)?attrValidator:attrDataType |
| ); |
| } |
| } |
| else |
| { |
| psviAttr->reset |
| ( |
| fRootElemName |
| , attrValid |
| , attrAssessed |
| , 0 |
| , 0 |
| , 0 |
| , false |
| , 0 |
| , 0 |
| ); |
| } |
| } |
| } |
| |
| // Add this attribute to the attribute list that we use to pass them |
| // to the handler. We reuse its existing elements but expand it as |
| // required. |
| XMLAttr* curAttr; |
| if (retCount >= curAttListSize) |
| { |
| curAttr = new (fMemoryManager) XMLAttr |
| ( |
| uriId |
| , suffPtr |
| , prefPtr |
| , normBuf.getRawBuffer() |
| , attType |
| , true |
| , fMemoryManager |
| ); |
| toFill.addElement(curAttr); |
| } |
| else |
| { |
| curAttr = toFill.elementAt(retCount); |
| curAttr->set |
| ( |
| uriId |
| , suffPtr |
| , prefPtr |
| , normBuf.getRawBuffer() |
| , attType |
| ); |
| curAttr->setSpecified(true); |
| } |
| if(psviAttr) |
| psviAttr->setValue(curAttr->getValue()); |
| |
| // Bump the count of attrs in the list |
| retCount++; |
| } |
| |
| // Now, if there are any attributes declared by this element, let's |
| // go through them and make sure that any required ones are provided, |
| // and fault in any fixed ones and defaulted ones that are not provided |
| // literally. |
| if (hasDefs) |
| { |
| // Check after all specified attrs are scanned |
| // (1) report error for REQUIRED attrs that are missing (V_TAGc) |
| // (2) add default attrs if missing (FIXED and NOT_FIXED) |
| |
| XMLAttDefList& attDefList = getAttDefList(currType, elemDecl); |
| |
| for(XMLSize_t i=0; i<attDefList.getAttDefCount(); i++) |
| { |
| // Get the current att def, for convenience and its def type |
| XMLAttDef *curDef = &attDefList.getAttDef(i); |
| const XMLAttDef::DefAttTypes defType = curDef->getDefaultType(); |
| |
| unsigned int *attCountPtr = fAttDefRegistry->get(curDef); |
| if (!attCountPtr || *attCountPtr < fElemCount) |
| { // did not occur |
| // note that since there is no attribute information |
| // item present, there is no PSVI infoset to augment here *except* |
| // that the element is invalid |
| |
| //the attribute is not provided |
| if (fValidate) |
| { |
| // If we are validating and its required, then an error |
| if ((defType == XMLAttDef::Required) || |
| (defType == XMLAttDef::Required_And_Fixed) ) |
| |
| { |
| fValidator->emitError |
| ( |
| XMLValid::RequiredAttrNotProvided |
| , curDef->getFullName() |
| ); |
| fPSVIElemContext.fErrorOccurred = true; |
| } |
| else if ((defType == XMLAttDef::Default) || |
| (defType == XMLAttDef::Fixed) ) |
| { |
| if (fStandalone && curDef->isExternal()) |
| { |
| // XML 1.0 Section 2.9 |
| // Document is standalone, so attributes must not be defaulted. |
| fValidator->emitError(XMLValid::NoDefAttForStandalone, curDef->getFullName(), elemDecl->getFullName()); |
| } |
| } |
| } |
| |
| // Fault in the value if needed, and bump the att count. |
| if ((defType == XMLAttDef::Default) |
| || (defType == XMLAttDef::Fixed)) |
| { |
| // Let the validator pass judgement on the attribute value |
| if (fValidate) |
| { |
| fValidator->validateAttrValue |
| ( |
| curDef |
| , curDef->getValue() |
| , false |
| , elemDecl |
| ); |
| } |
| |
| XMLAttr* curAtt; |
| if (retCount >= curAttListSize) |
| { |
| curAtt = new (fMemoryManager) XMLAttr(fMemoryManager); |
| fValidator->faultInAttr(*curAtt, *curDef); |
| fAttrList->addElement(curAtt); |
| } |
| else |
| { |
| curAtt = fAttrList->elementAt(retCount); |
| fValidator->faultInAttr(*curAtt, *curDef); |
| } |
| |
| // Indicate it was not explicitly specified and bump count |
| curAtt->setSpecified(false); |
| retCount++; |
| if(getPSVIHandler()) |
| { |
| QName *attName = ((SchemaAttDef *)curDef)->getAttName(); |
| PSVIAttribute *defAttrToFill = fPSVIAttrList->getPSVIAttributeToFill |
| ( |
| attName->getLocalPart(), fURIStringPool->getValueForId( attName->getURI()) |
| ); |
| XSAttributeDeclaration *defAttrDecl = (XSAttributeDeclaration *)fModel->getXSObject((void *)curDef); |
| DatatypeValidator * attrDataType = ((SchemaAttDef *)curDef)->getDatatypeValidator(); |
| XSSimpleTypeDefinition *defAttrType = |
| (XSSimpleTypeDefinition*)fModel->getXSObject(attrDataType); |
| // would have occurred during validation of default value |
| if(((SchemaValidator *)fValidator)->getErrorOccurred()) |
| { |
| defAttrToFill->reset( |
| fRootElemName |
| , PSVIItem::VALIDITY_INVALID |
| , PSVIItem::VALIDATION_FULL |
| , defAttrType |
| , 0 |
| , curDef->getValue() |
| , true |
| , defAttrDecl |
| , 0 |
| ); |
| } |
| else |
| { |
| XSSimpleTypeDefinition *defAttrMemberType = 0; |
| if(defAttrType->getVariety() == XSSimpleTypeDefinition::VARIETY_UNION) |
| { |
| defAttrMemberType = (XSSimpleTypeDefinition *)fModel->getXSObject |
| ( |
| ((SchemaValidator*)fValidator)->getMostRecentAttrValidator() |
| ); |
| } |
| defAttrToFill->reset |
| ( |
| fRootElemName |
| , PSVIItem::VALIDITY_VALID |
| , PSVIItem::VALIDATION_FULL |
| , defAttrType |
| , defAttrMemberType |
| , curDef->getValue() |
| , true |
| , defAttrDecl |
| , (defAttrMemberType)?((SchemaValidator *)fValidator)->getMostRecentAttrValidator():attrDataType |
| ); |
| } |
| defAttrToFill->setValue(curDef->getValue()); |
| } |
| } |
| } |
| else if (attCountPtr) |
| { |
| //attribute is provided |
| // (schema) report error for PROHIBITED attrs that are present (V_TAGc) |
| if (defType == XMLAttDef::Prohibited && fValidate) |
| { |
| fValidator->emitError |
| ( |
| XMLValid::ProhibitedAttributePresent |
| , curDef->getFullName() |
| ); |
| fPSVIElemContext.fErrorOccurred = true; |
| if (getPSVIHandler()) |
| { |
| QName *attQName = ((SchemaAttDef *)curDef)->getAttName(); |
| // bad luck... |
| PSVIAttribute *prohibitedAttr = fPSVIAttrList->getAttributePSVIByName |
| ( |
| attQName->getLocalPart(), |
| fURIStringPool->getValueForId(attQName->getURI()) |
| ); |
| prohibitedAttr->updateValidity(PSVIItem::VALIDITY_INVALID); |
| } |
| } |
| } |
| } |
| } |
| |
| return retCount; |
| } |
| |
| |
| // This method will take a raw attribute value and normalize it according to |
| // the rules of the attribute type. It will put the resulting value into the |
| // passed buffer. |
| // |
| // This code assumes that escaped characters in the original value (via char |
| // refs) are prefixed by a 0xFFFF character. This is because some characters |
| // are legal if escaped only. And some escape chars are not subject to |
| // normalization rules. |
| bool SGXMLScanner::normalizeAttValue( const XMLAttDef* const attDef |
| , const XMLCh* const attName |
| , const XMLCh* const value |
| , XMLBuffer& toFill) |
| { |
| // A simple state value for a whitespace processing state machine |
| enum States |
| { |
| InWhitespace |
| , InContent |
| }; |
| |
| // Get the type and name |
| const XMLAttDef::AttTypes type = (attDef) |
| ?attDef->getType() |
| :XMLAttDef::CData; |
| |
| // Assume its going to go fine, and empty the target buffer in preperation |
| bool retVal = true; |
| toFill.reset(); |
| |
| // check to see if it's a tokenized type that is declared externally |
| bool isAttTokenizedExternal = (attDef) |
| ?attDef->isExternal() && (type == XMLAttDef::ID || |
| type == XMLAttDef::IDRef || |
| type == XMLAttDef::IDRefs || |
| type == XMLAttDef::Entity || |
| type == XMLAttDef::Entities || |
| type == XMLAttDef::NmToken || |
| type == XMLAttDef::NmTokens) |
| :false; |
| |
| // Loop through the chars of the source value and normalize it according |
| // to the type. |
| States curState = InContent; |
| bool firstNonWS = false; |
| XMLCh nextCh; |
| const XMLCh* srcPtr = value; |
| |
| if (type == XMLAttDef::CData || type > XMLAttDef::Notation) { |
| while (*srcPtr) { |
| // Get the next character from the source. We have to watch for |
| // escaped characters (which are indicated by a 0xFFFF value followed |
| // by the char that was escaped.) |
| nextCh = *srcPtr; |
| |
| // Do we have an escaped character ? |
| if (nextCh == 0xFFFF) |
| { |
| nextCh = *++srcPtr; |
| } |
| else if ( (nextCh <= 0x0D) && (nextCh == 0x09 || nextCh == 0x0A || nextCh == 0x0D) ) { |
| // Check Validity Constraint for Standalone document declaration |
| // XML 1.0, Section 2.9 |
| if (fStandalone && fValidate && isAttTokenizedExternal) |
| { |
| // Can't have a standalone document declaration of "yes" if attribute |
| // values are subject to normalisation |
| fValidator->emitError(XMLValid::NoAttNormForStandalone, attName); |
| } |
| nextCh = chSpace; |
| } |
| else if (nextCh == chOpenAngle) { |
| // If its not escaped, then make sure its not a < character, which is |
| // not allowed in attribute values. |
| emitError(XMLErrs::BracketInAttrValue, attName); |
| retVal = false; |
| } |
| |
| // Add this char to the target buffer |
| toFill.append(nextCh); |
| |
| // And move up to the next character in the source |
| srcPtr++; |
| } |
| } |
| else { |
| while (*srcPtr) |
| { |
| // Get the next character from the source. We have to watch for |
| // escaped characters (which are indicated by a 0xFFFF value followed |
| // by the char that was escaped.) |
| nextCh = *srcPtr; |
| |
| // Do we have an escaped character ? |
| if (nextCh == 0xFFFF) |
| { |
| nextCh = *++srcPtr; |
| } |
| else if (nextCh == chOpenAngle) { |
| // If its not escaped, then make sure its not a < character, which is |
| // not allowed in attribute values. |
| emitError(XMLErrs::BracketInAttrValue, attName); |
| retVal = false; |
| } |
| |
| if (curState == InWhitespace) |
| { |
| if (!fReaderMgr.getCurrentReader()->isWhitespace(nextCh)) |
| { |
| if (firstNonWS) |
| toFill.append(chSpace); |
| curState = InContent; |
| firstNonWS = true; |
| } |
| else |
| { |
| srcPtr++; |
| continue; |
| } |
| } |
| else if (curState == InContent) |
| { |
| if (fReaderMgr.getCurrentReader()->isWhitespace(nextCh)) |
| { |
| curState = InWhitespace; |
| srcPtr++; |
| |
| // Check Validity Constraint for Standalone document declaration |
| // XML 1.0, Section 2.9 |
| if (fStandalone && fValidate && isAttTokenizedExternal) |
| { |
| if (!firstNonWS || (nextCh != chSpace && *srcPtr && fReaderMgr.getCurrentReader()->isWhitespace(*srcPtr))) |
| { |
| // Can't have a standalone document declaration of "yes" if attribute |
| // values are subject to normalisation |
| fValidator->emitError(XMLValid::NoAttNormForStandalone, attName); |
| } |
| } |
| continue; |
| } |
| firstNonWS = true; |
| } |
| |
| // Add this char to the target buffer |
| toFill.append(nextCh); |
| |
| // And move up to the next character in the source |
| srcPtr++; |
| } |
| } |
| |
| return retVal; |
| } |
| |
| // This method will just normalize the input value as CDATA without |
| // any standalone checking. |
| bool SGXMLScanner::normalizeAttRawValue( const XMLCh* const attrName |
| , const XMLCh* const value |
| , XMLBuffer& toFill) |
| { |
| // Assume its going to go fine, and empty the target buffer in preperation |
| bool retVal = true; |
| toFill.reset(); |
| |
| // Loop through the chars of the source value and normalize it according |
| // to the type. |
| bool escaped; |
| XMLCh nextCh; |
| const XMLCh* srcPtr = value; |
| while (*srcPtr) |
| { |
| // Get the next character from the source. We have to watch for |
| // escaped characters (which are indicated by a 0xFFFF value followed |
| // by the char that was escaped.) |
| nextCh = *srcPtr; |
| escaped = (nextCh == 0xFFFF); |
| if (escaped) |
| nextCh = *++srcPtr; |
| |
| // If its not escaped, then make sure its not a < character, which is |
| // not allowed in attribute values. |
| if (!escaped && (*srcPtr == chOpenAngle)) |
| { |
| emitError(XMLErrs::BracketInAttrValue, attrName); |
| retVal = false; |
| } |
| |
| if (!escaped) |
| { |
| // NOTE: Yes this is a little redundant in that a 0x20 is |
| // replaced with an 0x20. But its faster to do this (I think) |
| // than checking for 9, A, and D separately. |
| if (fReaderMgr.getCurrentReader()->isWhitespace(nextCh)) |
| nextCh = chSpace; |
| } |
| |
| // Add this char to the target buffer |
| toFill.append(nextCh); |
| |
| // And move up to the next character in the source |
| srcPtr++; |
| } |
| return retVal; |
| } |
| |
| // This method will reset the scanner data structures, and related plugged |
| // in stuff, for a new scan session. We get the input source for the primary |
| // XML entity, create the reader for it, and push it on the stack so that |
| // upon successful return from here we are ready to go. |
| void SGXMLScanner::scanReset(const InputSource& src) |
| { |
| |
| // This call implicitly tells us that we are going to reuse the scanner |
| // if it was previously used. So tell the validator to reset itself. |
| // |
| // But, if the fUseCacheGrammar flag is set, then don't reset it. |
| // |
| // NOTE: The ReaderMgr is flushed on the way out, because that is |
| // required to insure that files are closed. |
| fGrammarResolver->cacheGrammarFromParse(fToCacheGrammar); |
| fGrammarResolver->useCachedGrammarInParse(fUseCachedGrammar); |
| |
| // Clear transient schema info list. |
| // |
| fSchemaInfoList->removeAll (); |
| |
| // fModel may need updating, as fGrammarResolver could have cleaned it |
| if(fModel && getPSVIHandler()) |
| fModel = fGrammarResolver->getXSModel(); |
| |
| // Create dummy schema grammar |
| if (!fSchemaGrammar) { |
| fSchemaGrammar = new (fGrammarPoolMemoryManager) SchemaGrammar(fGrammarPoolMemoryManager); |
| } |
| |
| fGrammar = fSchemaGrammar; |
| fGrammarType = Grammar::DTDGrammarType; |
| fRootGrammar = 0; |
| |
| fValidator->setGrammar(fGrammar); |
| if (fValidatorFromUser) { |
| |
| ((SchemaValidator*) fValidator)->setErrorReporter(fErrorReporter); |
| ((SchemaValidator*) fValidator)->setGrammarResolver(fGrammarResolver); |
| ((SchemaValidator*) fValidator)->setExitOnFirstFatal(fExitOnFirstFatal); |
| } |
| |
| // Reset validation |
| fValidate = (fValScheme == Val_Always) ? true : false; |
| |
| // And for all installed handlers, send reset events. This gives them |
| // a chance to flush any cached data. |
| if (fDocHandler) |
| fDocHandler->resetDocument(); |
| if (fEntityHandler) |
| fEntityHandler->resetEntities(); |
| if (fErrorReporter) |
| fErrorReporter->resetErrors(); |
| |
| // Clear out the id reference list |
| resetValidationContext(); |
| |
| // Reset the Root Element Name |
| fMemoryManager->deallocate(fRootElemName);//delete [] fRootElemName; |
| fRootElemName = 0; |
| |
| // Reset IdentityConstraints |
| if (fICHandler) |
| fICHandler->reset(); |
| |
| // Reset the element stack, and give it the latest ids for the special |
| // URIs it has to know about. |
| fElemStack.reset |
| ( |
| fEmptyNamespaceId |
| , fUnknownNamespaceId |
| , fXMLNamespaceId |
| , fXMLNSNamespaceId |
| ); |
| |
| if (!fSchemaNamespaceId) |
| fSchemaNamespaceId = fURIStringPool->addOrFind(SchemaSymbols::fgURI_XSI); |
| |
| // Reset some status flags |
| fInException = false; |
| fStandalone = false; |
| fErrorCount = 0; |
| fHasNoDTD = true; |
| fSeeXsi = false; |
| fDoNamespaces = true; |
| fDoSchema = true; |
| |
| // Reset PSVI context |
| // Note that we always need this around for DOMTypeInfo |
| if (!fPSVIElement) |
| fPSVIElement = new (fMemoryManager) PSVIElement(fMemoryManager); |
| |
| if (!fErrorStack) |
| { |
| fErrorStack = new (fMemoryManager) ValueStackOf<bool>(8, fMemoryManager); |
| } |
| else |
| { |
| fErrorStack->removeAllElements(); |
| } |
| |
| resetPSVIElemContext(); |
| |
| // Reset the validators |
| fSchemaValidator->reset(); |
| fSchemaValidator->setErrorReporter(fErrorReporter); |
| fSchemaValidator->setExitOnFirstFatal(fExitOnFirstFatal); |
| fSchemaValidator->setGrammarResolver(fGrammarResolver); |
| if (fValidatorFromUser) |
| fValidator->reset(); |
| |
| // Handle the creation of the XML reader object for this input source. |
| // This will provide us with transcoding and basic lexing services. |
| XMLReader* newReader = fReaderMgr.createReader |
| ( |
| src |
| , true |
| , XMLReader::RefFrom_NonLiteral |
| , XMLReader::Type_General |
| , XMLReader::Source_External |
| , fCalculateSrcOfs |
| , fLowWaterMark |
| ); |
| |
| if (!newReader) { |
| if (src.getIssueFatalErrorIfNotFound()) |
| ThrowXMLwithMemMgr1(RuntimeException, XMLExcepts::Scan_CouldNotOpenSource, src.getSystemId(), fMemoryManager); |
| else |
| ThrowXMLwithMemMgr1(RuntimeException, XMLExcepts::Scan_CouldNotOpenSource_Warning, src.getSystemId(), fMemoryManager); |
| } |
| |
| // Push this read onto the reader manager |
| fReaderMgr.pushReader(newReader, 0); |
| |
| // and reset security-related things if necessary: |
| if(fSecurityManager != 0) |
| { |
| fEntityExpansionLimit = fSecurityManager->getEntityExpansionLimit(); |
| fEntityExpansionCount = 0; |
| } |
| fElemCount = 0; |
| if(fUIntPoolRowTotal >= 32) |
| { // 8 KB tied up with validating attributes... |
| fAttDefRegistry->removeAll(); |
| recreateUIntPool(); |
| } |
| else |
| { |
| // note that this will implicitly reset the values of the hashtables, |
| // though their buckets will still be tied up |
| resetUIntPool(); |
| } |
| fUndeclaredAttrRegistry->removeAll(); |
| } |
| |
| |
| // This method is called between markup in content. It scans for character |
| // data that is sent to the document handler. It watches for any markup |
| // characters that would indicate that the character data has ended. It also |
| // handles expansion of general and character entities. |
| // |
| // sendData() is a local static helper for this method which handles some |
| // code that must be done in three different places here. |
| void SGXMLScanner::sendCharData(XMLBuffer& toSend) |
| { |
| // If no data in the buffer, then nothing to do |
| if (toSend.isEmpty()) |
| return; |
| |
| // We do different things according to whether we are validating or |
| // not. If not, its always just characters; else, it depends on the |
| // current element's content model. |
| if (fValidate) |
| { |
| // Get the raw data we need for the callback |
| const XMLCh* rawBuf = toSend.getRawBuffer(); |
| const XMLSize_t len = toSend.getLen(); |
| |
| // Get the character data opts for the current element |
| XMLElementDecl::CharDataOpts charOpts = XMLElementDecl::AllCharData; |
| // And see if the current element is a 'Children' style content model |
| ComplexTypeInfo *currType = ((SchemaValidator*)fValidator)->getCurrentTypeInfo(); |
| if(currType) |
| { |
| SchemaElementDecl::ModelTypes modelType = (SchemaElementDecl::ModelTypes) currType->getContentType(); |
| if(modelType == SchemaElementDecl::Children || |
| modelType == SchemaElementDecl::ElementOnlyEmpty) |
| charOpts = XMLElementDecl::SpacesOk; |
| else if(modelType == SchemaElementDecl::Empty) |
| charOpts = XMLElementDecl::NoCharData; |
| } |
| |
| // should not be necessary once PSVI method on element decls |
| // are removed |
| if (charOpts == XMLElementDecl::NoCharData) |
| { |
| // They definitely cannot handle any type of char data |
| fValidator->emitError(XMLValid::NoCharDataInCM); |
| if (getPSVIHandler()) |
| { |
| // REVISIT: |
| // PSVIElement->setValidity(PSVIItem::VALIDITY_INVALID); |
| } |
| } |
| else if (fReaderMgr.getCurrentReader()->isAllSpaces(rawBuf, len)) |
| { |
| // Its all spaces. So, if they can take spaces, then send it |
| // as ignorable whitespace. If they can handle any char data |
| // send it as characters. |
| if (charOpts == XMLElementDecl::SpacesOk) { |
| if (fDocHandler) |
| fDocHandler->ignorableWhitespace(rawBuf, len, false); |
| } |
| else if (charOpts == XMLElementDecl::AllCharData) |
| { |
| XMLSize_t xsLen; |
| const XMLCh* xsNormalized; |
| DatatypeValidator* tempDV = ((SchemaValidator*) fValidator)->getCurrentDatatypeValidator(); |
| if (tempDV && tempDV->getWSFacet() != DatatypeValidator::PRESERVE) |
| { |
| // normalize the character according to schema whitespace facet |
| ((SchemaValidator*) fValidator)->normalizeWhiteSpace(tempDV, rawBuf, fWSNormalizeBuf); |
| xsNormalized = fWSNormalizeBuf.getRawBuffer(); |
| xsLen = fWSNormalizeBuf.getLen(); |
| } |
| else { |
| xsNormalized = rawBuf; |
| xsLen = len; |
| } |
| |
| // tell the schema validation about the character data for checkContent later |
| ((SchemaValidator*)fValidator)->setDatatypeBuffer(xsNormalized); |
| |
| // call all active identity constraints |
| if (toCheckIdentityConstraint() && fICHandler->getMatcherCount()) { |
| fContent.append(xsNormalized, xsLen); |
| } |
| |
| if (fDocHandler) { |
| if (fNormalizeData) { |
| fDocHandler->docCharacters(xsNormalized, xsLen, false); |
| } |
| else { |
| fDocHandler->docCharacters(rawBuf, len, false); |
| } |
| } |
| } |
| } |
| else |
| { |
| // If they can take any char data, then send it. Otherwise, they |
| // can only handle whitespace and can't handle this stuff so |
| // issue an error. |
| if (charOpts == XMLElementDecl::AllCharData) |
| { |
| XMLSize_t xsLen; |
| const XMLCh *xsNormalized; |
| DatatypeValidator* tempDV = ((SchemaValidator*) fValidator)->getCurrentDatatypeValidator(); |
| if (tempDV && tempDV->getWSFacet() != DatatypeValidator::PRESERVE) |
| { |
| ((SchemaValidator*) fValidator)->normalizeWhiteSpace(tempDV, rawBuf, fWSNormalizeBuf); |
| xsNormalized = fWSNormalizeBuf.getRawBuffer(); |
| xsLen = fWSNormalizeBuf.getLen(); |
| } |
| else { |
| xsNormalized = rawBuf; |
| xsLen = len; |
| } |
| |
| // tell the schema validation about the character data for checkContent later |
| ((SchemaValidator*)fValidator)->setDatatypeBuffer(xsNormalized); |
| |
| // call all active identity constraints |
| if (toCheckIdentityConstraint() && fICHandler->getMatcherCount()) { |
| fContent.append(xsNormalized, xsLen); |
| } |
| |
| if (fDocHandler) { |
| if (fNormalizeData) { |
| fDocHandler->docCharacters(xsNormalized, xsLen, false); |
| } |
| else { |
| fDocHandler->docCharacters(rawBuf, len, false); |
| } |
| } |
| } |
| else |
| { |
| fValidator->emitError(XMLValid::NoCharDataInCM); |
| if (getPSVIHandler()) |
| { |
| // REVISIT: |
| // PSVIElement->setValidity(PSVIItem::VALIDITY_INVALID); |
| } |
| } |
| } |
| } |
| else |
| { |
| // call all active identity constraints |
| if (toCheckIdentityConstraint() && fICHandler->getMatcherCount()) |
| fContent.append(toSend.getRawBuffer(), toSend.getLen()); |
| |
| // Always assume its just char data if not validating |
| if (fDocHandler) |
| fDocHandler->docCharacters(toSend.getRawBuffer(), toSend.getLen(), false); |
| } |
| |
| // Reset buffer |
| toSend.reset(); |
| } |
| |
| |
| |
| // This method is called with a key/value string pair that represents an |
| // xmlns="yyy" or xmlns:xxx="yyy" attribute. This method will update the |
| // current top of the element stack based on this data. We know that when |
| // we get here, that it is one of these forms, so we don't bother confirming |
| // it. |
| // |
| // But we have to ensure |
| // 1. xxx is not xmlns |
| // 2. if xxx is xml, then yyy must match XMLUni::fgXMLURIName, and vice versa |
| // 3. yyy is not XMLUni::fgXMLNSURIName |
| // 4. if xxx is not null, then yyy cannot be an empty string. |
| void SGXMLScanner::updateNSMap(const XMLCh* const attrName |
| , const XMLCh* const attrValue) |
| { |
| updateNSMap(attrName, attrValue, XMLString::indexOf(attrName, chColon)); |
| } |
| |
| void SGXMLScanner::updateNSMap(const XMLCh* const attrName |
| , const XMLCh* const attrValue |
| , const int colonOfs) |
| { |
| // We need a buffer to normalize the attribute value into |
| XMLBufBid bbNormal(&fBufMgr); |
| XMLBuffer& normalBuf = bbNormal.getBuffer(); |
| |
| // Normalize the value into the passed buffer. In this case, we don't |
| // care about the return value. An error was issued for the error, which |
| // is all we care about here. |
| normalizeAttRawValue(attrName, attrValue, normalBuf); |
| XMLCh* namespaceURI = normalBuf.getRawBuffer(); |
| |
| // We either have the default prefix (""), or we point it into the attr |
| // name parameter. Note that the xmlns is not the prefix we care about |
| // here. To us, the 'prefix' is really the local part of the attrName |
| // parameter. |
| // |
| // Check 1. xxx is not xmlns |
| // 2. if xxx is xml, then yyy must match XMLUni::fgXMLURIName, and vice versa |
| // 3. yyy is not XMLUni::fgXMLNSURIName |
| // 4. if xxx is not null, then yyy cannot be an empty string. |
| const XMLCh* prefPtr = XMLUni::fgZeroLenString; |
| if (colonOfs != -1) { |
| prefPtr = &attrName[colonOfs + 1]; |
| |
| if (XMLString::equals(prefPtr, XMLUni::fgXMLNSString)) |
| emitError(XMLErrs::NoUseOfxmlnsAsPrefix); |
| else if (XMLString::equals(prefPtr, XMLUni::fgXMLString)) { |
| if (!XMLString::equals(namespaceURI, XMLUni::fgXMLURIName)) |
| emitError(XMLErrs::PrefixXMLNotMatchXMLURI); |
| } |
| |
| if (!namespaceURI) |
| emitError(XMLErrs::NoEmptyStrNamespace, attrName); |
| else if(!*namespaceURI && fXMLVersion == XMLReader::XMLV1_0) |
| emitError(XMLErrs::NoEmptyStrNamespace, attrName); |
| } |
| |
| if (XMLString::equals(namespaceURI, XMLUni::fgXMLNSURIName)) |
| emitError(XMLErrs::NoUseOfxmlnsURI); |
| else if (XMLString::equals(namespaceURI, XMLUni::fgXMLURIName)) { |
| if (!XMLString::equals(prefPtr, XMLUni::fgXMLString)) |
| emitError(XMLErrs::XMLURINotMatchXMLPrefix); |
| } |
| |
| // Ok, we have to get the unique id for the attribute value, which is the |
| // URI that this value should be mapped to. The validator has the |
| // namespace string pool, so we ask him to find or add this new one. Then |
| // we ask the element stack to add this prefix to URI Id mapping. |
| fElemStack.addPrefix |
| ( |
| prefPtr |
| , fURIStringPool->addOrFind(namespaceURI) |
| ); |
| } |
| |
| void SGXMLScanner::scanRawAttrListforNameSpaces(XMLSize_t attCount) |
| { |
| // Make an initial pass through the list and find any xmlns attributes or |
| // schema attributes. |
| // When we find one, send it off to be used to update the element stack's |
| // namespace mappings. |
| for (XMLSize_t index = 0; index < attCount; index++) |
| { |
| // each attribute has the prefix:suffix="value" |
| const KVStringPair* curPair = fRawAttrList->elementAt(index); |
| const XMLCh* rawPtr = curPair->getKey(); |
| |
| // If either the key begins with "xmlns:" or its just plain |
| // "xmlns", then use it to update the map. |
| if (!XMLString::compareNString(rawPtr, XMLUni::fgXMLNSColonString, 6) |
| || XMLString::equals(rawPtr, XMLUni::fgXMLNSString)) |
| { |
| const XMLCh* valuePtr = curPair->getValue(); |
| |
| updateNSMap(rawPtr, valuePtr, fRawAttrColonList[index]); |
| |
| // if the schema URI is seen in the the valuePtr, set the boolean seeXsi |
| if (XMLString::equals(valuePtr, SchemaSymbols::fgURI_XSI)) { |
| fSeeXsi = true; |
| } |
| } |
| } |
| |
| // walk through the list again to deal with "xsi:...." |
| if (fSeeXsi) |
| { |
| for (XMLSize_t index = 0; index < attCount; index++) |
| { |
| // each attribute has the prefix:suffix="value" |
| const KVStringPair* curPair = fRawAttrList->elementAt(index); |
| const XMLCh* rawPtr = curPair->getKey(); |
| const XMLCh* prefPtr; |
| |
| int colonInd = fRawAttrColonList[index]; |
| |
| if (colonInd != -1) { |
| fURIBuf.set(rawPtr, colonInd); |
| prefPtr = fURIBuf.getRawBuffer(); |
| } |
| else { |
| prefPtr = XMLUni::fgZeroLenString; |
| } |
| |
| // if schema URI has been seen, scan for the schema location and uri |
| // and resolve the schema grammar |
| if (resolvePrefix(prefPtr, ElemStack::Mode_Attribute) == fSchemaNamespaceId) { |
| |
| const XMLCh* valuePtr = curPair->getValue(); |
| const XMLCh* suffPtr = &rawPtr[colonInd + 1]; |
| |
| if (XMLString::equals(suffPtr, SchemaSymbols::fgXSI_SCHEMALOCATION)) |
| parseSchemaLocation(valuePtr); |
| else if (XMLString::equals(suffPtr, SchemaSymbols::fgXSI_NONAMESPACESCHEMALOCATION)) |
| resolveSchemaGrammar(valuePtr, XMLUni::fgZeroLenString); |
| } |
| } |
| |
| // do it another time, as xsi:type and xsi:nill only work if the schema grammar has been already |
| // loaded (JIRA XERCESC-1937) |
| if (fValidator && fValidator->handlesSchema()) |
| { |
| for (XMLSize_t index = 0; index < attCount; index++) |
| { |
| // each attribute has the prefix:suffix="value" |
| const KVStringPair* curPair = fRawAttrList->elementAt(index); |
| const XMLCh* rawPtr = curPair->getKey(); |
| const XMLCh* prefPtr; |
| |
| int colonInd = fRawAttrColonList[index]; |
| |
| if (colonInd != -1) { |
| fURIBuf.set(rawPtr, colonInd); |
| prefPtr = fURIBuf.getRawBuffer(); |
| } |
| else { |
| prefPtr = XMLUni::fgZeroLenString; |
| } |
| |
| // scan for schema type |
| if (resolvePrefix(prefPtr, ElemStack::Mode_Attribute) == fSchemaNamespaceId) { |
| |
| const XMLCh* valuePtr = curPair->getValue(); |
| const XMLCh* suffPtr = &rawPtr[colonInd + 1]; |
| |
| if (XMLString::equals(suffPtr, SchemaSymbols::fgXSI_TYPE)) |
| { |
| XMLBufBid bbXsi(&fBufMgr); |
| XMLBuffer& fXsiType = bbXsi.getBuffer(); |
| |
| // normalize the attribute according to schema whitespace facet |
| DatatypeValidator* tempDV = DatatypeValidatorFactory::getBuiltInRegistry()->get(SchemaSymbols::fgDT_QNAME); |
| normalizeAttRawValue(SchemaSymbols::fgXSI_TYPE, valuePtr, fXsiType); |
| ((SchemaValidator*) fValidator)->normalizeWhiteSpace(tempDV, fXsiType.getRawBuffer(), fXsiType, true); |
| if (!fXsiType.isEmpty()) { |
| int colonPos = -1; |
| unsigned int uriId = resolveQName ( |
| fXsiType.getRawBuffer() |
| , fPrefixBuf |
| , ElemStack::Mode_Element |
| , colonPos |
| ); |
| ((SchemaValidator*)fValidator)->setXsiType(fPrefixBuf.getRawBuffer(), fXsiType.getRawBuffer() + colonPos + 1, uriId); |
| } |
| } |
| else if (XMLString::equals(suffPtr, SchemaSymbols::fgATT_NILL)) |
| { |
| // normalize the attribute according to schema whitespace facet |
| XMLBufBid bbXsi(&fBufMgr); |
| XMLBuffer& fXsiNil = bbXsi.getBuffer(); |
| |
| DatatypeValidator* tempDV = DatatypeValidatorFactory::getBuiltInRegistry()->get(SchemaSymbols::fgDT_BOOLEAN); |
| normalizeAttRawValue(SchemaSymbols::fgATT_NILL, valuePtr, fXsiNil); |
| ((SchemaValidator*) fValidator)->normalizeWhiteSpace(tempDV, fXsiNil.getRawBuffer(), fXsiNil, true); |
| if(XMLString::equals(fXsiNil.getRawBuffer(), SchemaSymbols::fgATTVAL_TRUE)) |
| ((SchemaValidator*)fValidator)->setNillable(true); |
| else if(XMLString::equals(fXsiNil.getRawBuffer(), SchemaSymbols::fgATTVAL_FALSE)) |
| ((SchemaValidator*)fValidator)->setNillable(false); |
| else |
| emitError(XMLErrs::InvalidAttValue, fXsiNil.getRawBuffer(), valuePtr); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| void SGXMLScanner::parseSchemaLocation(const XMLCh* const schemaLocationStr, bool ignoreLoadSchema) |
| { |
| BaseRefVectorOf<XMLCh>* schemaLocation = XMLString::tokenizeString(schemaLocationStr, fMemoryManager); |
| Janitor<BaseRefVectorOf<XMLCh> > janLoc(schemaLocation); |
| |
| XMLSize_t size = schemaLocation->size(); |
| if (size % 2 != 0 ) { |
| emitError(XMLErrs::BadSchemaLocation); |
| } else { |
| // We need a buffer to normalize the attribute value into |
| XMLBuffer normalBuf(1023, fMemoryManager); |
| for(XMLSize_t i=0; i<size; i=i+2) { |
| normalizeAttRawValue(SchemaSymbols::fgXSI_SCHEMALOCATION, schemaLocation->elementAt(i), normalBuf); |
| resolveSchemaGrammar(schemaLocation->elementAt(i+1), normalBuf.getRawBuffer(), ignoreLoadSchema); |
| } |
| } |
| } |
| |
| void SGXMLScanner::resolveSchemaGrammar(const XMLCh* const loc, const XMLCh* const uri, bool ignoreLoadSchema) { |
| |
| Grammar* grammar = 0; |
| |
| { |
| XMLSchemaDescriptionImpl theSchemaDescription(uri, fMemoryManager); |
| theSchemaDescription.setLocationHints(loc); |
| grammar = fGrammarResolver->getGrammar(&theSchemaDescription); |
| } |
| |
| // If multi-import is enabled, make sure the existing grammar came |
| // from the import directive. Otherwise we may end up reloading |
| // the same schema that came from the external grammar pool. Ideally, |
| // we would move fSchemaInfoList to XMLGrammarPool so that it survives |
| // the destruction of the scanner in which case we could rely on the |
| // same logic we use to weed out duplicate schemas below. |
| // |
| if (!grammar || grammar->getGrammarType() == Grammar::DTDGrammarType || |
| (getHandleMultipleImports() && |
| ((XMLSchemaDescription*)grammar->getGrammarDescription())-> |
| getContextType () == XMLSchemaDescription::CONTEXT_IMPORT)) |
| { |
| if (fLoadSchema || ignoreLoadSchema) |
| { |
| XSDDOMParser parser(0, fMemoryManager, 0); |
| |
| parser.setValidationScheme(XercesDOMParser::Val_Never); |
| parser.setDoNamespaces(true); |
| parser.setUserEntityHandler(fEntityHandler); |
| parser.setUserErrorReporter(fErrorReporter); |
| |
| //Normalize sysId |
| XMLBufBid nnSys(&fBufMgr); |
| XMLBuffer& normalizedSysId = nnSys.getBuffer(); |
| XMLString::removeChar(loc, 0xFFFF, normalizedSysId); |
| const XMLCh* normalizedURI = normalizedSysId.getRawBuffer(); |
| |
| // Create a buffer for expanding the system id |
| XMLBufBid bbSys(&fBufMgr); |
| XMLBuffer& expSysId = bbSys.getBuffer(); |
| |
| // Allow the entity handler to expand the system id if they choose |
| // to do so. |
| InputSource* srcToFill = 0; |
| if (fEntityHandler) |
| { |
| if (!fEntityHandler->expandSystemId(normalizedURI, expSysId)) |
| expSysId.set(normalizedURI); |
| |
| ReaderMgr::LastExtEntityInfo lastInfo; |
| fReaderMgr.getLastExtEntityInfo(lastInfo); |
| XMLResourceIdentifier resourceIdentifier(XMLResourceIdentifier::SchemaGrammar, |
| expSysId.getRawBuffer(), uri, XMLUni::fgZeroLenString, lastInfo.systemId, |
| &fReaderMgr); |
| srcToFill = fEntityHandler->resolveEntity(&resourceIdentifier); |
| } |
| else |
| { |
| expSysId.set(normalizedURI); |
| } |
| |
| // If they didn't create a source via the entity handler, then we |
| // have to create one on our own. |
| if (!srcToFill) |
| { |
| if (fDisableDefaultEntityResolution) |
| return; |
| |
| ReaderMgr::LastExtEntityInfo lastInfo; |
| fReaderMgr.getLastExtEntityInfo(lastInfo); |
| |
| XMLURL urlTmp(fMemoryManager); |
| if ((!urlTmp.setURL(lastInfo.systemId, expSysId.getRawBuffer(), urlTmp)) || |
| (urlTmp.isRelative())) |
| { |
| if (!fStandardUriConformant) |
| { |
| XMLBufBid ddSys(&fBufMgr); |
| XMLBuffer& resolvedSysId = ddSys.getBuffer(); |
| XMLUri::normalizeURI(expSysId.getRawBuffer(), resolvedSysId); |
| |
| srcToFill = new (fMemoryManager) LocalFileInputSource |
| ( |
| lastInfo.systemId |
| , resolvedSysId.getRawBuffer() |
| , fMemoryManager |
| ); |
| } |
| else |
| ThrowXMLwithMemMgr(MalformedURLException, XMLExcepts::URL_MalformedURL, fMemoryManager); |
| } |
| else |
| { |
| if (fStandardUriConformant && urlTmp.hasInvalidChar()) |
| ThrowXMLwithMemMgr(MalformedURLException, XMLExcepts::URL_MalformedURL, fMemoryManager); |
| |
| srcToFill = new (fMemoryManager) URLInputSource(urlTmp, fMemoryManager); |
| } |
| } |
| |
| // Put a janitor on the input source |
| Janitor<InputSource> janSrc(srcToFill); |
| |
| // Check if this exact schema has already been seen. |
| // |
| const XMLCh* sysId = srcToFill->getSystemId(); |
| unsigned int uriId = (uri && *uri) ? fURIStringPool->addOrFind(uri) : fEmptyNamespaceId; |
| SchemaInfo* importSchemaInfo = 0; |
| |
| if (fUseCachedGrammar) |
| importSchemaInfo = fCachedSchemaInfoList->get(sysId, uriId); |
| |
| if (!importSchemaInfo && !fToCacheGrammar) |
| importSchemaInfo = fSchemaInfoList->get(sysId, uriId); |
| |
| if (importSchemaInfo) |
| { |
| // We haven't added any new grammars so it is safe to just |
| // return. |
| // |
| return; |
| } |
| |
| // Should just issue warning if the schema is not found |
| bool flag = srcToFill->getIssueFatalErrorIfNotFound(); |
| srcToFill->setIssueFatalErrorIfNotFound(false); |
| |
| parser.parse(*srcToFill); |
| |
| // Reset the InputSource |
| srcToFill->setIssueFatalErrorIfNotFound(flag); |
| |
| if (parser.getSawFatal() && fExitOnFirstFatal) |
| emitError(XMLErrs::SchemaScanFatalError); |
| |
| DOMDocument* document = parser.getDocument(); //Our Grammar |
| |
| if (document != 0) { |
| |
| DOMElement* root = document->getDocumentElement();// This is what we pass to TraverserSchema |
| if (root != 0) |
| { |
| const XMLCh* newUri = root->getAttribute(SchemaSymbols::fgATT_TARGETNAMESPACE); |
| bool newGrammar = false; |
| if (!XMLString::equals(newUri, uri)) { |
| if (fValidate || fValScheme == Val_Auto) { |
| fValidator->emitError(XMLValid::WrongTargetNamespace, loc, uri); |
| } |
| |
| grammar = fGrammarResolver->getGrammar(newUri); |
| newGrammar = true; |
| } |
| |
| if (!grammar || |
| grammar->getGrammarType() == Grammar::DTDGrammarType || |
| (getHandleMultipleImports() && |
| ((XMLSchemaDescription*) grammar->getGrammarDescription())-> |
| getContextType () == XMLSchemaDescription::CONTEXT_IMPORT)) |
| { |
| // If we switched namespace URI, recheck the schema info. |
| // |
| if (newGrammar) |
| { |
| unsigned int newUriId = (newUri && *newUri) ? fURIStringPool->addOrFind(newUri) : fEmptyNamespaceId; |
| |
| if (fUseCachedGrammar) |
| importSchemaInfo = fCachedSchemaInfoList->get(sysId, newUriId); |
| |
| if (!importSchemaInfo && !fToCacheGrammar) |
| importSchemaInfo = fSchemaInfoList->get(sysId, newUriId); |
| |
| if (importSchemaInfo) |
| return; |
| } |
| |
| // Since we have seen a grammar, set our validation flag |
| // at this point if the validation scheme is auto |
| if (fValScheme == Val_Auto && !fValidate) { |
| fValidate = true; |
| fElemStack.setValidationFlag(fValidate); |
| } |
| |
| bool grammarFound = grammar && |
| grammar->getGrammarType() == Grammar::SchemaGrammarType; |
| |
| SchemaGrammar* schemaGrammar; |
| |
| if (grammarFound) { |
| schemaGrammar = (SchemaGrammar*) grammar; |
| } |
| else { |
| schemaGrammar = new (fGrammarPoolMemoryManager) SchemaGrammar(fGrammarPoolMemoryManager); |
| } |
| |
| XMLSchemaDescription* gramDesc = (XMLSchemaDescription*) schemaGrammar->getGrammarDescription(); |
| |
| gramDesc->setContextType(XMLSchemaDescription::CONTEXT_PREPARSE); |
| gramDesc->setLocationHints(sysId); |
| |
| TraverseSchema traverseSchema |
| ( |
| root |
| , fURIStringPool |
| , schemaGrammar |
| , fGrammarResolver |
| , fUseCachedGrammar ? fCachedSchemaInfoList : fSchemaInfoList |
| , fToCacheGrammar ? fCachedSchemaInfoList : fSchemaInfoList |
| , this |
| , sysId |
| , fEntityHandler |
| , fErrorReporter |
| , fMemoryManager |
| , grammarFound |
| ); |
| |
| // Reset the now invalid schema roots in the collected |
| // schema info entries. |
| // |
| { |
| RefHash2KeysTableOfEnumerator<SchemaInfo> i ( |
| fToCacheGrammar ? fCachedSchemaInfoList : fSchemaInfoList); |
| |
| while (i.hasMoreElements ()) |
| i.nextElement().resetRoot (); |
| } |
| |
| if (fGrammarType == Grammar::DTDGrammarType) { |
| fGrammar = schemaGrammar; |
| fGrammarType = Grammar::SchemaGrammarType; |
| fValidator->setGrammar(fGrammar); |
| } |
| |
| if (fValidate) { |
| // validate the Schema scan so far |
| fValidator->preContentValidation(false); |
| } |
| } |
| } |
| } |
| } |
| } |
| else |
| { |
| // Since we have seen a grammar, set our validation flag |
| // at this point if the validation scheme is auto |
| if (fValScheme == Val_Auto && !fValidate) { |
| fValidate = true; |
| fElemStack.setValidationFlag(fValidate); |
| } |
| |
| // we have seen a schema, so set up the fValidator as fSchemaValidator |
| if (fGrammarType == Grammar::DTDGrammarType) { |
| fGrammar = grammar; |
| fGrammarType = Grammar::SchemaGrammarType; |
| fValidator->setGrammar(fGrammar); |
| } |
| } |
| // update fModel; rely on the grammar resolver to do this |
| // efficiently |
| if(getPSVIHandler()) |
| fModel = fGrammarResolver->getXSModel(); |
| } |
| |
| InputSource* SGXMLScanner::resolveSystemId(const XMLCh* const sysId |
| ,const XMLCh* const pubId) |
| { |
| //Normalize sysId |
| XMLBufBid nnSys(&fBufMgr); |
| XMLBuffer& normalizedSysId = nnSys.getBuffer(); |
| XMLString::removeChar(sysId, 0xFFFF, normalizedSysId); |
| const XMLCh* normalizedURI = normalizedSysId.getRawBuffer(); |
| |
| // Create a buffer for expanding the system id |
| XMLBufBid bbSys(&fBufMgr); |
| XMLBuffer& expSysId = bbSys.getBuffer(); |
| |
| // Allow the entity handler to expand the system id if they choose |
| // to do so. |
| InputSource* srcToFill = 0; |
| if (fEntityHandler) |
| { |
| if (!fEntityHandler->expandSystemId(normalizedURI, expSysId)) |
| expSysId.set(normalizedURI); |
| |
| ReaderMgr::LastExtEntityInfo lastInfo; |
| fReaderMgr.getLastExtEntityInfo(lastInfo); |
| XMLResourceIdentifier resourceIdentifier(XMLResourceIdentifier::ExternalEntity, |
| expSysId.getRawBuffer(), 0, pubId, lastInfo.systemId, |
| &fReaderMgr); |
| srcToFill = fEntityHandler->resolveEntity(&resourceIdentifier); |
| } |
| else |
| { |
| expSysId.set(normalizedURI); |
| } |
| |
| // If they didn't create a source via the entity handler, then we |
| // have to create one on our own. |
| if (!srcToFill) |
| { |
| if (fDisableDefaultEntityResolution) |
| return 0; |
| |
| ReaderMgr::LastExtEntityInfo lastInfo; |
| fReaderMgr.getLastExtEntityInfo(lastInfo); |
| |
| XMLURL urlTmp(fMemoryManager); |
| if ((!urlTmp.setURL(lastInfo.systemId, expSysId.getRawBuffer(), urlTmp)) || |
| (urlTmp.isRelative())) |
| { |
| if (!fStandardUriConformant) |
| { |
| XMLBufBid ddSys(&fBufMgr); |
| XMLBuffer& resolvedSysId = ddSys.getBuffer(); |
| XMLUri::normalizeURI(expSysId.getRawBuffer(), resolvedSysId); |
| |
| srcToFill = new (fMemoryManager) LocalFileInputSource |
| ( |
| lastInfo.systemId |
| , resolvedSysId.getRawBuffer() |
| , fMemoryManager |
| ); |
| } |
| else |
| ThrowXMLwithMemMgr(MalformedURLException, XMLExcepts::URL_MalformedURL, fMemoryManager); |
| } |
| else |
| { |
| if (fStandardUriConformant && urlTmp.hasInvalidChar()) |
| ThrowXMLwithMemMgr(MalformedURLException, XMLExcepts::URL_MalformedURL, fMemoryManager); |
| srcToFill = new (fMemoryManager) URLInputSource(urlTmp, fMemoryManager); |
| } |
| } |
| |
| return srcToFill; |
| } |
| |
| |
| // --------------------------------------------------------------------------- |
| // SGXMLScanner: Private grammar preparsing methods |
| // --------------------------------------------------------------------------- |
| Grammar* SGXMLScanner::loadXMLSchemaGrammar(const InputSource& src, |
| const bool toCache) |
| { |
| // Reset the validators |
| fSchemaValidator->reset(); |
| fSchemaValidator->setErrorReporter(fErrorReporter); |
| fSchemaValidator->setExitOnFirstFatal(fExitOnFirstFatal); |
| fSchemaValidator->setGrammarResolver(fGrammarResolver); |
| |
| if (fValidatorFromUser) |
| fValidator->reset(); |
| |
| XSDDOMParser parser(0, fMemoryManager, 0); |
| |
| parser.setValidationScheme(XercesDOMParser::Val_Never); |
| parser.setDoNamespaces(true); |
| parser.setUserEntityHandler(fEntityHandler); |
| parser.setUserErrorReporter(fErrorReporter); |
| |
| // Should just issue warning if the schema is not found |
| bool flag = src.getIssueFatalErrorIfNotFound(); |
| ((InputSource&) src).setIssueFatalErrorIfNotFound(false); |
| |
| parser.parse(src); |
| |
| // Reset the InputSource |
| ((InputSource&) src).setIssueFatalErrorIfNotFound(flag); |
| |
| if (parser.getSawFatal() && fExitOnFirstFatal) |
| emitError(XMLErrs::SchemaScanFatalError); |
| |
| DOMDocument* document = parser.getDocument(); //Our Grammar |
| |
| if (document != 0) { |
| |
| DOMElement* root = document->getDocumentElement();// This is what we pass to TraverserSchema |
| if (root != 0) |
| { |
| const XMLCh* nsUri = root->getAttribute(SchemaSymbols::fgATT_TARGETNAMESPACE); |
| Grammar* grammar = fGrammarResolver->getGrammar(nsUri); |
| |
| // Check if this exact schema has already been seen. |
| // |
| const XMLCh* sysId = src.getSystemId(); |
| SchemaInfo* importSchemaInfo = 0; |
| |
| if (grammar) |
| { |
| if (nsUri && *nsUri) |
| importSchemaInfo = fCachedSchemaInfoList->get(sysId, fURIStringPool->addOrFind(nsUri)); |
| else |
| importSchemaInfo = fCachedSchemaInfoList->get(sysId, fEmptyNamespaceId); |
| } |
| |
| if (!importSchemaInfo) |
| { |
| bool grammarFound = grammar && |
| grammar->getGrammarType() == Grammar::SchemaGrammarType && |
| getHandleMultipleImports(); |
| |
| SchemaGrammar* schemaGrammar; |
| |
| if (grammarFound) |
| schemaGrammar = (SchemaGrammar*) grammar; |
| else |
| schemaGrammar = new (fGrammarPoolMemoryManager) SchemaGrammar(fGrammarPoolMemoryManager); |
| |
| XMLSchemaDescription* gramDesc = (XMLSchemaDescription*) schemaGrammar->getGrammarDescription(); |
| gramDesc->setContextType(XMLSchemaDescription::CONTEXT_PREPARSE); |
| gramDesc->setLocationHints(sysId); |
| |
| TraverseSchema traverseSchema |
| ( |
| root |
| , fURIStringPool |
| , schemaGrammar |
| , fGrammarResolver |
| , fCachedSchemaInfoList |
| , toCache ? fCachedSchemaInfoList : fSchemaInfoList |
| , this |
| , sysId |
| , fEntityHandler |
| , fErrorReporter |
| , fMemoryManager |
| , grammarFound |
| ); |
| |
| grammar = schemaGrammar; |
| |
| // Reset the now invalid schema roots in the collected |
| // schema info entries. |
| // |
| { |
| RefHash2KeysTableOfEnumerator<SchemaInfo> i ( |
| toCache ? fCachedSchemaInfoList : fSchemaInfoList); |
| |
| while (i.hasMoreElements ()) |
| i.nextElement().resetRoot (); |
| } |
| } |
| |
| if (fValidate) { |
| // validate the Schema scan so far |
| fValidator->setGrammar(grammar); |
| fValidator->preContentValidation(false); |
| } |
| |
| if (toCache) { |
| fGrammarResolver->cacheGrammars(); |
| } |
| |
| if(getPSVIHandler()) |
| fModel = fGrammarResolver->getXSModel(); |
| |
| return grammar; |
| } |
| } |
| |
| return 0; |
| } |
| |
| |
| |
| // --------------------------------------------------------------------------- |
| // SGXMLScanner: Private parsing methods |
| // --------------------------------------------------------------------------- |
| |
| // This method is called to do a raw scan of an attribute value. It does not |
| // do normalization (since we don't know their types yet.) It just scans the |
| // value and does entity expansion. |
| // |
| // End of entity's must be dealt with here. During DTD scan, they can come |
| // from external entities. During content, they can come from any entity. |
| // We just eat the end of entity and continue with our scan until we come |
| // to the closing quote. If an unterminated value causes us to go through |
| // subsequent entities, that will cause errors back in the calling code, |
| // but there's little we can do about it here. |
| bool SGXMLScanner::basicAttrValueScan(const XMLCh* const attrName, XMLBuffer& toFill) |
| { |
| // Reset the target buffer |
| toFill.reset(); |
| |
| // Get the next char which must be a single or double quote |
| XMLCh quoteCh; |
| if (!fReaderMgr.skipIfQuote(quoteCh)) |
| return false; |
| |
| // We have to get the current reader because we have to ignore closing |
| // quotes until we hit the same reader again. |
| const XMLSize_t curReader = fReaderMgr.getCurrentReaderNum(); |
| |
| // Loop until we get the attribute value. Note that we use a double |
| // loop here to avoid the setup/teardown overhead of the exception |
| // handler on every round. |
| while (true) |
| { |
| try |
| { |
| while(true) |
| { |
| XMLCh nextCh = fReaderMgr.getNextChar(); |
| |
| if (nextCh != quoteCh) |
| { |
| if (nextCh != chAmpersand) |
| { |
| if ((nextCh < 0xD800) || (nextCh > 0xDFFF)) |
| { |
| // Its got to at least be a valid XML character |
| if (!fReaderMgr.getCurrentReader()->isXMLChar(nextCh)) |
| { |
| if (nextCh == 0) |
| ThrowXMLwithMemMgr(UnexpectedEOFException, XMLExcepts::Gen_UnexpectedEOF, fMemoryManager); |
| |
| XMLCh tmpBuf[9]; |
| XMLString::binToText |
| ( |
| nextCh |
| , tmpBuf |
| , 8 |
| , 16 |
| , fMemoryManager |
| ); |
| emitError(XMLErrs::InvalidCharacterInAttrValue, attrName, tmpBuf); |
| } |
| } else // its a surrogate |
| { |
| // Deal with surrogate pairs |
| |
| // we expect a a leading surrogate. |
| if (nextCh <= 0xDBFF) |
| { |
| toFill.append(nextCh); |
| |
| // process the trailing surrogate |
| nextCh = fReaderMgr.getNextChar(); |
| |
| // it should be a trailing surrogate. |
| if ((nextCh < 0xDC00) || (nextCh > 0xDFFF)) |
| { |
| emitError(XMLErrs::Expected2ndSurrogateChar); |
| } |
| } else |
| { |
| // Its a trailing surrogate, but we are not expecting it |
| emitError(XMLErrs::Unexpected2ndSurrogateChar); |
| } |
| } |
| } else // its a chAmpersand |
| { |
| // Check for an entity ref . We ignore the empty flag in |
| // this one. |
| |
| bool escaped; |
| XMLCh firstCh; |
| XMLCh secondCh |
| ; |
| // If it was not returned directly, then jump back up |
| if (scanEntityRef(true, firstCh, secondCh, escaped) == EntityExp_Returned) |
| { |
| // If it was escaped, then put in a 0xFFFF value. This will |
| // be used later during validation and normalization of the |
| // value to know that the following character was via an |
| // escape char. |
| if (escaped) |
| toFill.append(0xFFFF); |
| |
| toFill.append(firstCh); |
| if (secondCh) |
| toFill.append(secondCh); |
| } |
| continue; |
| } |
| } else // its a quoteCh |
| { |
| // Check for our ending quote. It has to be in the same entity |
| // as where we started. Quotes in nested entities are ignored. |
| |
| if (curReader == fReaderMgr.getCurrentReaderNum()) |
| { |
| return true; |
| } |
| |
| // Watch for spillover into a previous entity |
| if (curReader > fReaderMgr.getCurrentReaderNum()) |
| { |
| emitError(XMLErrs::PartialMarkupInEntity); |
| return false; |
| } |
| } |
| |
| // add it to the buffer |
| toFill.append(nextCh); |
| |
| } |
| } |
| catch(const EndOfEntityException&) |
| { |
| // Just eat it and continue. |
| } |
| } |
| return true; |
| } |
| |
| |
| // This method scans a CDATA section. It collects the character into one |
| // of the temp buffers and calls the document handler, if any, with the |
| // characters. It assumes that the <![CDATA string has been scanned before |
| // this call. |
| void SGXMLScanner::scanCDSection() |
| { |
| static const XMLCh CDataClose[] = |
| { |
| chCloseSquare, chCloseAngle, chNull |
| }; |
| |
| // The next character should be the opening square bracket. If not |
| // issue an error, but then try to recover by skipping any whitespace |
| // and checking again. |
| if (!fReaderMgr.skippedChar(chOpenSquare)) |
| { |
| emitError(XMLErrs::ExpectedOpenSquareBracket); |
| fReaderMgr.skipPastSpaces(); |
| |
| // If we still don't find it, then give up, else keep going |
| if (!fReaderMgr.skippedChar(chOpenSquare)) |
| return; |
| } |
| |
| // Get a buffer for this |
| XMLBufBid bbCData(&fBufMgr); |
| |
| // We just scan forward until we hit the end of CDATA section sequence. |
| // CDATA is effectively a big escape mechanism so we don't treat markup |
| // characters specially here. |
| bool emittedError = false; |
| bool gotLeadingSurrogate = false; |
| |
| // Get the character data opts for the current element |
| XMLElementDecl::CharDataOpts charOpts = XMLElementDecl::AllCharData; |
| // And see if the current element is a 'Children' style content model |
| ComplexTypeInfo *currType = ((SchemaValidator*)fValidator)->getCurrentTypeInfo(); |
| if(currType) |
| { |
| SchemaElementDecl::ModelTypes modelType = (SchemaElementDecl::ModelTypes) currType->getContentType(); |
| if(modelType == SchemaElementDecl::Children || |
| modelType == SchemaElementDecl::ElementOnlyEmpty) |
| charOpts = XMLElementDecl::SpacesOk; |
| else if(modelType == SchemaElementDecl::Empty) |
| charOpts = XMLElementDecl::NoCharData; |
| } |
| |
| // should not be necessary when PSVI on element decl removed |
| const ElemStack::StackElem* topElem = fElemStack.topElement(); |
| |
| while (true) |
| { |
| const XMLCh nextCh = fReaderMgr.getNextChar(); |
| |
| // Watch for unexpected end of file |
| if (!nextCh) |
| { |
| emitError(XMLErrs::UnterminatedCDATASection); |
| ThrowXMLwithMemMgr(UnexpectedEOFException, XMLExcepts::Gen_UnexpectedEOF, fMemoryManager); |
| } |
| |
| if (fValidate && fStandalone && (fReaderMgr.getCurrentReader()->isWhitespace(nextCh))) |
| { |
| // This document is standalone; this ignorable CDATA whitespace is forbidden. |
| // XML 1.0, Section 2.9 |
| // And see if the current element is a 'Children' style content model |
| if (topElem->fThisElement->isExternal()) { |
| |
| if (charOpts == XMLElementDecl::SpacesOk) // Element Content |
| { |
| // Error - standalone should have a value of "no" as whitespace detected in an |
| // element type with element content whose element declaration was external |
| fValidator->emitError(XMLValid::NoWSForStandalone); |
| if (getPSVIHandler()) |
| { |
| // REVISIT: |
| // PSVIElement->setValidity(PSVIItem::VALIDITY_INVALID); |
| } |
| } |
| } |
| } |
| |
| // If this is a close square bracket it could be our closing |
| // sequence. |
| if (nextCh == chCloseSquare && fReaderMgr.skippedString(CDataClose)) |
| { |
| // make sure we were not expecting a trailing surrogate. |
| if (gotLeadingSurrogate) { |
| emitError(XMLErrs::Expected2ndSurrogateChar); |
| } |
| |
| XMLSize_t xsLen = bbCData.getLen(); |
| const XMLCh* xsNormalized = bbCData.getRawBuffer(); |
| if (fValidate) { |
| |
| DatatypeValidator* tempDV = ((SchemaValidator*) fValidator)->getCurrentDatatypeValidator(); |
| if (tempDV && tempDV->getWSFacet() != DatatypeValidator::PRESERVE) |
| { |
| // normalize the character according to schema whitespace facet |
| ((SchemaValidator*) fValidator)->normalizeWhiteSpace(tempDV, xsNormalized, fWSNormalizeBuf); |
| xsNormalized = fWSNormalizeBuf.getRawBuffer(); |
| xsLen = fWSNormalizeBuf.getLen(); |
| } |
| |
| // tell the schema validation about the character data for checkContent later |
| ((SchemaValidator*)fValidator)->setDatatypeBuffer(xsNormalized); |
| |
| if (charOpts != XMLElementDecl::AllCharData) |
| { |
| // They definitely cannot handle any type of char data |
| fValidator->emitError(XMLValid::NoCharDataInCM); |
| if (getPSVIHandler()) |
| { |
| // REVISIT: |
| // PSVIElement->setValidity(PSVIItem::VALIDITY_INVALID); |
| } |
| } |
| } |
| |
| // call all active identity constraints |
| if (toCheckIdentityConstraint() && fICHandler->getMatcherCount()) { |
| fContent.append(xsNormalized, xsLen); |
| } |
| |
| // If we have a doc handler, call it |
| if (fDocHandler) |
| { |
| if (fNormalizeData) { |
| fDocHandler->docCharacters(xsNormalized, xsLen, true); |
| } |
| else { |
| fDocHandler->docCharacters( |
| bbCData.getRawBuffer(), bbCData.getLen(), true |
| ); |
| } |
| } |
| |
| // And we are done |
| break; |
| } |
| |
| // Make sure its a valid character. But if we've emitted an error |
| // already, don't bother with the overhead since we've already told |
| // them about it. |
| if (!emittedError) |
| { |
| // Deal with surrogate pairs |
| if ((nextCh >= 0xD800) && (nextCh <= 0xDBFF)) |
| { |
| // Its a leading surrogate. If we already got one, then |
| // issue an error, else set leading flag to make sure that |
| // we look for a trailing next time. |
| if (gotLeadingSurrogate) |
| emitError(XMLErrs::Expected2ndSurrogateChar); |
| else |
| gotLeadingSurrogate = true; |
| } |
| else |
| { |
| // If its a trailing surrogate, make sure that we are |
| // prepared for that. Else, its just a regular char so make |
| // sure that we were not expected a trailing surrogate. |
| if ((nextCh >= 0xDC00) && (nextCh <= 0xDFFF)) |
| { |
| // Its trailing, so make sure we were expecting it |
| if (!gotLeadingSurrogate) |
| emitError(XMLErrs::Unexpected2ndSurrogateChar); |
| } |
| else |
| { |
| // Its just a char, so make sure we were not expecting a |
| // trailing surrogate. |
| if (gotLeadingSurrogate) |
| emitError(XMLErrs::Expected2ndSurrogateChar); |
| |
| // Its got to at least be a valid XML character |
| else if (!fReaderMgr.getCurrentReader()->isXMLChar(nextCh)) |
| { |
| XMLCh tmpBuf[9]; |
| XMLString::binToText |
| ( |
| nextCh |
| , tmpBuf |
| , 8 |
| , 16 |
| , fMemoryManager |
| ); |
| emitError(XMLErrs::InvalidCharacter, tmpBuf); |
| emittedError = true; |
| } |
| } |
| gotLeadingSurrogate = false; |
| } |
| } |
| |
| // Add it to the buffer |
| bbCData.append(nextCh); |
| } |
| } |
| |
| |
| void SGXMLScanner::scanCharData(XMLBuffer& toUse) |
| { |
| // We have to watch for the stupid ]]> sequence, which is illegal in |
| // character data. So this is a little state machine that handles that. |
| enum States |
| { |
| State_Waiting |
| , State_GotOne |
| , State_GotTwo |
| }; |
| |
| // Reset the buffer before we start |
| toUse.reset(); |
| |
| // Turn on the 'throw at end' flag of the reader manager |
| ThrowEOEJanitor jan(&fReaderMgr, true); |
| |
| // In order to be more efficient we have to use kind of a deeply nested |
| // set of blocks here. The outer block puts on a try and catches end of |
| // entity exceptions. The inner loop is the per-character loop. If we |
| // put the try inside the inner loop, it would work but would require |
| // the exception handling code setup/teardown code to be invoked for |
| // each character. |
| XMLCh nextCh; |
| XMLCh secondCh = 0; |
| States curState = State_Waiting; |
| bool escaped = false; |
| bool gotLeadingSurrogate = false; |
| bool notDone = true; |
| while (notDone) |
| { |
| try |
| { |
| while (true) |
| { |
| // Eat through as many plain content characters as possible without |
| // needing special handling. Moving most content characters here, |
| // in this one call, rather than running the overall loop once |
| // per content character, is a speed optimization. |
| if (curState == State_Waiting && !gotLeadingSurrogate) |
| { |
| fReaderMgr.movePlainContentChars(toUse); |
| } |
| |
| // Try to get another char from the source |
| // The code from here on down covers all contengencies, |
| if (!fReaderMgr.getNextCharIfNot(chOpenAngle, nextCh)) |
| { |
| // If we were waiting for a trailing surrogate, its an error |
| if (gotLeadingSurrogate) |
| emitError(XMLErrs::Expected2ndSurrogateChar); |
| |
| notDone = false; |
| break; |
| } |
| |
| // Watch for a reference. Note that the escapement mechanism |
| // is ignored in this content. |
| escaped = false; |
| if (nextCh == chAmpersand) |
| { |
| sendCharData(toUse); |
| |
| // Turn off the throwing at the end of entity during this |
| ThrowEOEJanitor jan(&fReaderMgr, false); |
| |
| if (scanEntityRef(false, nextCh, secondCh, escaped) != EntityExp_Returned) |
| { |
| gotLeadingSurrogate = false; |
| continue; |
| } |
| } |
| else if ((nextCh >= 0xD800) && (nextCh <= 0xDBFF)) |
| { |
| // Deal with surrogate pairs |
| // Its a leading surrogate. If we already got one, then |
| // issue an error, else set leading flag to make sure that |
| // we look for a trailing next time. |
| if (gotLeadingSurrogate) |
| emitError(XMLErrs::Expected2ndSurrogateChar); |
| else |
| gotLeadingSurrogate = true; |
| } |
| else |
| { |
| // If its a trailing surrogate, make sure that we are |
| // prepared for that. Else, its just a regular char so make |
| // sure that we were not expected a trailing surrogate. |
| if ((nextCh >= 0xDC00) && (nextCh <= 0xDFFF)) |
| { |
| // Its trailing, so make sure we were expecting it |
| if (!gotLeadingSurrogate) |
| emitError(XMLErrs::Unexpected2ndSurrogateChar); |
| } |
| else |
| { |
| // Its just a char, so make sure we were not expecting a |
| // trailing surrogate. |
| if (gotLeadingSurrogate) |
| emitError(XMLErrs::Expected2ndSurrogateChar); |
| |
| // Make sure the returned char is a valid XML char |
| if (!fReaderMgr.getCurrentReader()->isXMLChar(nextCh)) |
| { |
| XMLCh tmpBuf[9]; |
| XMLString::binToText |
| ( |
| nextCh |
| , tmpBuf |
| , 8 |
| , 16 |
| , fMemoryManager |
| ); |
| emitError(XMLErrs::InvalidCharacter, tmpBuf); |
| } |
| } |
| gotLeadingSurrogate = false; |
| } |
| |
| // Keep the state machine up to date |
| if (!escaped) |
| { |
| if (nextCh == chCloseSquare) |
| { |
| if (curState == State_Waiting) |
| curState = State_GotOne; |
| else if (curState == State_GotOne) |
| curState = State_GotTwo; |
| } |
| else if (nextCh == chCloseAngle) |
| { |
| if (curState == State_GotTwo) |
| emitError(XMLErrs::BadSequenceInCharData); |
| curState = State_Waiting; |
| } |
| else |
| { |
| curState = State_Waiting; |
| } |
| } |
| else |
| { |
| curState = State_Waiting; |
| } |
| |
| // Add this char to the buffer |
| toUse.append(nextCh); |
| |
| if (secondCh) |
| { |
| toUse.append(secondCh); |
| secondCh=0; |
| } |
| } |
| } |
| catch(const EndOfEntityException& toCatch) |
| { |
| // Some entity ended, so we have to send any accumulated |
| // chars and send an end of entity event. |
| sendCharData(toUse); |
| gotLeadingSurrogate = false; |
| |
| if (fDocHandler) |
| fDocHandler->endEntityReference(toCatch.getEntity()); |
| } |
| } |
| |
| // Check the validity constraints as per XML 1.0 Section 2.9 |
| if (fValidate && fStandalone) |
| { |
| // See if the text contains whitespace |
| // Get the raw data we need for the callback |
| const XMLCh* rawBuf = toUse.getRawBuffer(); |
| const XMLSize_t len = toUse.getLen(); |
| const bool isSpaces = fReaderMgr.getCurrentReader()->containsWhiteSpace(rawBuf, len); |
| |
| if (isSpaces) |
| { |
| // And see if the current element is a 'Children' style content model |
| const ElemStack::StackElem* topElem = fElemStack.topElement(); |
| |
| if (topElem->fThisElement->isExternal()) { |
| |
| // Get the character data opts for the current element |
| XMLElementDecl::CharDataOpts charOpts = XMLElementDecl::AllCharData; |
| // And see if the current element is a 'Children' style content model |
| ComplexTypeInfo *currType = ((SchemaValidator*)fValidator)->getCurrentTypeInfo(); |
| if(currType) |
| { |
| SchemaElementDecl::ModelTypes modelType = (SchemaElementDecl::ModelTypes) currType->getContentType(); |
| if(modelType == SchemaElementDecl::Children || |
| modelType == SchemaElementDecl::ElementOnlyEmpty) |
| charOpts = XMLElementDecl::SpacesOk; |
| } |
| |
| if (charOpts == XMLElementDecl::SpacesOk) // => Element Content |
| { |
| // Error - standalone should have a value of "no" as whitespace detected in an |
| // element type with element content whose element declaration was external |
| // |
| fValidator->emitError(XMLValid::NoWSForStandalone); |
| if (getPSVIHandler()) |
| { |
| // REVISIT: |
| // PSVIElement->setValidity(PSVIItem::VALIDITY_INVALID); |
| } |
| } |
| } |
| } |
| } |
| // Send any char data that we accumulated into the buffer |
| sendCharData(toUse); |
| } |
| |
| |
| // This method will scan a general/character entity ref. It will either |
| // expand a char ref and return it directly, or push a reader for a general |
| // entity. |
| // |
| // The return value indicates whether the char parameters hold the value |
| // or whether the value was pushed as a reader, or that it failed. |
| // |
| // The escaped flag tells the caller whether the returned parameter resulted |
| // from a character reference, which escapes the character in some cases. It |
| // only makes any difference if the return value indicates the value was |
| // returned directly. |
| SGXMLScanner::EntityExpRes |
| SGXMLScanner::scanEntityRef( const bool |
| , XMLCh& firstCh |
| , XMLCh& secondCh |
| , bool& escaped) |
| { |
| // Assume no escape |
| secondCh = 0; |
| escaped = false; |
| |
| // We have to insure that its all in one entity |
| const XMLSize_t curReader = fReaderMgr.getCurrentReaderNum(); |
| |
| // If the next char is a pound, then its a character reference and we |
| // need to expand it always. |
| if (fReaderMgr.skippedChar(chPound)) |
| { |
| // Its a character reference, so scan it and get back the numeric |
| // value it represents. |
| if (!scanCharRef(firstCh, secondCh)) |
| return EntityExp_Failed; |
| |
| escaped = true; |
| |
| if (curReader != fReaderMgr.getCurrentReaderNum()) |
| emitError(XMLErrs::PartialMarkupInEntity); |
| |
| return EntityExp_Returned; |
| } |
| |
| // Expand it since its a normal entity ref |
| XMLBufBid bbName(&fBufMgr); |
| int colonPosition; |
| if (!fReaderMgr.getQName(bbName.getBuffer(), &colonPosition)) |
| { |
| if (bbName.isEmpty()) |
| emitError(XMLErrs::ExpectedEntityRefName); |
| else |
| emitError(XMLErrs::InvalidEntityRefName, bbName.getRawBuffer()); |
| return EntityExp_Failed; |
| } |
| |
| // Next char must be a semi-colon. But if its not, just emit |
| // an error and try to continue. |
| if (!fReaderMgr.skippedChar(chSemiColon)) |
| emitError(XMLErrs::UnterminatedEntityRef, bbName.getRawBuffer()); |
| |
| // Make sure we ended up on the same entity reader as the & char |
| if (curReader != fReaderMgr.getCurrentReaderNum()) |
| emitError(XMLErrs::PartialMarkupInEntity); |
| |
| // Look up the name in the general entity pool |
| // If it does not exist, then obviously an error |
| if (!fEntityTable->containsKey(bbName.getRawBuffer())) |
| { |
| // XML 1.0 Section 4.1 |
| // Well-formedness Constraint for entity not found: |
| // In a document without any DTD, a document with only an internal DTD subset which contains no parameter entity references, |
| // or a document with "standalone='yes'", for an entity reference that does not occur within the external subset |
| // or a parameter entity |
| if (fStandalone || fHasNoDTD) |
| emitError(XMLErrs::EntityNotFound, bbName.getRawBuffer()); |
| |
| return EntityExp_Failed; |
| } |
| |
| // here's where we need to check if there's a SecurityManager, |
| // how many entity references we've had |
| if(fSecurityManager != 0 && ++fEntityExpansionCount > fEntityExpansionLimit) { |
| XMLCh expLimStr[32]; |
| XMLString::sizeToText(fEntityExpansionLimit, expLimStr, 31, 10, fMemoryManager); |
| emitError |
| ( |
| XMLErrs::EntityExpansionLimitExceeded |
| , expLimStr |
| ); |
| // there seems nothing better to be done than to reset the entity expansion limit |
| fEntityExpansionCount = 0; |
| } |
| |
| firstCh = fEntityTable->get(bbName.getRawBuffer()); |
| escaped = true; |
| return EntityExp_Returned; |
| } |
| |
| |
| bool SGXMLScanner::switchGrammar(const XMLCh* const newGrammarNameSpace) |
| { |
| Grammar* tempGrammar = fGrammarResolver->getGrammar(newGrammarNameSpace); |
| |
| if (!tempGrammar) { |
| tempGrammar = fSchemaGrammar; |
| } |
| |
| if (!tempGrammar) |
| return false; |
| else { |
| fGrammar = tempGrammar; |
| fGrammarType = fGrammar->getGrammarType(); |
| if (fGrammarType == Grammar::DTDGrammarType) { |
| ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::Gen_NoDTDValidator, fMemoryManager); |
| } |
| |
| fValidator->setGrammar(fGrammar); |
| return true; |
| } |
| } |
| |
| // check if we should skip or lax the validation of the element |
| // if skip - no validation |
| // if lax - validate only if the element if found |
| bool SGXMLScanner::laxElementValidation(QName* element, ContentLeafNameTypeVector* cv, |
| const XMLContentModel* const cm, |
| const XMLSize_t parentElemDepth) |
| { |
| bool skipThisOne = false; |
| bool laxThisOne = false; |
| unsigned int elementURI = element->getURI(); |
| unsigned int currState = fElemState[parentElemDepth]; |
| unsigned int currLoop = fElemLoopState[parentElemDepth]; |
| |
| if (currState == XMLContentModel::gInvalidTrans) { |
| return laxThisOne; |
| } |
| |
| SubstitutionGroupComparator comparator(fGrammarResolver, fURIStringPool); |
| |
| if (cv) { |
| XMLSize_t i = 0; |
| XMLSize_t leafCount = cv->getLeafCount(); |
| unsigned int nextState = 0; |
| |
| for (; i < leafCount; i++) { |
| |
| QName* fElemMap = cv->getLeafNameAt(i); |
| unsigned int uri = fElemMap->getURI(); |
| ContentSpecNode::NodeTypes type = cv->getLeafTypeAt(i); |
| |
| if (type == ContentSpecNode::Leaf) { |
| if (((uri == elementURI) |
| && XMLString::equals(fElemMap->getLocalPart(), element->getLocalPart())) |
| || comparator.isEquivalentTo(element, fElemMap)) { |
| |
| nextState = cm->getNextState(currState, i); |
| |
| if (nextState != XMLContentModel::gInvalidTrans) |
| break; |
| } |
| } else if ((type & 0x0f) == ContentSpecNode::Any) { |
| nextState = cm->getNextState(currState, i); |
| if (nextState != XMLContentModel::gInvalidTrans) |
| break; |
| } |
| else if ((type & 0x0f) == ContentSpecNode::Any_Other) { |
| if (uri != elementURI && elementURI != fEmptyNamespaceId) { |
| nextState = cm->getNextState(currState, i); |
| if (nextState != XMLContentModel::gInvalidTrans) |
| break; |
| } |
| } |
| else if ((type & 0x0f) == ContentSpecNode::Any_NS) { |
| if (uri == elementURI) { |
| nextState = cm->getNextState(currState, i); |
| if (nextState != XMLContentModel::gInvalidTrans) |
| break; |
| } |
| } |
| |
| } // for |
| |
| if (i == leafCount) { // no match |
| fElemState[parentElemDepth] = XMLContentModel::gInvalidTrans; |
| fElemLoopState[parentElemDepth] = 0; |
| return laxThisOne; |
| } |
| |
| ContentSpecNode::NodeTypes type = cv->getLeafTypeAt(i); |
| if ((type & 0x0f) == ContentSpecNode::Any || |
| (type & 0x0f) == ContentSpecNode::Any_Other || |
| (type & 0x0f) == ContentSpecNode::Any_NS) |
| { |
| if (type == ContentSpecNode::Any_Skip || |
| type == ContentSpecNode::Any_NS_Skip || |
| type == ContentSpecNode::Any_Other_Skip) { |
| skipThisOne = true; |
| } |
| else if (type == ContentSpecNode::Any_Lax || |
| type == ContentSpecNode::Any_NS_Lax || |
| type == ContentSpecNode::Any_Other_Lax) { |
| laxThisOne = true; |
| } |
| } |
| fElemState[parentElemDepth] = nextState; |
| fElemLoopState[parentElemDepth] = currLoop; |
| } // if |
| |
| if (skipThisOne) { |
| fValidate = false; |
| fElemStack.setValidationFlag(fValidate); |
| } |
| |
| return laxThisOne; |
| } |
| |
| |
| // check if there is an AnyAttribute, and if so, see if we should lax or skip |
| // if skip - no validation |
| // if lax - validate only if the attribute if found |
| bool SGXMLScanner::anyAttributeValidation(SchemaAttDef* attWildCard, unsigned int uriId, bool& skipThisOne, bool& laxThisOne) |
| { |
| XMLAttDef::AttTypes wildCardType = attWildCard->getType(); |
| bool anyEncountered = false; |
| skipThisOne = false; |
| laxThisOne = false; |
| if (wildCardType == XMLAttDef::Any_Any) |
| anyEncountered = true; |
| else if (wildCardType == XMLAttDef::Any_Other) { |
| if (attWildCard->getAttName()->getURI() != uriId |
| && uriId != fEmptyNamespaceId) |
| anyEncountered = true; |
| } |
| else if (wildCardType == XMLAttDef::Any_List) { |
| ValueVectorOf<unsigned int>* nameURIList = attWildCard->getNamespaceList(); |
| XMLSize_t listSize = (nameURIList) ? nameURIList->size() : 0; |
| |
| if (listSize) { |
| for (XMLSize_t i=0; i < listSize; i++) { |
| if (nameURIList->elementAt(i) == uriId) |
| anyEncountered = true; |
| } |
| } |
| } |
| |
| if (anyEncountered) { |
| XMLAttDef::DefAttTypes defType = attWildCard->getDefaultType(); |
| if (defType == XMLAttDef::ProcessContents_Skip) { |
| // attribute should just be bypassed, |
| skipThisOne = true; |
| if (getPSVIHandler()) |
| { |
| // REVISIT: |
| // PSVIAttribute->setValidationAttempted(PSVIItem::VALIDATION_NONE); |
| } |
| } |
| else if (defType == XMLAttDef::ProcessContents_Lax) { |
| laxThisOne = true; |
| } |
| } |
| |
| return anyEncountered; |
| } |
| |
| inline XMLAttDefList& getAttDefList(ComplexTypeInfo* currType, XMLElementDecl* elemDecl) |
| { |
| if (currType) |
| return currType->getAttDefList(); |
| else |
| return elemDecl->getAttDefList(); |
| } |
| |
| void SGXMLScanner::endElementPSVI(SchemaElementDecl* const elemDecl, |
| DatatypeValidator* const memberDV) |
| { |
| PSVIElement::ASSESSMENT_TYPE validationAttempted; |
| PSVIElement::VALIDITY_STATE validity = PSVIElement::VALIDITY_NOTKNOWN; |
| |
| if (fPSVIElemContext.fElemDepth > fPSVIElemContext.fFullValidationDepth) |
| validationAttempted = PSVIElement::VALIDATION_FULL; |
| else if (fPSVIElemContext.fElemDepth > fPSVIElemContext.fNoneValidationDepth) |
| validationAttempted = PSVIElement::VALIDATION_NONE; |
| else |
| { |
| validationAttempted = PSVIElement::VALIDATION_PARTIAL; |
| fPSVIElemContext.fFullValidationDepth = |
| fPSVIElemContext.fNoneValidationDepth = fPSVIElemContext.fElemDepth - 1; |
| } |
| |
| if (fValidate && elemDecl->isDeclared()) |
| { |
| validity = (fPSVIElemContext.fErrorOccurred) |
| ? PSVIElement::VALIDITY_INVALID : PSVIElement::VALIDITY_VALID; |
| } |
| |
| XSTypeDefinition* typeDef = 0; |
| bool isMixed = false; |
| if (fPSVIElemContext.fCurrentTypeInfo) |
| { |
| typeDef = (XSTypeDefinition*) fModel->getXSObject(fPSVIElemContext.fCurrentTypeInfo); |
| SchemaElementDecl::ModelTypes modelType = (SchemaElementDecl::ModelTypes)fPSVIElemContext.fCurrentTypeInfo->getContentType(); |
| isMixed = (modelType == SchemaElementDecl::Mixed_Simple |
| || modelType == SchemaElementDecl::Mixed_Complex); |
| } |
| else if (fPSVIElemContext.fCurrentDV) |
| typeDef = (XSTypeDefinition*) fModel->getXSObject(fPSVIElemContext.fCurrentDV); |
| |
| XMLCh* canonicalValue = 0; |
| if (fPSVIElemContext.fNormalizedValue && !isMixed && |
| validity == PSVIElement::VALIDITY_VALID) |
| { |
| if (memberDV) |
| canonicalValue = (XMLCh*) memberDV->getCanonicalRepresentation(fPSVIElemContext.fNormalizedValue, fMemoryManager); |
| else if (fPSVIElemContext.fCurrentDV) |
| canonicalValue = (XMLCh*) fPSVIElemContext.fCurrentDV->getCanonicalRepresentation(fPSVIElemContext.fNormalizedValue, fMemoryManager); |
| } |
| |
| fPSVIElement->reset |
| ( |
| validity |
| , validationAttempted |
| , fRootElemName |
| , fPSVIElemContext.fIsSpecified |
| , (elemDecl->isDeclared()) |
| ? (XSElementDeclaration*) fModel->getXSObject(elemDecl) : 0 |
| , typeDef |
| , (memberDV) ? (XSSimpleTypeDefinition*) fModel->getXSObject(memberDV) : 0 |
| , fModel |
| , elemDecl->getDefaultValue() |
| , fPSVIElemContext.fNormalizedValue |
| , canonicalValue |
| ); |
| |
| fPSVIHandler->handleElementPSVI |
| ( |
| elemDecl->getBaseName() |
| , fURIStringPool->getValueForId(elemDecl->getURI()) |
| , fPSVIElement |
| ); |
| |
| // decrease element depth |
| fPSVIElemContext.fElemDepth--; |
| |
| } |
| |
| void SGXMLScanner::resetPSVIElemContext() |
| { |
| fPSVIElemContext.fIsSpecified = false; |
| fPSVIElemContext.fErrorOccurred = false; |
| fPSVIElemContext.fElemDepth = -1; |
| fPSVIElemContext.fFullValidationDepth = -1; |
| fPSVIElemContext.fNoneValidationDepth = -1; |
| fPSVIElemContext.fCurrentDV = 0; |
| fPSVIElemContext.fCurrentTypeInfo = 0; |
| fPSVIElemContext.fNormalizedValue = 0; |
| } |
| |
| XERCES_CPP_NAMESPACE_END |