Commit e353eb7f authored by Sebastian Eichelbaum's avatar Sebastian Eichelbaum
Browse files

[ADD #125] error report for failed project files.

parent 06b365ed
......@@ -25,6 +25,8 @@
#include <vector>
#include <string>
#include "WLogger.h"
#include "WProjectFileIO.h"
WProjectFileIO::WProjectFileIO():
......@@ -53,3 +55,9 @@ const std::vector< std::string >& WProjectFileIO::getErrors() const
return m_errors;
}
void WProjectFileIO::addError( std::string description )
{
wlog::error( "Project Loader" ) << description;
m_errors.push_back( description );
}
......@@ -26,6 +26,7 @@
#include <fstream>
#include <string>
#include <vector>
#include <iterator>
#include <boost/regex.hpp>
......@@ -76,6 +77,7 @@ WProjectFile::~WProjectFile()
{
// cleanup
m_parsers.clear();
m_signalLoadDoneConnection.disconnect();
}
boost::shared_ptr< WProjectFileIO > WProjectFile::getCameraWriter()
......@@ -193,7 +195,7 @@ void WProjectFile::threadMain()
{
( *iter )->done();
// append errors
std::copy( ( *iter )->getErrors().begin(), ( *iter )->getErrors().begin(), errors.begin() );
std::copy( ( *iter )->getErrors().begin(), ( *iter )->getErrors().end(), std::back_inserter( errors ) );
}
catch( const std::exception& e )
{
......
......@@ -40,8 +40,8 @@
* Class loading project files. This class opens an file and reads it line by line. It delegates the actual parsing to each of the known
* WProjectFileIO instances which then do their job.
*/
class WProjectFile: public WThreadedRunner,
public boost::enable_shared_from_this< WProjectFile >
class WProjectFile: public WThreadedRunner,
public boost::enable_shared_from_this< WProjectFile >
{
public:
/**
......
......@@ -92,12 +92,11 @@ bool WModuleProjectFileCombiner::parse( std::string line, unsigned int lineNumbe
// data modules are not allowed here
if( !proto )
{
wlog::error( "Project Loader" ) << "There is no prototype available for module \"" << matches[2] << "\". Skipping.";
addError( "There is no prototype available for module \"" + matches[2] + "\". Skipping." );
}
else if( proto->getType() == MODULE_DATA )
{
wlog::error( "Project Loader" ) << "Data modules are not allowed to be specified in a \"MODULE\" Statement." <<
" Use the \"DATA\" statement instead. Skipping.";
addError( "Data modules are not allowed to be specified in a \"MODULE\" Statement. Use the \"DATA\" statement instead. Skipping." );
}
else
{
......@@ -116,8 +115,7 @@ bool WModuleProjectFileCombiner::parse( std::string line, unsigned int lineNumbe
boost::shared_ptr< WModule > proto = WModuleFactory::getModuleFactory()-> isPrototypeAvailable( "Data Module" );
if( !proto )
{
wlog::error( "Project Loader" ) << "There is no prototype available for module \"" << "Data Module" << "\"."
<< " This should not happen!. Skipping.";
addError( "There is no prototype available for module \"Data Module\". This should not happen!. Skipping." );
}
else
{
......@@ -125,7 +123,7 @@ bool WModuleProjectFileCombiner::parse( std::string line, unsigned int lineNumbe
boost::shared_ptr< WModule > module = WModuleFactory::getModuleFactory()->create( proto );
if( parameter.empty() )
{
wlog::error( "Project Loader" ) << "Data modules need an additional filename parameter. Skipping.";
addError( "Data modules need an additional filename parameter. Skipping." );
}
else
{
......@@ -184,10 +182,8 @@ void WModuleProjectFileCombiner::apply()
// if isReady now is false, the module has crashed before it got ready -> remove the module from the list
if( ( *iter ).second->isCrashed()() )
{
wlog::warn( "Project Loader" ) << "In the module with ID "
<< ( *iter ).first
<< " a problem occurred. Connections and properties relating to this"
<< " module will fail.";
addError( "In the module with ID " + ( *iter ).first +
std::string( " a problem occurred. Connections and properties relating to this module will fail." ) );
m_modules.erase( iter );
}
}
......@@ -198,8 +194,8 @@ void WModuleProjectFileCombiner::apply()
// grab corresponding module
if( !m_modules.count( ( *iter ).first.first ) )
{
wlog::error( "Project Loader" ) << "There is no module with ID \"" << ( *iter ).first.first << "\" to set the property \"" <<
( *iter ).first.second << "\" for. Skipping.";
addError( "There is no module with ID \"" + string_utils::toString( ( *iter ).first.first ) + "\" to set the property \"" +
( *iter ).first.second + std::string( "\" for. Skipping." ) );
continue;
}
boost::shared_ptr< WModule > m = m_modules[ ( *iter ).first.first ];
......@@ -208,8 +204,8 @@ void WModuleProjectFileCombiner::apply()
boost::shared_ptr< WPropertyBase > prop = m->getProperties()->findProperty( ( *iter ).first.second );
if( !prop )
{
wlog::error( "Project Loader" ) << "The module \"" << m->getName() << "\" has no property named \"" <<
( *iter ).first.second << "\". Skipping.";
addError( "The module \"" + m->getName() + std::string( "\" has no property named \"" ) + ( *iter ).first.second +
std::string( "\". Skipping." ) );
continue;
}
else
......@@ -220,14 +216,13 @@ void WModuleProjectFileCombiner::apply()
bool result = prop->setAsString( ( *iter ).second );
if( !result )
{
wlog::error( "Project Loader" ) << "Failed to set property " << ( *iter ).first.second << " in module \"" <<
m->getName() << "\".";
addError( "Failed to set property " + ( *iter ).first.second + " in module \"" + m->getName() + "\"." );
}
}
else
{
wlog::error( "Project Loader" ) << "The module \"" << m->getName() << "\" has a property named \"" <<
( *iter ).first.second << "\" which is an INFORMATION property. Skipping.";
addError( "The module \"" + m->getName() + "\" has a property named \"" +
( *iter ).first.second + "\" which is an INFORMATION property. Skipping." );
}
}
}
......@@ -244,9 +239,9 @@ void WModuleProjectFileCombiner::apply()
boost::shared_ptr< WModule > m1;
if( !m_modules.count( c1.first ) )
{
wlog::error( "Project Loader" ) << "There is no module with ID \"" << c1.first << "\" for the connection "
<< "(" << c1.first << "," << c1.second << ")->(" << c2.first << "," << c2.second << "). Skipping.";
addError( "There is no module with ID \"" + string_utils::toString( c1.first ) + "\" for the connection "
+ "(" + string_utils::toString( c1.first ) + "," + c1.second + ")->(" + string_utils::toString( c2.first ) + "," +
c2.second + "). Skipping." );
continue;
}
m1 = m_modules[ c1.first ];
......@@ -254,8 +249,9 @@ void WModuleProjectFileCombiner::apply()
boost::shared_ptr< WModule > m2;
if( !m_modules.count( c2.first ) )
{
wlog::error( "Project Loader" ) << "There is no module with ID \"" << c2.first << "\" for the connection "
<< "(" << c1.first << "," << c1.second << ")->(" << c2.first << "," << c2.second << "). Skipping.";
addError( "There is no module with ID \"" + string_utils::toString( c2.first ) + "\" for the connection "
+ "(" + string_utils::toString( c1.first ) + "," + c1.second + ")->(" + string_utils::toString( c2.first ) +
"," + c2.second + "). Skipping." );
continue;
}
......@@ -271,7 +267,7 @@ void WModuleProjectFileCombiner::apply()
}
catch( const WModuleConnectorNotFound& e )
{
wlog::error( "Project Loader" ) << "There is no output connector \"" << c1.second << "\" in module \"" << m1->getName() << "\"";
addError( "There is no output connector \"" + c1.second + "\" in module \"" + m1->getName() + "\"" );
continue;
}
boost::shared_ptr< WModuleInputConnector > con2;
......@@ -281,7 +277,7 @@ void WModuleProjectFileCombiner::apply()
}
catch( const WModuleConnectorNotFound& e )
{
wlog::error( "Project Loader" ) << "There is no input connector \"" << c2.second << "\" in module \"" << m2->getName() << "\"";
addError( "There is no input connector \"" + c2.second + "\" in module \"" + m2->getName() + "\"" );
continue;
}
......@@ -292,8 +288,9 @@ void WModuleProjectFileCombiner::apply()
}
catch( const WException& e )
{
wlog::error( "Project Loader" ) << "Connection " << "(" << c1.first << "," << c1.second << ")->(" << c2.first << "," << c2.second <<
") could not be created. Incompatible connectors?. Skipping.";
addError( "Connection (" + string_utils::toString( c1.first ) + "," + c1.second + ")->(" +
string_utils::toString( c2.first ) + "," + c2.second +
") could not be created. Incompatible connectors?. Skipping." );
continue;
}
}
......
......@@ -44,7 +44,7 @@
* WProjectFileIO to allow WProjectFile to fill this combiner.
*/
class WModuleProjectFileCombiner: public WModuleCombiner,
public WProjectFileIO
public WProjectFileIO
{
public:
/**
......
......@@ -80,6 +80,7 @@
#include "events/WModuleRemovedEvent.h"
#include "events/WOpenCustomDockWidgetEvent.h"
#include "events/WCloseCustomDockWidgetEvent.h"
#include "events/WLoadFinishedEvent.h"
#include "guiElements/WQtPropertyBoolAction.h"
#include "WQt4Gui.h"
#include "WQtCombinerToolbar.h"
......@@ -690,9 +691,7 @@ void WMainWindow::openLoadDialog()
// is this a project file?
if( ( suffix == ".owp" ) || ( suffix == ".owproj" ) )
{
WProjectFile::SPtr proj( new WProjectFile( fn.string() ) );
// This call is asynchronous. It parses the file and the starts a thread to actually do all the stuff
proj->load();
asyncProjectLoad( fn.string() );
}
else
{
......@@ -704,6 +703,23 @@ void WMainWindow::openLoadDialog()
m_loaderSignal( loadDataFilenames );
}
void WMainWindow::asyncProjectLoad( std::string filename )
{
WProjectFile::SPtr proj( new WProjectFile( filename, boost::bind( &WMainWindow::slotLoadFinished, this, _1, _2 ) ) );
proj->load();
}
void WMainWindow::slotLoadFinished( boost::filesystem::path file, std::vector< std::string > errors )
{
// as this function might be called from outside the gui thread, use an event:
QCoreApplication::postEvent( this, new WLoadFinishedEvent( file, errors ) );
if( errors.size() )
{
wlog::warn( "MainWindow" ) << "Async load error occurred. Informing user.";
}
}
void WMainWindow::openAboutQtDialog()
{
QMessageBox::aboutQt( this, "About Qt" );
......@@ -964,6 +980,40 @@ bool WMainWindow::event( QEvent* event )
}
}
if( event->type() == WQT_LOADFINISHED )
{
// convert event
WLoadFinishedEvent* e1 = dynamic_cast< WLoadFinishedEvent* >( event );
if( e1 )
{
if( e1->getErrors().size() )
{
size_t curErrCount = 0;
const size_t maxErrCount = 5;
std::string errors = "<ul>";
for( std::vector< std::string >::const_iterator iter = e1->getErrors().begin(); iter != e1->getErrors().end(); ++iter )
{
errors += "<li> " + *iter;
curErrCount++;
if( ( curErrCount == maxErrCount ) && ( e1->getErrors().size() > maxErrCount ) )
{
size_t errDiff = e1->getErrors().size() - curErrCount;
errors += "<li> ... and " + string_utils::toString( errDiff ) + " more errors.";
break;
}
}
errors += "</ul>";
QMessageBox::critical( this, "Error during load",
"Errors occurred during load of \"" + QString::fromStdString( e1->getFilename() ) + "\". "
"The loader tried to apply as much as possible, ignoring the erroneous data. The first errors where:"
"<br><br>"
"<font color=\"#f00\">" + QString::fromStdString( errors )+ "</font>" );
}
}
}
return QMainWindow::event( event );
}
......@@ -1143,12 +1193,7 @@ void WMainWindow::dropEvent( QDropEvent *event )
{
for( size_t i = 0; i < projects.size(); ++i )
{
boost::shared_ptr< WProjectFile > proj = boost::shared_ptr< WProjectFile >(
new WProjectFile( projects[ i ] )
);
// This call is asynchronous. It parses the file and the starts a thread to actually do all the stuff
proj->load();
asyncProjectLoad( projects[ i ] );
}
event->accept();
}
......
......@@ -170,6 +170,13 @@ public:
*/
QSplashScreen* getSplash() const;
/**
* Loads a given project asynchronously.
*
* \param filename the file to load.
*/
void asyncProjectLoad( std::string filename );
protected:
/**
* Setup the GUI by handling special modules. NavSlices for example setup several toolbar buttons.
......@@ -393,6 +400,15 @@ private:
*/
WSettingAction* m_autoDisplaySetting;
/**
* Called whenever a async load has finished. Used by \ref asyncProjectLoad. It might be called from outside the GUI thread.
*
* \param file the filename
* \param errors the list of errors
*/
void slotLoadFinished( boost::filesystem::path file, std::vector< std::string > errors );
private slots:
/**
* Handles some special GL vendors and shows the user a dialog.
......
......@@ -114,12 +114,8 @@ void WQt4Gui::deferredLoad()
{
try
{
boost::shared_ptr< WProjectFile > proj = boost::shared_ptr< WProjectFile >(
new WProjectFile( m_optionsMap["project"].as< std::string >() )
);
// This call is asynchronous. It parses the file and the starts a thread to actually do all the stuff
proj->load();
m_mainWindow->asyncProjectLoad( m_optionsMap["project"].as< std::string >() );
}
catch( const WException& e )
{
......
......@@ -142,7 +142,7 @@ public:
*/
boost::signals2::signal1< void, std::vector< std::string > >* getLoadButtonSignal();
/**
/**
* Instruct to open a new custom widget. The specified condition should be the shutdown condition of the module, as the function returns only
* if the widget was created. To ensure that the creation is aborted properly if the module shuts down in the meantime, this condition is
* used.
......
......@@ -881,7 +881,7 @@ void WQtControlPanel::setActiveModule( WModule::SPtr module, bool forceUpdate )
if( m_previousTab != "" )
{
// search the tab with the previous title
for( size_t idx = 0; idx < m_tabWidget->count(); ++idx )
for( int idx = 0; idx < m_tabWidget->count(); ++idx )
{
if( m_tabWidget->tabText( idx ) == m_previousTab )
{
......
......@@ -81,4 +81,7 @@
// close an existing custom dock widget
#define WQT_CLOSECUSTOMDOCKWIDGET QEvent::User + 16
// A async load operation has finished
#define WQT_LOADFINISHED QEvent::User + 17
#endif // WEVENTTYPES_H
//---------------------------------------------------------------------------
//
// Project: OpenWalnut ( http://www.openwalnut.org )
//
// Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
// For more information see http://www.openwalnut.org/copying
//
// This file is part of OpenWalnut.
//
// OpenWalnut is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// OpenWalnut is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
//
//---------------------------------------------------------------------------
#include <vector>
#include <string>
#include "WEventTypes.h"
#include "WLoadFinishedEvent.h"
WLoadFinishedEvent::WLoadFinishedEvent( boost::filesystem::path filename, std::vector< std::string > errors )
: QEvent( static_cast< QEvent::Type >( WQT_LOADFINISHED ) ),
m_filename( filename ),
m_errors( errors )
{
// initialize members
}
WLoadFinishedEvent::~WLoadFinishedEvent()
{
// cleanup
}
const std::vector< std::string >& WLoadFinishedEvent::getErrors() const
{
return m_errors;
}
std::string WLoadFinishedEvent::getFilename() const
{
return m_filename.string();
}
//---------------------------------------------------------------------------
//
// Project: OpenWalnut ( http://www.openwalnut.org )
//
// Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
// For more information see http://www.openwalnut.org/copying
//
// This file is part of OpenWalnut.
//
// OpenWalnut is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// OpenWalnut is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
//
//---------------------------------------------------------------------------
#ifndef WLOADFINISHEDEVENT_H
#define WLOADFINISHEDEVENT_H
#include <vector>
#include <string>
#include <boost/filesystem/path.hpp>
#include <QtCore/QEvent>
/**
* Event signalling a finished asynchronous load job.
*/
class WLoadFinishedEvent: public QEvent
{
public:
/**
* Creates a new event instance denoting that a asynchronous load was finished.
*
* \param filename the filename of the file that was loaded
* \param errors the list of errors. Can be empty.
*/
WLoadFinishedEvent( boost::filesystem::path filename, std::vector< std::string > errors );
/**
* Destructor.
*/
virtual ~WLoadFinishedEvent();
/**
* Returns the list of errors
*
* \return the errors.
*/
const std::vector< std::string >& getErrors() const;
/**
* The filename of the file loaded.
*
* \return the filename
*/
std::string getFilename() const;
protected:
private:
/**
* The filename of the file loaded
*/
boost::filesystem::path m_filename;
/**
* The error list.
*/
std::vector< std::string > m_errors;
};
#endif // WLOADFINISHEDEVENT_H
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment