Bug #161 Validation of TEI Lite DTD fails

Don't treat validation warning as error
Prefer memory to temporary file
This commit is contained in:
Zane U. Ji 2013-12-16 22:05:24 +08:00
parent 80229a3686
commit 4f97286f5e
4 changed files with 145 additions and 128 deletions

View File

@ -1,4 +1,5 @@
#include "wx/wx.h" #include <wx/wx.h>
#include <wx/textbuf.h>
#include "xmlctrl.h" #include "xmlctrl.h"
#include "validationthread.h" #include "validationthread.h"
#include "wrapxerces.h" #include "wrapxerces.h"
@ -42,7 +43,11 @@ void *ValidationThread::Entry()
myBuffer.c_str(), myBuffer.c_str(),
myBuffer.size(), myBuffer.size(),
mySystem, 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(); myBuffer.clear();

View File

@ -19,11 +19,13 @@
#include "wrapxerces.h" #include "wrapxerces.h"
#include <xercesc/parsers/XercesDOMParser.hpp>
#include <xercesc/sax2/XMLReaderFactory.hpp> #include <xercesc/sax2/XMLReaderFactory.hpp>
#include <xercesc/sax2/SAX2XMLReader.hpp> #include <xercesc/sax2/SAX2XMLReader.hpp>
#include <xercesc/sax2/DefaultHandler.hpp> #include <xercesc/sax2/DefaultHandler.hpp>
#include <xercesc/util/XMLUni.hpp> #include <xercesc/util/XMLUni.hpp>
#include <xercesc/framework/MemBufInputSource.hpp> #include <xercesc/framework/MemBufInputSource.hpp>
#include <xercesc/framework/LocalFileInputSource.hpp>
#include <sstream> #include <sstream>
#include <utility> #include <utility>
#include <stdexcept> #include <stdexcept>
@ -51,7 +53,6 @@ WrapXerces::WrapXerces()
{ {
WrapXerces::Init(); WrapXerces::Init();
errorPosition = std::make_pair ( 1, 1 );
catalogResolver = new XercesCatalogResolver(); catalogResolver = new XercesCatalogResolver();
} }
@ -62,135 +63,99 @@ WrapXerces::~WrapXerces()
bool WrapXerces::validate ( const wxString& fileName ) bool WrapXerces::validate ( const wxString& fileName )
{ {
SAX2XMLReader *parser = XMLReaderFactory::createXMLReader(); return validateMemory ( NULL, 0, fileName );
}
// tbd: cache grammar
bool WrapXerces::validateMemory (
const char *utf8Buffer,
size_t len,
const wxString &fileName,
wxThread *thread /*= NULL*/,
bool forceGrammarCheck /*= true*/,
const wxChar *messageEOL /*= _T("[br]")*/)
{
#if 0 // Test DOM parser
std::auto_ptr<XercesDOMParser> 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<SAX2XMLReader> parser ( XMLReaderFactory::createXMLReader() );
parser->setFeature ( XMLUni::fgSAX2CoreNameSpaces, true ); parser->setFeature ( XMLUni::fgSAX2CoreNameSpaces, true );
parser->setFeature ( XMLUni::fgSAX2CoreValidation, true ); parser->setFeature ( XMLUni::fgSAX2CoreValidation, true );
parser->setFeature ( XMLUni::fgXercesDynamic, false ); parser->setFeature ( XMLUni::fgXercesDynamic, !forceGrammarCheck );
parser->setFeature ( XMLUni::fgXercesSchema, true ); parser->setFeature ( XMLUni::fgXercesSchema, true );
parser->setFeature ( XMLUni::fgXercesSchemaFullChecking, true); parser->setFeature ( XMLUni::fgXercesSchemaFullChecking, true);
parser->setFeature ( XMLUni::fgXercesValidationErrorAsFatal, true ); parser->setFeature ( XMLUni::fgXercesValidationErrorAsFatal, true );
parser->setFeature ( XMLUni::fgXercesLoadExternalDTD, true ); parser->setFeature ( XMLUni::fgXercesLoadExternalDTD, true );
parser->setContentHandler ( &mySAX2Handler );
#endif
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;
}
// tbd: cache grammar
bool WrapXerces::validateMemory (
const char *buffer,
size_t len,
const wxString &system,
wxThread *thread /*= NULL*/ )
{
std::auto_ptr<SAX2XMLReader> parser ( XMLReaderFactory::createXMLReader() );
parser->setFeature ( XMLUni::fgSAX2CoreNameSpaces, true );
parser->setFeature ( XMLUni::fgSAX2CoreValidation, true );
parser->setFeature ( XMLUni::fgXercesDynamic, true );
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 ); parser->setErrorHandler ( &mySAX2Handler );
//parser->setEntityResolver ( &handler ); //parser->setEntityResolver ( &handler );
parser->setEntityResolver ( catalogResolver ); parser->setEntityResolver ( catalogResolver );
XMLByte* xmlBuffer = (XMLByte*) buffer; mySAX2Handler.setEOL ( messageEOL );
MemBufInputSource source
( xmlBuffer std::auto_ptr<InputSource> source;
, len if ( utf8Buffer != NULL )
, ( const XMLCh * ) ( const char * ) system.mb_str ( getMBConv() ) {
); 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 try
{ {
if ( thread == NULL ) if ( thread == NULL )
{ {
parser->parse ( source ); parser->parse ( *source );
} }
else if ( !thread->TestDestroy() ) else if ( !thread->TestDestroy() )
{ {
XMLPScanToken token; XMLPScanToken token;
if ( parser->parseFirst ( source, token ) ) if ( parser->parseFirst ( *source, token ) )
while ( (!thread->TestDestroy()) && parser->parseNext ( token ) ) while ( (!thread->TestDestroy()) && parser->parseNext ( token ) )
continue; continue;
} }
} }
catch ( XMLException& e ) 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; return false;
} }
catch ( SAXParseException& e ) catch ( SAXParseException& e )
{ {
lastError << _T ( "Ln " ) << e.getLineNumber() << _T ( " Col " ) // It has already been processed in mySAX2Handler
<< e.getColumnNumber() << _T ( ": " ) << toString ( e.getMessage() );
errorPosition = std::make_pair ( e.getLineNumber(), e.getColumnNumber() );
return false; return false;
} }
catch ( ... ) catch ( ... )
{ {
if ( thread != NULL && thread->TestDestroy() ) if ( thread != NULL && thread->TestDestroy() )
throw; throw;
lastError = wxEmptyString; mySAX2Handler.getErrors() << _("Unexpected validation error");
return false; return false;
} }
return true;
}
const wxString &WrapXerces::getLastError() return mySAX2Handler.getErrors().empty();
{
int i = lastError.Find( _T ( "Message:" ) );
if ( i != wxNOT_FOUND )
{
lastError = lastError.substr( i );
}
return lastError;
}
std::pair<int, int> WrapXerces::getErrorPosition()
{
return errorPosition;
} }
const wxMBConv &WrapXerces::getMBConv() const wxMBConv &WrapXerces::getMBConv()
@ -240,3 +205,20 @@ wxMemoryBuffer WrapXerces::toString ( const wxString &str )
return buffer; 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();
}
}

