diff --git a/src/validationthread.cpp b/src/validationthread.cpp index 3c5cf4f..329ad98 100644 --- a/src/validationthread.cpp +++ b/src/validationthread.cpp @@ -1,4 +1,5 @@ -#include "wx/wx.h" +#include +#include #include "xmlctrl.h" #include "validationthread.h" #include "wrapxerces.h" @@ -42,7 +43,11 @@ void *ValidationThread::Entry() myBuffer.c_str(), myBuffer.size(), mySystem, - this ); + this, + // Don't be too harsh to new created documents, which may haven't + // associated any schema yet + /*forceCheckGrammar*/false, + wxTextBuffer::GetEOL() ); myBuffer.clear(); diff --git a/src/wrapxerces.cpp b/src/wrapxerces.cpp index f938f3c..45280f1 100644 --- a/src/wrapxerces.cpp +++ b/src/wrapxerces.cpp @@ -19,11 +19,13 @@ #include "wrapxerces.h" +#include #include #include #include #include #include +#include #include #include #include @@ -51,7 +53,6 @@ WrapXerces::WrapXerces() { WrapXerces::Init(); - errorPosition = std::make_pair ( 1, 1 ); catalogResolver = new XercesCatalogResolver(); } @@ -62,135 +63,99 @@ WrapXerces::~WrapXerces() bool WrapXerces::validate ( const wxString& fileName ) { - SAX2XMLReader *parser = XMLReaderFactory::createXMLReader(); - - parser->setFeature ( XMLUni::fgSAX2CoreNameSpaces, true ); - parser->setFeature ( XMLUni::fgSAX2CoreValidation, true ); - parser->setFeature ( XMLUni::fgXercesDynamic, false ); - parser->setFeature ( XMLUni::fgXercesSchema, true ); - parser->setFeature ( XMLUni::fgXercesSchemaFullChecking, true ); - parser->setFeature ( XMLUni::fgXercesValidationErrorAsFatal, true ); - parser->setFeature ( XMLUni::fgXercesLoadExternalDTD, true ); - - - DefaultHandler handler; - MySAX2Handler mySAX2Handler; - parser->setContentHandler ( &handler ); - parser->setErrorHandler ( &mySAX2Handler ); - - //DefaultHandler handler; - //parser->setEntityResolver ( &handler ); - parser->setEntityResolver ( catalogResolver ); - - try - { - parser->parse ( (const XMLCh *) toString ( fileName ).GetData() ); - } - catch ( XMLException& e ) - { - delete parser; - lastError = toString ( e.getMessage() ); - return false; - } - catch ( SAXParseException& e ) - { - delete parser; - lastError << _T ( "Validation stopped at line " ) - << e.getLineNumber() << _T ( ", column " ) << e.getColumnNumber() - << _T ( ": " ) << toString ( e.getMessage() ); - errorPosition = std::make_pair ( e.getLineNumber(), e.getColumnNumber() ); - return false; - } - catch ( ... ) - { - delete parser; - lastError = _T ( "Unexpected validation error" ); - return false; - } - delete parser; - return true; + return validateMemory ( NULL, 0, fileName ); } // tbd: cache grammar bool WrapXerces::validateMemory ( - const char *buffer, + const char *utf8Buffer, size_t len, - const wxString &system, - wxThread *thread /*= NULL*/ ) + const wxString &fileName, + wxThread *thread /*= NULL*/, + bool forceGrammarCheck /*= true*/, + const wxChar *messageEOL /*= _T("[br]")*/) { +#if 0 // Test DOM parser + std::auto_ptr parser ( new XercesDOMParser() ); + + parser->setDoNamespaces(true); + parser->setExitOnFirstFatalError(true); + parser->setValidationConstraintFatal(true); + //parser->setCreateEntityReferenceNodes(true); // Default is true + parser->setValidationScheme(XercesDOMParser::Val_Auto); + parser->setDoSchema(true); + parser->setValidationSchemaFullChecking(true); + parser->setCreateCommentNodes(false); +#else std::auto_ptr parser ( XMLReaderFactory::createXMLReader() ); parser->setFeature ( XMLUni::fgSAX2CoreNameSpaces, true ); parser->setFeature ( XMLUni::fgSAX2CoreValidation, true ); - parser->setFeature ( XMLUni::fgXercesDynamic, true ); + parser->setFeature ( XMLUni::fgXercesDynamic, !forceGrammarCheck ); parser->setFeature ( XMLUni::fgXercesSchema, true ); - //parser->setFeature ( XMLUni::fgXercesSchemaFullChecking, true ); + parser->setFeature ( XMLUni::fgXercesSchemaFullChecking, true); parser->setFeature ( XMLUni::fgXercesValidationErrorAsFatal, true ); parser->setFeature ( XMLUni::fgXercesLoadExternalDTD, true ); - DefaultHandler handler; - MySAX2Handler mySAX2Handler; - parser->setContentHandler ( &handler ); + parser->setContentHandler ( &mySAX2Handler ); +#endif + parser->setErrorHandler ( &mySAX2Handler ); //parser->setEntityResolver ( &handler ); parser->setEntityResolver ( catalogResolver ); - XMLByte* xmlBuffer = (XMLByte*) buffer; - MemBufInputSource source - ( xmlBuffer - , len - , ( const XMLCh * ) ( const char * ) system.mb_str ( getMBConv() ) - ); + mySAX2Handler.setEOL ( messageEOL ); + + std::auto_ptr source; + if ( utf8Buffer != NULL ) + { + source.reset ( new MemBufInputSource ( (XMLByte*) utf8Buffer, len, + (const XMLCh *) toString ( fileName ).GetData() ) ); + wxString utf8 = _T("UTF-8"); + source->setEncoding ( (const XMLCh *) toString ( utf8 ).GetData() ); + } + else + { + source.reset ( new LocalFileInputSource ( + (const XMLCh *) toString ( fileName ).GetData() ) ); + } try { if ( thread == NULL ) { - parser->parse ( source ); + parser->parse ( *source ); } else if ( !thread->TestDestroy() ) { XMLPScanToken token; - if ( parser->parseFirst ( source, token ) ) + if ( parser->parseFirst ( *source, token ) ) while ( (!thread->TestDestroy()) && parser->parseNext ( token ) ) continue; } } catch ( XMLException& e ) { - lastError = wxEmptyString; + wxString error = toString ( e.getMessage() ); + int i = error.Find( _T("Message:") ); + if ( i != wxNOT_FOUND ) + error = error.substr( i ); + mySAX2Handler.getErrors() << error; return false; } catch ( SAXParseException& e ) { - lastError << _T ( "Ln " ) << e.getLineNumber() << _T ( " Col " ) - << e.getColumnNumber() << _T ( ": " ) << toString ( e.getMessage() ); - errorPosition = std::make_pair ( e.getLineNumber(), e.getColumnNumber() ); + // It has already been processed in mySAX2Handler return false; } catch ( ... ) { if ( thread != NULL && thread->TestDestroy() ) throw; - lastError = wxEmptyString; + mySAX2Handler.getErrors() << _("Unexpected validation error"); return false; } - return true; -} -const wxString &WrapXerces::getLastError() -{ - int i = lastError.Find( _T ( "Message:" ) ); - if ( i != wxNOT_FOUND ) - { - lastError = lastError.substr( i ); - } - - return lastError; -} - -std::pair WrapXerces::getErrorPosition() -{ - return errorPosition; + return mySAX2Handler.getErrors().empty(); } const wxMBConv &WrapXerces::getMBConv() @@ -240,3 +205,20 @@ wxMemoryBuffer WrapXerces::toString ( const wxString &str ) return buffer; } + +void MySAX2Handler::logError ( const wxString &type, wxLogLevelValues level, + const SAXParseException& e ) +{ + mErrors << wxString::Format ( + _("%s at line %llu, column %llu: %s%s"), + type.c_str(), e.getLineNumber(), e.getColumnNumber(), + WrapXerces::toString ( e.getMessage() ).c_str(), mEOL.c_str() ); + + // Only save the first warning position + if ( level > mLevel || ( level == mLevel && mErrorPosition.first == 1 + && mErrorPosition.second == 1 ) ) + { + mErrorPosition.first = e.getLineNumber(); + mErrorPosition.second = e.getColumnNumber(); + } +} diff --git a/src/wrapxerces.h b/src/wrapxerces.h index db55c15..b9002d0 100644 --- a/src/wrapxerces.h +++ b/src/wrapxerces.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,58 @@ using namespace xercesc; +class MySAX2Handler : public DefaultHandler +{ + public: + MySAX2Handler() + { + mEOL = "\n"; + resetErrors(); + } + void error ( const SAXParseException& e ) + { + logError ( _("Error"), wxLOG_Error, e ); + throw e; + } + void warning ( const SAXParseException& e ) + { + logError ( _("Warning"), wxLOG_Warning, e ); + } + void fatalError ( const SAXParseException& e ) + { + logError ( _("FatalError"), wxLOG_FatalError, e ); + throw e; + } + void resetErrors() + { + mErrors.clear(); + mErrorPosition = std::make_pair ( 1, 1 ); + } + const wxString &getErrors() const + { + return mErrors; + } + wxString &getErrors() + { + return mErrors; + } + const std::pair &getErrorPosition() const + { + return mErrorPosition; + } + void logError ( const wxString &type, wxLogLevelValues level, + const SAXParseException& e ); + void setEOL ( const wxChar *eol ) + { + mEOL = eol; + } + protected: + wxString mErrors; + std::pair mErrorPosition; + wxLogLevelValues mLevel; + wxString mEOL; +}; + class WrapXerces : private boost::noncopyable { public: @@ -44,10 +97,19 @@ class WrapXerces : private boost::noncopyable WrapXerces(); virtual ~WrapXerces(); bool validate ( const wxString &fileName ); - bool validateMemory ( const char *buffer, size_t len, - const wxString &system, wxThread *thread = NULL ); - const wxString &getLastError(); - std::pair getErrorPosition(); + bool validateMemory ( const char *utf8Buffer, size_t len, + const wxString &fileName, wxThread *thread = NULL, + bool forceGrammarCheck = true, /* Must specify a grammar */ + const wxChar *messageEOL = _T("[br]") ); + const wxString &getLastError() + { + return mySAX2Handler.getErrors(); + } + const std::pair &getErrorPosition() + { + return mySAX2Handler.getErrorPosition(); + } + static wxString toString ( const XMLCh *str ); // Convert Unicode string to const XMLCh * //#if wxCHECK_VERSION(2,9,0) @@ -60,21 +122,7 @@ class WrapXerces : private boost::noncopyable static const wxMBConv &getMBConv(); XercesCatalogResolver *catalogResolver; - wxString lastError; - std::pair errorPosition; -}; - -class MySAX2Handler : public DefaultHandler -{ - public: - void error ( const SAXParseException& e ) - { - throw e; - } - void warning ( const SAXParseException& e ) - { - throw e; - } + MySAX2Handler mySAX2Handler; }; #endif diff --git a/src/xmlcopyeditor.cpp b/src/xmlcopyeditor.cpp index f0e1975..4fd83b4 100644 --- a/src/xmlcopyeditor.cpp +++ b/src/xmlcopyeditor.cpp @@ -3922,32 +3922,14 @@ void MyFrame::OnValidateSchema ( wxCommandEvent& event ) } #endif - wxString fileName; - fileName = doc->getFullFileName(); - - WrapTempFileName wtfn ( fileName ); - if ( fileName.empty() || doc->GetModify() ) - { - wxString wideBuffer = doc->GetText(); - - std::string buffer = ( const char * ) wideBuffer.mb_str ( wxConvUTF8 ); - if ( !saveRawUtf8 ( - wtfn.name(), - buffer ) ) - { - messagePane ( - _ ( "Cannot save temporary copy for validation; please save or discard changes" ), - CONST_STOP ); - return; - } - fileName = wtfn.wideName(); - } - statusProgress ( _ ( "Validation in progress..." ) ); doc->clearErrorIndicators(); + wxString fileName = doc->getFullFileName(); + wxString utf8Buffer = doc->myGetTextRaw(); std::auto_ptr validator ( new WrapXerces() ); - if ( !validator->validate ( fileName ) ) + if ( !validator->validateMemory ( utf8Buffer.c_str(), utf8Buffer.size(), + fileName ) ) { statusProgress ( wxEmptyString ); messagePane ( validator->getLastError(), CONST_WARNING );