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