OpenWalnut  1.4.0
WModuleProjectFileCombiner.cpp
1 //---------------------------------------------------------------------------
2 //
3 // Project: OpenWalnut ( http://www.openwalnut.org )
4 //
5 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
6 // For more information see http://www.openwalnut.org/copying
7 //
8 // This file is part of OpenWalnut.
9 //
10 // OpenWalnut is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // OpenWalnut is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
22 //
23 //---------------------------------------------------------------------------
24 
25 #include <iostream>
26 #include <map>
27 #include <set>
28 #include <list>
29 #include <string>
30 #include <utility>
31 #include <limits>
32 
33 #include <boost/regex.hpp>
34 
35 #include "../WKernel.h"
36 #include "../WModuleCombiner.h"
37 #include "../WModuleFactory.h"
38 #include "../WModuleConnector.h"
39 #include "../WModule.h"
40 #include "../WDataModule.h"
41 #include "../WModuleInputConnector.h"
42 #include "../WModuleOutputConnector.h"
43 #include "../exceptions/WModuleConnectorNotFound.h"
44 
45 #include "../../common/exceptions/WFileNotFound.h"
46 #include "../../common/WStringUtils.h"
47 #include "../../common/WProperties.h"
48 #include "../../common/WPropertyBase.h"
49 #include "../../common/WPropertyVariable.h"
50 #include "../../common/WPropertyTypes.h"
51 #include "../../common/WLogger.h"
52 #include "../WProjectFile.h"
53 #include "../../graphicsEngine/WGEColormapping.h"
54 
55 #include "WModuleProjectFileCombiner.h"
56 
57 WModuleProjectFileCombiner::WModuleProjectFileCombiner( boost::shared_ptr< WModuleContainer > target ):
58  WModuleCombiner( target ),
60 {
61 }
62 
64  WModuleCombiner( WKernel::getRunningKernel()->getRootContainer() ),
66 {
67 }
68 
70 {
71  // cleanup
72  m_modules.clear();
73 }
74 
76 {
77  // nothing special. Simply create new instance.
79  p->setProject( project );
80  return p;
81 }
82 
83 bool WModuleProjectFileCombiner::parse( std::string line, unsigned int lineNumber )
84 {
85  // this is the proper regular expression for modules
86  static const boost::regex modRe( "^ *MODULE:([0-9]*):(.*)$" );
87  static const boost::regex dataRe( "^ *DATA:([0-9]*):\"?([^\"]*)\"?$" );
88  static const boost::regex conRe( "^ *CONNECTION:\\(([0-9]*),(.*)\\)->\\(([0-9]*),(.*)\\)$" );
89  static const boost::regex propRe( "^ *PROPERTY:\\(([0-9]*),(.*)\\)=(.*)$" );
90 
91  boost::smatch matches; // the list of matches
92  if( boost::regex_match( line, matches, modRe ) )
93  {
94  // it is a module line
95  // matches[1] is the ID
96  // matches[2] is the name of the module
97 
98  wlog::debug( "Project Loader [Parser]" ) << "Line " << lineNumber << ": Module \"" << matches[2] << "\" with ID " << matches[1];
99 
100  // create a module instance
101  boost::shared_ptr< WModule > proto = WModuleFactory::getModuleFactory()-> isPrototypeAvailable( matches[2] );
102 
103  // data modules are not allowed here
104  if( !proto )
105  {
106  addError( "There is no prototype available for module \"" + matches[2] + "\". Skipping." );
107  }
108  else if( proto->getType() == MODULE_DATA )
109  {
110  addError( "Data modules are not allowed to be specified in a \"MODULE\" Statement. Use the \"DATA\" statement instead. Skipping." );
111  }
112  else
113  {
114  boost::shared_ptr< WModule > module = WModuleFactory::getModuleFactory()->create( proto );
115  // set restore mode
116  module->setRestoreNeeded();
117 
118  m_modules.insert( ModuleID( string_utils::fromString< unsigned int >( matches[1] ), module ) );
119  }
120  }
121  else if( boost::regex_match( line, matches, dataRe ) )
122  {
123  // it is a dataset line
124  // matches[1] is the ID
125  // matches[2] is the filename
126  wlog::debug( "Project Loader [Parser]" ) << "Line " << lineNumber << ": Data \"" << matches[2] << "\" with ID " << matches[1];
127 
128  // create a module instance
129  boost::shared_ptr< WModule > proto = WModuleFactory::getModuleFactory()-> isPrototypeAvailable( "Data Module" );
130  if( !proto )
131  {
132  addError( "There is no prototype available for module \"Data Module\". This should not happen!. Skipping." );
133  }
134  else
135  {
136  std::string parameter = std::string( matches[2] );
137  boost::shared_ptr< WModule > module = WModuleFactory::getModuleFactory()->create( proto );
138 
139  // set restore mode
140  module->setRestoreNeeded();
141  if( parameter.empty() )
142  {
143  addError( "Data modules need an additional filename parameter. Skipping." );
144  }
145  else
146  {
147  boost::static_pointer_cast< WDataModule >( module )->setFilename( parameter );
148  m_modules.insert( ModuleID( string_utils::fromString< unsigned int >( matches[1] ), module ) );
149  }
150  }
151  }
152  else if( boost::regex_match( line, matches, conRe ) )
153  {
154  // it is a connector line
155  // matches[1] and [2] are the module ID and connector name of the output connector
156  // matches[3] and [4] are the module ID and connector name of the target input connector
157 
158  wlog::debug( "Project Loader [Parser]" ) << "Line " << lineNumber << ": Connection between \"" << matches[2] << "\" of module "
159  << matches[1] << " and \"" << matches[4] << "\" of module " << matches[3] << ".";
160 
161  // now we search in modules[ matches[1] ] for an output connector named matches[2]
162  m_connections.push_back( Connection( Connector( string_utils::fromString< unsigned int >( matches[1] ), matches[2] ),
163  Connector( string_utils::fromString< unsigned int >( matches[3] ), matches[4] ) ) );
164  }
165  else if( boost::regex_match( line, matches, propRe ) )
166  {
167  // it is a property line
168  // matches[1] is the module ID
169  // matches[2] is the property name
170  // matches[3] is the property value
171 
172  wlog::debug( "Project Loader [Parser]" ) << "Line " << lineNumber << ": Property \"" << matches[2] << "\" of module " << matches[1]
173  << " set to " << matches[3];
174 
175  m_properties.push_back( PropertyValue( Property( string_utils::fromString< unsigned int >( matches[1] ), matches[2] ), matches[3] ) );
176  }
177  else
178  {
179  return false;
180  }
181 
182  return true;
183 }
184 
186 {
187  // reset sort indices in colormapper as we load new ones.
188  WGEColormapping::instance()->resetSortIndices();
189 
190  // now add each module to the target container
191  for( std::map< unsigned int, boost::shared_ptr< WModule > >::const_iterator iter = m_modules.begin(); iter != m_modules.end(); ++iter )
192  {
193  m_container->add( ( *iter ).second );
194  }
195 
196  // 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.
197  // -> so we wait after adding and starting them
198  for( std::map< unsigned int, boost::shared_ptr< WModule > >::iterator iter = m_modules.begin(); iter != m_modules.end(); ++iter )
199  {
200  ( *iter ).second->isReadyOrCrashed().wait();
201 
202  // if isReady now is false, the module has crashed before it got ready -> remove the module from the list
203  if( ( *iter ).second->isCrashed()() )
204  {
205  addError( "In the module with ID " + ( *iter ).first +
206  std::string( " a problem occurred. Connections and properties relating to this module will fail." ) );
207  // m_modules.erase( iter );
208  }
209  }
210 
211  // now, as we have created the modules, we need to set the properties for each of it.
212  for( std::list< PropertyValue >::const_iterator iter = m_properties.begin(); iter != m_properties.end(); ++iter )
213  {
214  // grab corresponding module
215  if( !m_modules.count( ( *iter ).first.first ) )
216  {
217  addError( "There is no module with ID \"" + string_utils::toString( ( *iter ).first.first ) + "\" to set the property \"" +
218  ( *iter ).first.second + std::string( "\" for. Skipping." ) );
219  continue;
220  }
221  boost::shared_ptr< WModule > m = m_modules[ ( *iter ).first.first ];
222 
223  // has this module the specified property?
224  boost::shared_ptr< WPropertyBase > prop = m->getProperties()->findProperty( ( *iter ).first.second );
225  if( !prop )
226  {
227  addWarning( "The module \"" + m->getName() + std::string( "\" has no property named \"" ) + ( *iter ).first.second +
228  std::string( "\". Skipping." ) );
229  continue;
230  }
231  else
232  {
233  if( prop->getPurpose() != PV_PURPOSE_INFORMATION )
234  {
235  // set the property here
236  bool result = prop->setAsString( ( *iter ).second );
237  if( !result )
238  {
239  addWarning( "Failed to set property " + ( *iter ).first.second + " in module \"" + m->getName() + "\"." );
240  }
241  }
242  else
243  {
244  addWarning( "The module \"" + m->getName() + "\" has a property named \"" +
245  ( *iter ).first.second + "\" which is an INFORMATION property. Skipping." );
246  }
247  }
248  }
249 
250  // and finally, connect them all together
251  for( std::list< Connection >::const_iterator iter = m_connections.begin(); iter != m_connections.end(); ++iter )
252  {
253  // each connection contains two connectors
254  Connector c1 = ( *iter ).first;
255  Connector c2 = ( *iter ).second;
256 
257  // each of these connectors contains the module ID and the connector name
258  // grab corresponding module 1
259  boost::shared_ptr< WModule > m1;
260  if( !m_modules.count( c1.first ) )
261  {
262  addError( "There is no module with ID \"" + string_utils::toString( c1.first ) + "\" for the connection "
263  + "(" + string_utils::toString( c1.first ) + "," + c1.second + ")->(" + string_utils::toString( c2.first ) + "," +
264  c2.second + "). Skipping." );
265  continue;
266  }
267  m1 = m_modules[ c1.first ];
268 
269  boost::shared_ptr< WModule > m2;
270  if( !m_modules.count( c2.first ) )
271  {
272  addError( "There is no module with ID \"" + string_utils::toString( c2.first ) + "\" for the connection "
273  + "(" + string_utils::toString( c1.first ) + "," + c1.second + ")->(" + string_utils::toString( c2.first ) +
274  "," + c2.second + "). Skipping." );
275 
276  continue;
277  }
278  m2 = m_modules[ c2.first ];
279 
280  // now we have the modules referenced by the ID
281  // -> query the connectors
282  // NOTE: we assume the first connector to be an output connector!
283  boost::shared_ptr< WModuleOutputConnector > con1;
284  try
285  {
286  con1 = m1->getOutputConnector( c1.second );
287  }
288  catch( const WModuleConnectorNotFound& e )
289  {
290  addError( "There is no output connector \"" + c1.second + "\" in module \"" + m1->getName() + "\"" );
291  continue;
292  }
293  boost::shared_ptr< WModuleInputConnector > con2;
294  try
295  {
296  con2 = m2->getInputConnector( c2.second );
297  }
298  catch( const WModuleConnectorNotFound& e )
299  {
300  addError( "There is no input connector \"" + c2.second + "\" in module \"" + m2->getName() + "\"" );
301  continue;
302  }
303 
304  // finally, connect them
305  try
306  {
307  con1->connect( con2 );
308  }
309  catch( const WException& e )
310  {
311  addError( "Connection (" + string_utils::toString( c1.first ) + "," + c1.second + ")->(" +
312  string_utils::toString( c2.first ) + "," + c2.second +
313  ") could not be created. Incompatible connectors?. Skipping." );
314  continue;
315  }
316  }
317 
318  // the colornapper should now sort the textures according to the loaded indices
319  WGEColormapping::instance()->sortByIndex();
320 
321  // notify modules about the loaded set properties
322  for( std::map< unsigned int, boost::shared_ptr< WModule > >::iterator iter = m_modules.begin(); iter != m_modules.end(); ++iter )
323  {
324  ( *iter ).second->reportRestoreComplete();
325  }
326 
327  // clear all our lists (deref all contained pointers)
328  m_connections.clear();
329  m_properties.clear();
330 }
331 
333 {
334  apply();
335 }
336 
337 void WModuleProjectFileCombiner::save( std::ostream& output ) // NOLINT
338 {
339  // we need to save the colormapper's texture order. To do this, we need to update the textures sort indices
340  WGEColormapping::instance()->setSortIndices();
341 
342  // grab access object of root container
344 
345  std::map< boost::shared_ptr< WModule >, unsigned int > moduleToIDMap;
346 
347  output << "//////////////////////////////////////////////////////////////////////////////////////////////////////////////////" << std::endl <<
348  "// Modules and Properties" << std::endl <<
349  "//////////////////////////////////////////////////////////////////////////////////////////////////////////////////" << std::endl <<
350  std::endl;
351 
352  // iterate all modules:
353  unsigned int i = 0;
354  for( WModuleContainer::ModuleConstIterator iter = container->get().begin(); iter != container->get().end(); ++iter )
355  {
356  // store the mapping of ptr to ID
357  moduleToIDMap[ ( *iter ) ] = i;
358 
359  // handle data modules separately
360  if( ( *iter )->getType() == MODULE_DATA )
361  {
362  output << "DATA:" << i << ":" << boost::static_pointer_cast< WDataModule >( ( *iter ) )->getFilename().string() << std::endl;
363  }
364  else
365  {
366  output << "MODULE:" << i << ":" << ( *iter )->getName() << std::endl;
367  }
368 
369  // the properties:
370  printProperties( output, ( *iter )->getProperties(), "", "", i );
371 
372  m_modules.insert( ModuleID( i, *iter ) );
373 
374  // some readability:
375  output << std::endl;
376  ++i;
377  }
378 
379  // finally, process all connections for each module
380  output << "//////////////////////////////////////////////////////////////////////////////////////////////////////////////////" << std::endl <<
381  "// Connections" << std::endl <<
382  "//////////////////////////////////////////////////////////////////////////////////////////////////////////////////" << std::endl <<
383  std::endl;
384 
385 
386  // iterate over all modules
387  for( WModuleContainer::ModuleConstIterator iter = container->get().begin(); iter != container->get().end(); ++iter )
388  {
389  // iterate over all outputs
390  const WModule::OutputConnectorList& outs = ( *iter )->getOutputConnectors();
391  for( WModule::OutputConnectorList::const_iterator citer = outs.begin(); citer != outs.end(); ++citer )
392  {
393  // iterate over all connections:
394  boost::unique_lock<boost::shared_mutex> lock( ( *citer )->m_connectionListLock );
395  for( std::set<boost::shared_ptr<WModuleConnector> >::const_iterator iciter = ( *citer )->m_connected.begin();
396  iciter != ( *citer )->m_connected.end(); ++iciter )
397  {
398  // as the module is a weak_ptr -> lock and get access to it
399  boost::shared_ptr< WModule > theOtherModule = ( *iciter )->m_module.lock();
400  output << "CONNECTION:(" << moduleToIDMap[ ( *iter ) ] << "," << ( *citer )->getName() << ")->(" <<
401  moduleToIDMap[ theOtherModule ] << "," << ( *iciter )->getName() << ")" << std::endl;
402  }
403  lock.unlock();
404  }
405  }
406 }
407 
408 boost::shared_ptr< WModule > WModuleProjectFileCombiner::mapToModule( unsigned int id ) const
409 {
410  // existing?
411  ModuleIDMap::const_iterator it = m_modules.find( id );
412  if( it == m_modules.end() )
413  {
414  return WModule::SPtr();
415  }
416 
417  return ( *it ).second;
418 }
419 
420 unsigned int WModuleProjectFileCombiner::mapFromModule( boost::shared_ptr< WModule > module ) const
421 {
422  ModuleIDMap::const_iterator it = std::find_if( m_modules.begin(), m_modules.end(),
423  boost::bind( &ModuleIDMap::value_type::second, _1 ) == module
424  );
425  if( it == m_modules.end() )
426  {
427  return std::numeric_limits< unsigned int >::max();
428  }
429 
430  return ( *it ).first;
431 }
432 
boost::shared_ptr< WModuleContainer > getRootContainer() const
Returns the root module container.
Definition: WKernel.cpp:127
virtual const std::string getName() const =0
Gets the name of this prototype.
Base for all data loader modules.
Definition: WDataModule.h:36
static WKernel * getRunningKernel()
Returns pointer to the currently running kernel.
Definition: WKernel.cpp:117
std::pair< unsigned int, std::string > Connector
A connector is described by ID and name.
virtual void apply()
Apply the internal module structure to the target container.
std::pair< unsigned int, boost::shared_ptr< WModule > > ModuleID
The module ID type.
OpenWalnut kernel, managing modules and interaction between UI, GE and DataHandler.
Definition: WKernel.h:61
std::vector< boost::shared_ptr< WModuleOutputConnector > > OutputConnectorList
The type for the list of output connectors.
Definition: WModule.h:104
void addWarning(std::string description)
Add an warning.
virtual SPtr clone(WProjectFile *project) const
Create a clone of the IO.
virtual boost::shared_ptr< WModule > mapToModule(unsigned int id) const
Map a given project file ID to a module.
std::pair< Property, std::string > PropertyValue
A property value is a property and the new value as string.
virtual ~WModuleProjectFileCombiner()
Destructor.
General purpose exception and therefore base class for all kernel related exceptions.
virtual void save(std::ostream &output)
Saves the state to the specified stream.
std::pair< unsigned int, std::string > Property
A property is a pair of ID and name.
Class loading project files.
Definition: WProjectFile.h:49
std::list< PropertyValue > m_properties
All properties.
This is a base class for all module combination classes.
ModuleContainerType::const_iterator ModuleConstIterator
The const iterator type of the container.
void addError(std::string description)
Add an error.
boost::shared_ptr< WProjectFileIO > SPtr
Abbreviation for a shared pointer.
static boost::shared_ptr< WGEColormapping > instance()
Returns instance of the module factory to use to create modules.
std::string toString(const T &value)
Convert a given value to a string.
Definition: WStringUtils.h:120
std::list< Connection > m_connections
All connections.
boost::shared_ptr< WModuleContainer > m_container
The module container to use for the modules.
boost::shared_ptr< WModule > SPtr
Shared pointer to a WModule.
Definition: WModule.h:109
static SPtr getModuleFactory()
Returns instance of the module factory to use to create modules.
A base class for all parts of OpenWalnut which can be serialized to a project file.
void printProperties(std::ostream &output, boost::shared_ptr< WProperties > props, std::string indent, std::string prefix, unsigned int index, std::string indexPrefix="")
Recursively prints the properties and nested properties.
virtual bool parse(std::string line, unsigned int lineNumber)
This method parses the specified line and interprets it to fill the internal module graph structure...
Basic exception handler.
Definition: WException.h:38
std::pair< Connector, Connector > Connection
A connection is a pair of connectors.
virtual unsigned int mapFromModule(boost::shared_ptr< WModule > module) const
Map a given module to project file ID.
WModuleProjectFileCombiner()
Creates an empty combiner.
WStreamedLogger debug(const std::string &source)
Logging a debug message.
Definition: WLogger.h:331
boost::shared_ptr< WSharedObjectTicketRead< ModuleContainerType > > ReadTicket
Type for read tickets.
Definition: WSharedObject.h:62
virtual void done()
Called whenever the end of the project file has been reached.
std::map< unsigned int, boost::shared_ptr< WModule > > m_modules
All Modules.