OpenWalnut 1.3.1
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 
00032 #include <boost/regex.hpp>
00033 
00034 #include "../WKernel.h"
00035 #include "../WModuleCombiner.h"
00036 #include "../WModuleFactory.h"
00037 #include "../WModuleConnector.h"
00038 #include "../WModule.h"
00039 #include "../WDataModule.h"
00040 #include "../WModuleInputConnector.h"
00041 #include "../WModuleOutputConnector.h"
00042 #include "../exceptions/WModuleConnectorNotFound.h"
00043 
00044 #include "../../common/exceptions/WFileNotFound.h"
00045 #include "../../common/WStringUtils.h"
00046 #include "../../common/WProperties.h"
00047 #include "../../common/WPropertyBase.h"
00048 #include "../../common/WPropertyVariable.h"
00049 #include "../../common/WPropertyTypes.h"
00050 #include "../../common/WLogger.h"
00051 #include "../../common/math/linearAlgebra/WLinearAlgebra.h"
00052 
00053 #include "WModuleProjectFileCombiner.h"
00054 
00055 WModuleProjectFileCombiner::WModuleProjectFileCombiner( boost::shared_ptr< WModuleContainer > target ):
00056     WModuleCombiner( target ),
00057     WProjectFileIO()
00058 {
00059 }
00060 
00061 WModuleProjectFileCombiner::WModuleProjectFileCombiner():
00062     WModuleCombiner( WKernel::getRunningKernel()->getRootContainer() ),
00063     WProjectFileIO()
00064 {
00065 }
00066 
00067 WModuleProjectFileCombiner::~WModuleProjectFileCombiner()
00068 {
00069     // cleanup
00070 }
00071 
00072 bool WModuleProjectFileCombiner::parse( std::string line, unsigned int lineNumber )
00073 {
00074     // this is the proper regular expression for modules
00075     static const boost::regex modRe( "^ *MODULE:([0-9]*):(.*)$" );
00076     static const boost::regex dataRe( "^ *DATA:([0-9]*):\"?([^\"]*)\"?$" );
00077     static const boost::regex conRe( "^ *CONNECTION:\\(([0-9]*),(.*)\\)->\\(([0-9]*),(.*)\\)$" );
00078     static const boost::regex propRe( "^ *PROPERTY:\\(([0-9]*),(.*)\\)=(.*)$" );
00079 
00080     boost::smatch matches;  // the list of matches
00081     if( boost::regex_match( line, matches, modRe ) )
00082     {
00083         // it is a module line
00084         // matches[1] is the ID
00085         // matches[2] is the name of the module
00086 
00087         wlog::debug( "Project Loader [Parser]" ) << "Line " << lineNumber << ": Module \"" << matches[2] << "\" with ID " << matches[1];
00088 
00089         // create a module instance
00090         boost::shared_ptr< WModule > proto = WModuleFactory::getModuleFactory()-> isPrototypeAvailable( matches[2] );
00091 
00092         // data modules are not allowed here
00093         if( !proto )
00094         {
00095             addError( "There is no prototype available for module \"" + matches[2] + "\". Skipping." );
00096         }
00097         else if( proto->getType() == MODULE_DATA )
00098         {
00099             addError( "Data modules are not allowed to be specified in a \"MODULE\" Statement. Use the \"DATA\" statement instead. Skipping." );
00100         }
00101         else
00102         {
00103             boost::shared_ptr< WModule > module = WModuleFactory::getModuleFactory()->create( proto );
00104             m_modules.insert( ModuleID( string_utils::fromString< unsigned int >( matches[1] ), module ) );
00105         }
00106     }
00107     else if( boost::regex_match( line, matches, dataRe ) )
00108     {
00109         // it is a dataset line
00110         // matches[1] is the ID
00111         // matches[2] is the filename
00112         wlog::debug( "Project Loader [Parser]" ) << "Line " << lineNumber << ": Data \"" << matches[2] << "\" with ID " << matches[1];
00113 
00114         // create a module instance
00115         boost::shared_ptr< WModule > proto = WModuleFactory::getModuleFactory()-> isPrototypeAvailable( "Data Module" );
00116         if( !proto )
00117         {
00118             addError( "There is no prototype available for module \"Data Module\". This should not happen!. Skipping." );
00119         }
00120         else
00121         {
00122             std::string parameter = std::string( matches[2] );
00123             boost::shared_ptr< WModule > module = WModuleFactory::getModuleFactory()->create( proto );
00124             if( parameter.empty() )
00125             {
00126                 addError( "Data modules need an additional filename parameter. Skipping." );
00127             }
00128             else
00129             {
00130                 boost::shared_static_cast< WDataModule >( module )->setFilename( parameter );
00131                 m_modules.insert( ModuleID( string_utils::fromString< unsigned int >( matches[1] ), module ) );
00132             }
00133         }
00134     }
00135     else if( boost::regex_match( line, matches, conRe ) )
00136     {
00137         // it is a connector line
00138         // matches[1] and [2] are the module ID and connector name of the output connector
00139         // matches[3] and [4] are the module ID and connector name of the target input connector
00140 
00141         wlog::debug( "Project Loader [Parser]" ) << "Line " << lineNumber << ": Connection between \"" << matches[2] << "\" of module "
00142                                                  << matches[1] << " and \"" << matches[4] << "\" of module " << matches[3] << ".";
00143 
00144         // now we search in modules[ matches[1] ] for an output connector named matches[2]
00145         m_connections.push_back( Connection( Connector( string_utils::fromString< unsigned int >( matches[1] ), matches[2] ),
00146                                            Connector( string_utils::fromString< unsigned int >( matches[3] ), matches[4] ) ) );
00147     }
00148     else if( boost::regex_match( line, matches, propRe ) )
00149     {
00150         // it is a property line
00151         // matches[1] is the module ID
00152         // matches[2] is the property name
00153         // matches[3] is the property value
00154 
00155         wlog::debug( "Project Loader [Parser]" ) << "Line " << lineNumber << ": Property \"" << matches[2] << "\" of module " << matches[1]
00156                                                  << " set to " << matches[3];
00157 
00158         m_properties.push_back( PropertyValue( Property( string_utils::fromString< unsigned int >( matches[1] ), matches[2] ), matches[3] ) );
00159     }
00160     else
00161     {
00162         return false;
00163     }
00164 
00165     return true;
00166 }
00167 
00168 void WModuleProjectFileCombiner::apply()
00169 {
00170     // now add each module to the target container
00171     for( std::map< unsigned int, boost::shared_ptr< WModule > >::const_iterator iter = m_modules.begin(); iter != m_modules.end(); ++iter )
00172     {
00173         m_container->add( ( *iter ).second );
00174     }
00175 
00176     // 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.
00177     // -> so we wait after adding and starting them
00178     for( std::map< unsigned int, boost::shared_ptr< WModule > >::iterator iter = m_modules.begin(); iter != m_modules.end(); ++iter )
00179     {
00180         ( *iter ).second->isReadyOrCrashed().wait();
00181 
00182         // if isReady now is false, the module has crashed before it got ready -> remove the module from the list
00183         if( ( *iter ).second->isCrashed()() )
00184         {
00185             addError( "In the module with ID " + ( *iter ).first +
00186                       std::string( " a problem occurred. Connections and properties relating to this module will fail." ) );
00187             m_modules.erase( iter );
00188         }
00189     }
00190 
00191     // now, as we have created the modules, we need to set the properties for each of it.
00192     for( std::list< PropertyValue >::const_iterator iter = m_properties.begin(); iter != m_properties.end(); ++iter )
00193     {
00194         // grab corresponding module
00195         if( !m_modules.count( ( *iter ).first.first ) )
00196         {
00197             addError( "There is no module with ID \"" + string_utils::toString( ( *iter ).first.first ) + "\" to set the property \"" +
00198                                                         ( *iter ).first.second + std::string( "\" for. Skipping." ) );
00199             continue;
00200         }
00201         boost::shared_ptr< WModule > m = m_modules[ ( *iter ).first.first ];
00202 
00203         // has this module the specified property?
00204         boost::shared_ptr< WPropertyBase > prop = m->getProperties()->findProperty( ( *iter ).first.second );
00205         if( !prop )
00206         {
00207             addError( "The module \"" + m->getName() + std::string( "\" has no property named \"" ) + ( *iter ).first.second +
00208                       std::string( "\". Skipping." ) );
00209             continue;
00210         }
00211         else
00212         {
00213             if( prop->getPurpose() != PV_PURPOSE_INFORMATION )
00214             {
00215                 // set the property here
00216                 bool result = prop->setAsString( ( *iter ).second );
00217                 if( !result )
00218                 {
00219                     addError( "Failed to set property " + ( *iter ).first.second + " in module \"" + m->getName() + "\"." );
00220                 }
00221             }
00222             else
00223             {
00224                 addError( "The module \"" + m->getName() + "\" has a property named \"" +
00225                          ( *iter ).first.second + "\" which is an INFORMATION property. Skipping." );
00226             }
00227         }
00228     }
00229 
00230     // and finally, connect them all together
00231     for( std::list< Connection >::const_iterator iter = m_connections.begin(); iter != m_connections.end(); ++iter )
00232     {
00233         // each connection contains two connectors
00234         Connector c1 = ( *iter ).first;
00235         Connector c2 = ( *iter ).second;
00236 
00237         // each of these connectors contains the module ID and the connector name
00238         // grab corresponding module 1
00239         boost::shared_ptr< WModule > m1;
00240         if( !m_modules.count( c1.first ) )
00241         {
00242             addError( "There is no module with ID \"" + string_utils::toString( c1.first ) + "\" for the connection "
00243                       + "(" + string_utils::toString( c1.first ) + "," + c1.second + ")->(" +  string_utils::toString( c2.first ) + "," +
00244                       c2.second + "). Skipping." );
00245             continue;
00246         }
00247         m1 = m_modules[ c1.first ];
00248 
00249         boost::shared_ptr< WModule > m2;
00250         if( !m_modules.count( c2.first ) )
00251         {
00252             addError( "There is no module with ID \"" + string_utils::toString( c2.first ) +  "\" for the connection "
00253                       + "(" + string_utils::toString( c1.first ) + "," + c1.second + ")->(" + string_utils::toString( c2.first ) +
00254                       "," + c2.second + "). Skipping." );
00255 
00256             continue;
00257         }
00258         m2 = m_modules[ c2.first ];
00259 
00260         // now we have the modules referenced by the ID
00261         // -> query the connectors
00262         // NOTE: we assume the first connector to be an output connector!
00263         boost::shared_ptr< WModuleOutputConnector > con1;
00264         try
00265         {
00266             con1 = m1->getOutputConnector( c1.second );
00267         }
00268         catch( const WModuleConnectorNotFound& e )
00269         {
00270             addError( "There is no output connector \"" + c1.second + "\" in module \"" + m1->getName() + "\"" );
00271             continue;
00272         }
00273         boost::shared_ptr< WModuleInputConnector > con2;
00274         try
00275         {
00276             con2 = m2->getInputConnector( c2.second );
00277         }
00278         catch( const WModuleConnectorNotFound& e )
00279         {
00280             addError( "There is no input connector \"" + c2.second + "\" in module \"" + m2->getName() + "\"" );
00281             continue;
00282         }
00283 
00284         // finally, connect them
00285         try
00286         {
00287             con1->connect( con2 );
00288         }
00289         catch( const WException& e )
00290         {
00291             addError( "Connection (" + string_utils::toString( c1.first ) + "," + c1.second + ")->(" +
00292                       string_utils::toString( c2.first ) + "," + c2.second +
00293                       ") could not be created. Incompatible connectors?. Skipping." );
00294             continue;
00295         }
00296     }
00297 
00298     // clear all our lists (deref all contained pointers)
00299     m_modules.clear();
00300     m_connections.clear();
00301     m_properties.clear();
00302 }
00303 
00304 void WModuleProjectFileCombiner::done()
00305 {
00306     apply();
00307 }
00308 
00309 void WModuleProjectFileCombiner::printProperties( std::ostream& output, boost::shared_ptr< WProperties > props, std::string indent, //NOLINT
00310                                                   std::string prefix, unsigned int module )
00311 {
00312     // lock, unlocked if l looses focus
00313     WProperties::PropertySharedContainerType::ReadTicket l = props->getProperties();
00314 
00315     output << indent << "// Property Group: " << props->getName() << std::endl;
00316 
00317     // iterate of them and print them to output
00318     for( WProperties::PropertyConstIterator iter = l->get().begin(); iter != l->get().end(); ++iter )
00319     {
00320         // information properties do not get written
00321         if( ( *iter )->getPurpose () == PV_PURPOSE_INFORMATION )
00322         {
00323             continue;
00324         }
00325         if( ( *iter )->getType() != PV_GROUP )
00326         {
00327             output << indent + "    " << "PROPERTY:(" << module << "," << prefix + ( *iter )->getName() << ")="
00328                    << ( *iter )->getAsString() << std::endl;
00329         }
00330         else
00331         {
00332             // it is a group -> recursively print it
00333             if( prefix.empty() )
00334             {
00335                 printProperties( output, ( *iter )->toPropGroup(), indent + "    ", ( *iter )->getName() + "/", module );
00336             }
00337             else
00338             {
00339                 printProperties( output, ( *iter )->toPropGroup(), indent + "    ", prefix + ( *iter )->getName() + "/", module );
00340             }
00341         }
00342     }
00343 
00344     output << indent << "// Property Group END: " << props->getName() << std::endl;
00345 }
00346 
00347 void WModuleProjectFileCombiner::save( std::ostream& output )   // NOLINT
00348 {
00349     // grab access object of root container
00350     WModuleContainer::ModuleSharedContainerType::ReadTicket container = WKernel::getRunningKernel()->getRootContainer()->getModules();
00351 
00352     std::map< boost::shared_ptr< WModule >, unsigned int > moduleToIDMap;
00353 
00354     output << "//////////////////////////////////////////////////////////////////////////////////////////////////////////////////" << std::endl <<
00355               "// Modules and Properties" << std::endl <<
00356               "//////////////////////////////////////////////////////////////////////////////////////////////////////////////////" << std::endl <<
00357               std::endl;
00358 
00359     // iterate all modules:
00360     unsigned int i = 0;
00361     for( WModuleContainer::ModuleConstIterator iter = container->get().begin(); iter != container->get().end(); ++iter )
00362     {
00363         // store the mapping of ptr to ID
00364         moduleToIDMap[ ( *iter ) ] = i;
00365 
00366         // handle data modules separately
00367         if( ( *iter )->getType() == MODULE_DATA )
00368         {
00369             output << "DATA:" << i << ":" <<  boost::shared_static_cast< WDataModule >( ( *iter ) )->getFilename().string() << std::endl;
00370         }
00371         else
00372         {
00373             output << "MODULE:" << i << ":" <<  ( *iter )->getName() << std::endl;
00374         }
00375 
00376         // the properties:
00377         printProperties( output, ( *iter )->getProperties(), "", "", i );
00378 
00379         // some readability:
00380         output << std::endl;
00381         ++i;
00382     }
00383 
00384     // finally, process all connections for each module
00385     output << "//////////////////////////////////////////////////////////////////////////////////////////////////////////////////" << std::endl <<
00386               "// Connections" << std::endl <<
00387               "//////////////////////////////////////////////////////////////////////////////////////////////////////////////////" << std::endl <<
00388               std::endl;
00389 
00390 
00391     // iterate over all modules
00392     for( WModuleContainer::ModuleConstIterator iter = container->get().begin(); iter != container->get().end(); ++iter )
00393     {
00394         // iterate over all outputs
00395         const WModule::OutputConnectorList& outs = ( *iter )->getOutputConnectors();
00396         for( WModule::OutputConnectorList::const_iterator citer = outs.begin(); citer != outs.end(); ++citer )
00397         {
00398             // iterate over all connections:
00399             boost::unique_lock<boost::shared_mutex> lock( ( *citer )->m_connectionListLock );
00400             for( std::set<boost::shared_ptr<WModuleConnector> >::const_iterator iciter = ( *citer )->m_connected.begin();
00401                   iciter != ( *citer )->m_connected.end(); ++iciter )
00402             {
00403                 // as the module is a weak_ptr -> lock and get access to it
00404                 boost::shared_ptr< WModule > theOtherModule = ( *iciter )->m_module.lock();
00405                 output << "CONNECTION:(" << moduleToIDMap[ ( *iter ) ] << "," << ( *citer )->getName() << ")->(" <<
00406                                             moduleToIDMap[ theOtherModule ] << "," << ( *iciter )->getName() << ")" << std::endl;
00407             }
00408             lock.unlock();
00409         }
00410     }
00411 }
00412