View File

@ -23,6 +23,7 @@
#include <wx/wx.h> #include <wx/wx.h>
#include <wx/strconv.h> #include <wx/strconv.h>
#include <wx/buffer.h> #include <wx/buffer.h>
#include <wx/log.h>
#include <string> #include <string>
#include <utility> #include <utility>
#include <boost/utility.hpp> #include <boost/utility.hpp>
@ -37,6 +38,58 @@
using namespace xercesc; 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<int, int> &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<int, int> mErrorPosition;
wxLogLevelValues mLevel;
wxString mEOL;
};
class WrapXerces : private boost::noncopyable class WrapXerces : private boost::noncopyable
{ {
public: public:
@ -44,10 +97,19 @@ class WrapXerces : private boost::noncopyable
WrapXerces(); WrapXerces();
virtual ~WrapXerces(); virtual ~WrapXerces();
bool validate ( const wxString &fileName ); bool validate ( const wxString &fileName );
bool validateMemory ( const char *buffer, size_t len, bool validateMemory ( const char *utf8Buffer, size_t len,
const wxString &system, wxThread *thread = NULL ); const wxString &fileName, wxThread *thread = NULL,
const wxString &getLastError(); bool forceGrammarCheck = true, /* Must specify a grammar */
std::pair<int, int> getErrorPosition(); const wxChar *messageEOL = _T("[br]") );
const wxString &getLastError()
{
return mySAX2Handler.getErrors();
}
const std::pair<int, int> &getErrorPosition()
{
return mySAX2Handler.getErrorPosition();
}
static wxString toString ( const XMLCh *str ); static wxString toString ( const XMLCh *str );
// Convert Unicode string to const XMLCh * // Convert Unicode string to const XMLCh *
//#if wxCHECK_VERSION(2,9,0) //#if wxCHECK_VERSION(2,9,0)
@ -60,21 +122,7 @@ class WrapXerces : private boost::noncopyable
static const wxMBConv &getMBConv(); static const wxMBConv &getMBConv();
XercesCatalogResolver *catalogResolver; XercesCatalogResolver *catalogResolver;
wxString lastError; MySAX2Handler mySAX2Handler;
std::pair<int, int> errorPosition;
};
class MySAX2Handler : public DefaultHandler
{
public:
void error ( const SAXParseException& e )
{
throw e;
}
void warning ( const SAXParseException& e )
{
throw e;
}
}; };
#endif #endif

View File

@ -3922,32 +3922,14 @@ void MyFrame::OnValidateSchema ( wxCommandEvent& event )
} }
#endif #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..." ) ); statusProgress ( _ ( "Validation in progress..." ) );
doc->clearErrorIndicators(); doc->clearErrorIndicators();
wxString fileName = doc->getFullFileName();
wxString utf8Buffer = doc->myGetTextRaw();
std::auto_ptr<WrapXerces> validator ( new WrapXerces() ); std::auto_ptr<WrapXerces> validator ( new WrapXerces() );
if ( !validator->validate ( fileName ) ) if ( !validator->validateMemory ( utf8Buffer.c_str(), utf8Buffer.size(),
fileName ) )
{ {
statusProgress ( wxEmptyString ); statusProgress ( wxEmptyString );
messagePane ( validator->getLastError(), CONST_WARNING ); messagePane ( validator->getLastError(), CONST_WARNING );