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 "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();

View File

@ -19,11 +19,13 @@
#include "wrapxerces.h"
#include <xercesc/parsers/XercesDOMParser.hpp>
#include <xercesc/sax2/XMLReaderFactory.hpp>
#include <xercesc/sax2/SAX2XMLReader.hpp>
#include <xercesc/sax2/DefaultHandler.hpp>
#include <xercesc/util/XMLUni.hpp>
#include <xercesc/framework/MemBufInputSource.hpp>
#include <xercesc/framework/LocalFileInputSource.hpp>
#include <sstream>
#include <utility>
#include <stdexcept>
@ -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();
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::fgSAX2CoreValidation, true );
parser->setFeature ( XMLUni::fgXercesDynamic, false );
parser->setFeature ( XMLUni::fgXercesDynamic, !forceGrammarCheck );
parser->setFeature ( XMLUni::fgXercesSchema, true );
parser->setFeature ( XMLUni::fgXercesSchemaFullChecking, true);
parser->setFeature ( XMLUni::fgXercesValidationErrorAsFatal, 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->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<InputSource> 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<int, int> 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();
}
}

View File

@ -23,6 +23,7 @@
#include <wx/wx.h>
#include <wx/strconv.h>
#include <wx/buffer.h>
#include <wx/log.h>
#include <string>
#include <utility>
#include <boost/utility.hpp>
@ -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<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
{
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<int, int> 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<int, int> &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<int, int> errorPosition;
};
class MySAX2Handler : public DefaultHandler
{
public:
void error ( const SAXParseException& e )
{
throw e;
}
void warning ( const SAXParseException& e )
{
throw e;
}
MySAX2Handler mySAX2Handler;
};
#endif

View File

@ -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<WrapXerces> validator ( new WrapXerces() );
if ( !validator->validate ( fileName ) )
if ( !validator->validateMemory ( utf8Buffer.c_str(), utf8Buffer.size(),
fileName ) )
{
statusProgress ( wxEmptyString );
messagePane ( validator->getLastError(), CONST_WARNING );