OpenWalnut  1.4.0
WModuleProjectFileCombiner.cpp
00001 //---------------------------------------------------------------------------
00002 //
00003 // Project: OpenWalnut ( http://www.openwalnut.org )
00004 //
00005 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
00006 // For more information see http://www.openwalnut.org/copying
00007 //
00008 // This file is part of OpenWalnut.
00009 //
00010 // OpenWalnut is free software: you can redistribute it and/or modify
00011 // it under the terms of the GNU Lesser General Public License as published by
00012 // the Free Software Foundation, either version 3 of the License, or
00013 // (at your option) any later version.
00014 //
00015 // OpenWalnut is distributed in the hope that it will be useful,
00016 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 // GNU Lesser General Public License for more details.
00019 //
00020 // You should have received a copy of the GNU Lesser General Public License
00021 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
00022 //
00023 //---------------------------------------------------------------------------
00024 
00025 #include <iostream>
00026 #include <map>
00027 #include <set>
00028 #include <list>
00029 #include <string>
00030 #include <utility>
00031 #include <limits>
00032 
00033 #include <boost/regex.hpp>
00034 
00035 #include "../WKernel.h"
00036 #include "../WModuleCombiner.h"
00037 #include "../WModuleFactory.h"
00038 #include "../WModuleConnector.h"
00039 #include "../WModule.h"
00040 #include "../WDataModule.h"
00041 #include "../WModuleInputConnector.h"
00042 #include "../WModuleOutputConnector.h"
00043 #include "../exceptions/WModuleConnectorNotFound.h"
00044 
00045 #include "../../common/exceptions/WFileNotFound.h"
00046 #include "../../common/WStringUtils.h"
00047 #include "../../common/WProperties.h"
00048 #include "../../common/WPropertyBase.h"
00049 #include "../../common/WPropertyVariable.h"
00050 #include "../../common/WPropertyTypes.h"
00051 #include "../../common/WLogger.h"
00052 #include "../WProjectFile.h"
00053 #include "../../graphicsEngine/WGEColormapping.h"
00054 
00055 #include "WModuleProjectFileCombiner.h"
00056 
00057 WModuleProjectFileCombiner::WModuleProjectFileCombiner( boost::shared_ptr< WModuleContainer > target ):
00058     WModuleCombiner( target ),
00059     WProjectFileIO()
00060 {
00061 }
00062 
00063 WModuleProjectFileCombiner::WModuleProjectFileCombiner():
00064     WModuleCombiner( WKernel::getRunningKernel()->getRootContainer() ),
00065     WProjectFileIO()
00066 {
00067 }
00068 
00069 WModuleProjectFileCombiner::~WModuleProjectFileCombiner()
00070 {
00071     // cleanup
00072     m_modules.clear();
00073 }
00074 
00075 WProjectFileIO::SPtr WModuleProjectFileCombiner::clone( WProjectFile* project ) const
00076 {
00077     // nothing special. Simply create new instance.
00078     WProjectFileIO::SPtr p( new WModuleProjectFileCombiner() );
00079     p->setProject( project );
00080     return p;
00081 }
00082 
00083 bool WModuleProjectFileCombiner::parse( std::string line, unsigned int lineNumber )
00084 {
00085     // this is the proper regular expression for modules
00086     static const boost::regex modRe( "^ *MODULE:([0-9]*):(.*)$" );
00087     static const boost::regex dataRe( "^ *DATA:([0-9]*):\"?([^\"]*)\"?$" );
00088     static const boost::regex conRe( "^ *CONNECTION:\\(([0-9]*),(.*)\\)->\\(([0-9]*),(.*)\\)$" );
00089     static const boost::regex propRe( "^ *PROPERTY:\\(([0-9]*),(.*)\\)=(.*)$" );
00090 
00091     boost::smatch matches;  // the list of matches
00092     if( boost::regex_match( line, matches, modRe ) )
00093     {
00094         // it is a module line
00095         // matches[1] is the ID
00096         // matches[2] is the name of the module
00097 
00098         wlog::debug( "Project Loader [Parser]" ) << "Line " << lineNumber << ": Module \"" << matches[2] << "\" with ID " << matches[1];
00099 
00100         // create a module instance
00101         boost::shared_ptr< WModule > proto = WModuleFactory::getModuleFactory()-> isPrototypeAvailable( matches[2] );
00102 
00103         // data modules are not allowed here
00104         if( !proto )
00105         {
00106             addError( "There is no prototype available for module \"" + matches[2] + "\". Skipping." );
00107         }
00108         else if( proto->getType() == MODULE_DATA )
00109         {
00110             addError( "Data modules are not allowed to be specified in a \"MODULE\" Statement. Use the \"DATA\" statement instead. Skipping." );
00111         }
00112         else
00113         {
00114             boost::shared_ptr< WModule > module = WModuleFactory::getModuleFactory()->create( proto );
00115             // set restore mode
00116             module->setRestoreNeeded();
00117 
00118             m_modules.insert( ModuleID( string_utils::fromString< unsigned int >( matches[1] ), module ) );
00119         }
00120     }
00121     else if( boost::regex_match( line, matches, dataRe ) )
00122     {
00123         // it is a dataset line
00124         // matches[1] is the ID
00125         // matches[2] is the filename
00126         wlog::debug( "Project Loader [Parser]" ) << "Line " << lineNumber << ": Data \"" << matches[2] << "\" with ID " << matches[1];
00127 
00128         // create a module instance
00129         boost::shared_ptr< WModule > proto = WModuleFactory::getModuleFactory()-> isPrototypeAvailable( "Data Module" );
00130         if( !proto )
00131         {
00132             addError( "There is no prototype available for module \"Data Module\". This should not happen!. Skipping." );
00133         }
00134         else
00135         {
00136             std::string parameter = std::string( matches[2] );
00137             boost::shared_ptr< WModule > module = WModuleFactory::getModuleFactory()->create( proto );
00138 
00139             // set restore mode
00140             module->setRestoreNeeded();
00141             if( parameter.empty() )
00142             {
00143                 addError( "Data modules need an additional filename parameter. Skipping." );
00144             }
00145             else
00146             {
00147                 boost::static_pointer_cast< WDataModule >( module )->setFilename( parameter );
00148                 m_modules.insert( ModuleID( string_utils::fromString< unsigned int >( matches[1] ), module ) );
00149             }
00150         }
00151     }
00152     else if( boost::regex_match( line, matches, conRe ) )
00153     {
00154         // it is a connector line
00155         // matches[1] and [2] are the module ID and connector name of the output connector
00156         // matches[3] and [4] are the module ID and connector name of the target input connector
00157 
00158         wlog::debug( "Project Loader [Parser]" ) << "Line " << lineNumber << ": Connection between \"" << matches[2] << "\" of module "
00159                                                  << matches[1] << " and \"" << matches[4] << "\" of module " << matches[3] << ".";
00160 
00161         // now we search in modules[ matches[1] ] for an output connector named matches[2]
00162         m_connections.push_back( Connection( Connector( string_utils::fromString< unsigned int >( matches[1] ), matches[2] ),
00163                                            Connector( string_utils::fromString< unsigned int >( matches[3] ), matches[4] ) ) );
00164     }
00165     else if( boost::regex_match( line, matches, propRe ) )
00166     {
00167         // it is a property line
00168         // matches[1] is the module ID
00169         // matches[2] is the property name
00170         // matches[3] is the property value
00171 
00172         wlog::debug( "Project Loader [Parser]" ) << "Line " << lineNumber << ": Property \"" << matches[2] << "\" of module " << matches[1]
00173                                                  << " set to " << matches[3];
00174 
00175         m_properties.push_back( PropertyValue( Property( string_utils::fromString< unsigned int >( matches[1] ), matches[2] ), matches[3] ) );
00176     }
00177     else
00178     {
00179         return false;
00180     }
00181 
00182     return true;
00183 }
00184 
00185 void WModuleProjectFileCombiner::apply()
00186 {
00187     // reset sort indices in colormapper as we load new ones.
00188     WGEColormapping::instance()->resetSortIndices();
00189 
00190     // now add each module to the target container
00191     for( std::map< unsigned int, boost::shared_ptr< WModule > >::const_iterator iter = m_modules.begin(); iter != m_modules.end(); ++iter )
00192     {
00193         m_container->add( ( *iter ).second );
00194     }
00195 
00196     // now wait for the modules to get ready. We could have waited for this in the previous loop, but a long loading module would block others.
00197     // -> so we wait after adding and starting them
00198     for( std::map< unsigned int, boost::shared_ptr< WModule > >::iterator iter = m_modules.begin(); iter != m_modules.end(); ++iter )
00199     {
00200         ( *iter ).second->isReadyOrCrashed().wait();
00201 
00202         // if isReady now is false, the module has crashed before it got ready -> remove the module from the list
00203         if( ( *iter ).second->isCrashed()() )
00204         {
00205             addError( "In the module with ID " + ( *iter ).first +
00206                       std::string( " a problem occurred. Connections and properties relating to this module will fail." ) );
00207             // m_modules.erase( iter );
00208         }
00209     }
00210 
00211     // now, as we have created the modules, we need to set the properties for each of it.
00212     for( std::list< PropertyValue >::const_iterator iter = m_properties.begin(); iter != m_properties.end(); ++iter )
00213     {
00214         // grab corresponding module
00215         if( !m_modules.count( ( *iter ).first.first ) )
00216         {
00217             addError( "There is no module with ID \"" + string_utils::toString( ( *iter ).first.first ) + "\" to set the property \"" +
00218                                                         ( *iter ).first.second + std::string( "\" for. Skipping." ) );
00219             continue;
00220         }
00221         boost::shared_ptr< WModule > m = m_modules[ ( *iter ).first.first ];
00222 
00223         // has this module the specified property?
00224         boost::shared_ptr< WPropertyBase > prop = m->getProperties()->findProperty( ( *iter ).first.second );
00225         if( !prop )
00226         {
00227             addWarning( "The module \"" + m->getName() + std::string( "\" has no property named \"" ) + ( *iter ).first.second +
00228                         std::string( "\". Skipping." ) );
00229             continue;
00230         }
00231         else
00232         {
00233             if( prop->getPurpose() != PV_PURPOSE_INFORMATION )
00234             {
00235                 // set the property here
00236                 bool result = prop->setAsString( ( *iter ).second );
00237                 if( !result )
00238                 {
00239                     addWarning( "Failed to set property " + ( *iter ).first.second + " in module \"" + m->getName() + "\"." );
00240                 }
00241             }
00242             else
00243             {
00244                 addWarning( "The module \"" + m->getName() + "\" has a property named \"" +
00245                             ( *iter ).first.second + "\" which is an INFORMATION property. Skipping." );
00246             }
00247         }
00248     }
00249 
00250     // and finally, connect them all together
00251     for( std::list< Connection >::const_iterator iter = m_connections.begin(); iter != m_connections.end(); ++iter )
00252     {
00253         // each connection contains two connectors
00254         Connector c1 = ( *iter ).first;
00255         Connector c2 = ( *iter ).second;
00256 
00257         // each of these connectors contains the module ID and the connector name
00258         // grab corresponding module 1
00259         boost::shared_ptr< WModule > m1;
00260         if( !m_modules.count( c1.first ) )
00261         {
00262             addError( "There is no module with ID \"" + string_utils::toString( c1.first ) + "\" for the connection "
00263                       + "(" + string_utils::toString( c1.first ) + "," + c1.second + ")->(" +  string_utils::toString( c2.first ) + "," +
00264                       c2.second + "). Skipping." );
00265             continue;
00266         }
00267         m1 = m_modules[ c1.first ];
00268 
00269         boost::shared_ptr< WModule > m2;
00270         if( !m_modules.count( c2.first ) )
00271         {
00272             addError( "There is no module with ID \"" + string_utils::toString( c2.first ) +  "\" for the connection "
00273                       + "(" + string_utils::toString( c1.first ) + "," + c1.second + ")->(" + string_utils::toString( c2.first ) +
00274                       "," + c2.second + "). Skipping." );
00275 
00276             continue;
00277         }
00278         m2 = m_modules[ c2.first ];
00279 
00280         // now we have the modules referenced by the ID
00281         // -> query the connectors
00282         // NOTE: we assume the first connector to be an output connector!
00283         boost::shared_ptr< WModuleOutputConnector > con1;
00284         try
00285         {
00286             con1 = m1->getOutputConnector( c1.second );
00287         }
00288         catch( const WModuleConnectorNotFound& e )
00289         {
00290             addError( "There is no output connector \"" + c1.second + "\" in module \"" + m1->getName() + "\"" );
00291             continue;
00292         }
00293         boost::shared_ptr< WModuleInputConnector > con2;
00294         try
00295         {
00296             con2 = m2->getInputConnector( c2.second );
00297         }
00298         catch( const WModuleConnectorNotFound& e )
00299         {
00300             addError( "There is no input connector \"" + c2.second + "\" in module \"" + m2->getName() + "\"" );
00301             continue;
00302         }
00303 
00304         // finally, connect them
00305         try
00306         {
00307             con1->connect( con2 );
00308         }
00309         catch( const WException& e )
00310         {
00311             addError( "Connection (" + string_utils::toString( c1.first ) + "," + c1.second + ")->(" +
00312                       string_utils::toString( c2.first ) + "," + c2.second +
00313                       ") could not be created. Incompatible connectors?. Skipping." );
00314             continue;
00315         }
00316     }
00317 
00318     // the colornapper should now sort the textures according to the loaded indices
00319     WGEColormapping::instance()->sortByIndex();
00320 
00321     // notify modules about the loaded set properties
00322     for( std::map< unsigned int, boost::shared_ptr< WModule > >::iterator iter = m_modules.begin(); iter != m_modules.end(); ++iter )
00323     {
00324         ( *iter ).second->reportRestoreComplete();
00325     }
00326 
00327     // clear all our lists (deref all contained pointers)
00328     m_connections.clear();
00329     m_properties.clear();
00330 }
00331 
00332 void WModuleProjectFileCombiner::done()
00333 {
00334     apply();
00335 }
00336 
00337 void WModuleProjectFileCombiner::save( std::ostream& output )   // NOLINT
00338 {
00339     // we need to save the colormapper's texture order. To do this, we need to update the textures sort indices
00340     WGEColormapping::instance()->setSortIndices();
00341 
00342     // grab access object of root container
00343     WModuleContainer::ModuleSharedContainerType::ReadTicket container = WKernel::getRunningKernel()->getRootContainer()->getModules();
00344 
00345     std::map< boost::shared_ptr< WModule >, unsigned int > moduleToIDMap;
00346 
00347     output << "//////////////////////////////////////////////////////////////////////////////////////////////////////////////////" << std::endl <<
00348               "// Modules and Properties" << std::endl <<
00349               "//////////////////////////////////////////////////////////////////////////////////////////////////////////////////" << std::endl <<
00350               std::endl;
00351 
00352     // iterate all modules:
00353     unsigned int i = 0;
00354     for( WModuleContainer::ModuleConstIterator iter = container->get().begin(); iter != container->get().end(); ++iter )
00355     {
00356         // store the mapping of ptr to ID
00357         moduleToIDMap[ ( *iter ) ] = i;
00358 
00359         // handle data modules separately
00360         if( ( *iter )->getType() == MODULE_DATA )
00361         {
00362             output << "DATA:" << i << ":" <<  boost::static_pointer_cast< WDataModule >( ( *iter ) )->getFilename().string() << std::endl;
00363         }
00364         else
00365         {
00366             output << "MODULE:" << i << ":" <<  ( *iter )->getName() << std::endl;
00367         }
00368 
00369         // the properties:
00370         printProperties( output, ( *iter )->getProperties(), "", "", i );
00371 
00372         m_modules.insert( ModuleID( i, *iter ) );
00373 
00374         // some readability:
00375         output << std::endl;
00376         ++i;
00377     }
00378 
00379     // finally, process all connections for each module
00380     output << "//////////////////////////////////////////////////////////////////////////////////////////////////////////////////" << std::endl <<
00381               "// Connections" << std::endl <<
00382               "//////////////////////////////////////////////////////////////////////////////////////////////////////////////////" << std::endl <<
00383               std::endl;
00384 
00385 
00386     // iterate over all modules
00387     for( WModuleContainer::ModuleConstIterator iter = container->get().begin(); iter != container->get().end(); ++iter )
00388     {
00389         // iterate over all outputs
00390         const WModule::OutputConnectorList& outs = ( *iter )->getOutputConnectors();
00391         for( WModule::OutputConnectorList::const_iterator citer = outs.begin(); citer != outs.end(); ++citer )
00392         {
00393             // iterate over all connections:
00394             boost::unique_lock<boost::shared_mutex> lock( ( *citer )->m_connectionListLock );
00395             for( std::set<boost::shared_ptr<WModuleConnector> >::const_iterator iciter = ( *citer )->m_connected.begin();
00396                   iciter != ( *citer )->m_connected.end(); ++iciter )
00397             {
00398                 // as the module is a weak_ptr -> lock and get access to it
00399                 boost::shared_ptr< WModule > theOtherModule = ( *iciter )->m_module.lock();
00400                 output << "CONNECTION:(" << moduleToIDMap[ ( *iter ) ] << "," << ( *citer )->getName() << ")->(" <<
00401                                             moduleToIDMap[ theOtherModule ] << "," << ( *iciter )->getName() << ")" << std::endl;
00402             }
00403             lock.unlock();
00404         }
00405     }
00406 }
00407 
00408 boost::shared_ptr< WModule > WModuleProjectFileCombiner::mapToModule( unsigned int id ) const
00409 {
00410     // existing?
00411     ModuleIDMap::const_iterator it = m_modules.find( id );
00412     if( it == m_modules.end() )
00413     {
00414         return WModule::SPtr();
00415     }
00416 
00417     return ( *it ).second;
00418 }
00419 
00420 unsigned int WModuleProjectFileCombiner::mapFromModule( boost::shared_ptr< WModule > module ) const
00421 {
00422     ModuleIDMap::const_iterator it = std::find_if( m_modules.begin(), m_modules.end(),
00423                                                    boost::bind( &ModuleIDMap::value_type::second, _1 ) == module
00424                                                  );
00425     if( it == m_modules.end() )
00426     {
00427         return std::numeric_limits< unsigned int >::max();
00428     }
00429 
00430     return ( *it ).first;
00431 }
00432