TuttleOFX  1
node.hpp
Go to the documentation of this file.
00001 #ifndef _SAM_NODE_HPP_
00002 #define _SAM_NODE_HPP_
00003 
00004 #include <tuttle/host/Core.hpp>
00005 
00006 #include <boost/algorithm/string/case_conv.hpp>
00007 #include <boost/foreach.hpp>
00008 #include <boost/regex.hpp>
00009 
00010 #include <map>
00011 
00012 namespace sam {
00013 
00014 inline std::string retrieveNodeFullname( const std::string& userId )
00015 {
00016         std::string userIdLower = userId;
00017         boost::to_lower( userIdLower );
00018 
00019         std::vector<std::string> results;
00020         std::vector<std::string> ambiguousResults;
00021 
00022         typedef std::map<std::string, tuttle::host::ofx::imageEffect::OfxhImageEffectPlugin*> PluginMap;
00023         const PluginMap& pluginsById = tuttle::host::core().getImageEffectPluginCache().getPluginsByID();
00024 
00025         if( pluginsById.find(userIdLower) != pluginsById.end() )
00026         {
00027                 return userIdLower;
00028         }
00029 
00030         BOOST_FOREACH( const PluginMap::value_type& plugin, pluginsById )
00031         {
00032                 const std::string& plugId = plugin.first;
00033                 boost::iterator_range<std::string::const_iterator> it = boost::algorithm::find_last( plugId, userIdLower );
00034                 if( it )
00035                 {
00036                         if( ( it.end() == plugId.end() ) &&
00037                                 ( *(it.begin()-1) == '.' )
00038                           )
00039                         {
00040                                 results.push_back( plugId );
00041                         }
00042                         else
00043                         {
00044                                 ambiguousResults.push_back( plugId );
00045                         }
00046                 }
00047         }
00048         if( results.size() == 1 )
00049                 return results.front();
00050         if( results.size() > 1 )
00051         {
00052                 ambiguousResults.insert( ambiguousResults.begin(), results.begin(), results.end() );
00053         }
00054         if( ambiguousResults.size() == 1 )
00055                 return ambiguousResults.front();
00056 
00057         if( ambiguousResults.size() > 1 )
00058         {
00059                 tuttle::exception::user userMsg;
00060                 userMsg + "Ambiguous node name \"" + userId + "\".\n";
00061                 userMsg + "Possible nodes:\n";
00062                 BOOST_FOREACH( const std::string& p, ambiguousResults )
00063                 {
00064                         userMsg + " - \"" + p + "\"\n";
00065                 }
00066                 BOOST_THROW_EXCEPTION( tuttle::exception::Value()
00067                         << userMsg );
00068         }
00069 
00070         BOOST_THROW_EXCEPTION( tuttle::exception::Value()
00071                 << tuttle::exception::user() + "Unrecognized node name \"" + userId + "\"." );
00072 }
00073 
00074 typedef std::pair<ttl::ofx::attribute::OfxhClipImage*, std::string> ClipAndConnection;
00075 
00076 void setParametersForNode( const std::vector<std::string> parameters, ttl::Graph::Node& currentNode, std::vector<ClipAndConnection>& clipsToConnect, const bool isDummyNode = false, bool orderedParams = true )
00077 {
00078         static const boost::regex re_param( "(?:([a-zA-Z_][a-zA-Z0-9_]*)=)?(.*)" );
00079         std::size_t paramIdx = 0;
00080         
00081         BOOST_FOREACH( const std::string& paramStr, parameters )
00082         {
00083                 boost::cmatch matches;
00084                 if( !boost::regex_match( paramStr.c_str(), matches, re_param ) )
00085                 {
00086                         BOOST_THROW_EXCEPTION(
00087                                 tuttle::exception::Value() <<
00088                                 tuttle::exception::user() + "Parameter can't be parsed \"" + paramStr + "\"." );
00089                 }
00090                 if( matches.size() != 3 )
00091                 {
00092                         // should never happen
00093                         BOOST_THROW_EXCEPTION(
00094                                 tuttle::exception::Value() <<
00095                                 tuttle::exception::user() + "Parameter can't be parsed \"" + paramStr + "\". " + matches.size() + " matches." );
00096                 }
00097                 const std::string attributeName = matches[1];
00098                 const std::string attributeValue = matches[2];
00099                 if( attributeName.size() )
00100                 {
00101                         // if we start using non-ordered param (== named param)
00102                         // we can't use ordered parameters anymore
00103                         orderedParams = false;
00104                 }
00105                 else if( orderedParams == false )
00106                 {
00107                         BOOST_THROW_EXCEPTION(
00108                                 tuttle::exception::Value() <<
00109                                 tuttle::exception::user() + "Non-keyword parameter after keyword parameter. \"" + paramStr + "\"." );
00110                 }
00111                 //TUTTLE_LOG_INFO( "* " << paramStr );
00112                 //TUTTLE_LOG_INFO( "3: " << paramName << " => " << paramValue );
00113 
00114                 if( isDummyNode )
00115                 {
00116                         ++paramIdx;
00117                         continue;
00118                 }
00119                 
00120                 // setup the node with parameter value in tuttle.
00121                 if( attributeName.size() )
00122                 {
00123                         // set a value to a named parameter or clip
00124                         using namespace ttl::ofx::attribute;
00125                         OfxhParam* param = NULL;
00126                         param = currentNode.getParamSet().getParamPtrByScriptName( attributeName );
00127 
00128                         OfxhClipImage* clip = NULL;
00129 
00130                         if( param == NULL )
00131                         {
00132                                 // search in clips
00133                                 clip = currentNode.getClipImageSet().getClipPtr( attributeName );
00134                         }
00135 
00136                         if( param == NULL && clip == NULL )
00137                         {
00138                                 std::vector<std::string> allAttr;
00139                                 std::vector<std::string> paramMatches;
00140                                 std::vector<std::string> clipMatches;
00141                                 //if( acceptPartialName ) // if sam-do accept partial names
00142                                 {
00143 
00144                                         BOOST_FOREACH( OfxhParamSet::ParamMap::value_type& p, currentNode.getParamSet().getParamsByScriptName() )
00145                                         {
00146                                                 allAttr.push_back( p.first );
00147                                                 if( boost::algorithm::starts_with( p.first, attributeName ) )
00148                                                 {
00149                                                         paramMatches.push_back( p.first );
00150                                                         param = p.second;
00151                                                 }
00152                                         }
00153 
00154                                         BOOST_FOREACH( OfxhClipImageSet::ClipImageMap::value_type& c, currentNode.getClipImageSet().getClipsByName() )
00155                                         {
00156                                                 allAttr.push_back( c.first );
00157                                                 if( boost::algorithm::starts_with( c.first, attributeName ) )
00158                                                 {
00159                                                         clipMatches.push_back( c.first );
00160                                                         clip = c.second;
00161                                                 }
00162                                         }
00163                                         if( paramMatches.size() + clipMatches.size() > 1 )
00164                                         {
00165                                                 std::vector<std::string> matches;
00166                                                 matches.insert( matches.begin(), paramMatches.begin(), paramMatches.end() );
00167                                                 matches.insert( matches.end(), clipMatches.begin(), clipMatches.end() );
00168                                                 BOOST_THROW_EXCEPTION(
00169                                                         ttl::exception::Value() <<
00170                                                         ttl::exception::user() + "Ambiguous partial attribute name \"" + attributeName + "\". Possible values are: " + boost::algorithm::join( matches, ", " ) + "." );
00171                                         }
00172                                 }
00173 
00174                                 if( paramMatches.size() + clipMatches.size() == 0 )
00175                                 {
00176                                         BOOST_THROW_EXCEPTION(
00177                                                 ttl::exception::Value() <<
00178                                                 ttl::exception::user() + "Attribute name \"" + attributeName + "\" not found. Possible values are: " + boost::algorithm::join( allAttr, ", " ) + "." );
00179                                 }
00180                         }
00181 
00182                         if( param != NULL )
00183                         {
00184                                 param->setValueFromExpression( attributeValue );
00185                         }
00186                         else if( clip != NULL )
00187                         {
00188                                 clipsToConnect.push_back( ClipAndConnection( clip, attributeValue ) );
00189                         }
00190                         else
00191                         {
00192                                 BOOST_THROW_EXCEPTION(
00193                                         ttl::exception::Value() <<
00194                                         ttl::exception::user() + "Parameter or clip name " + tuttle::quotes( attributeName ) + " not found." );
00195                         }
00196                 }
00197                 else
00198                 {
00199                         currentNode.getParam( paramIdx ).setValueFromExpression( attributeValue );
00200                 }
00201                 ++paramIdx;
00202         }
00203 }
00204 
00205 void connectClips( const std::vector<ttl::Graph::Node*> nodes, const std::vector<ClipAndConnection> clipsToConnect, ttl::Graph::Node& currentNode, ttl::Graph& graph, const std::vector<std::string> idNames )
00206 {
00207         // connect current node to previous node(s)
00208         if( nodes.size() > 0 ) // if not the first node
00209         {
00210                 if( clipsToConnect.size() == 0 )
00211                 {
00212                         // No clip connection specified, so by default
00213                         // we connect the new node to the last previous node
00214 
00215                         /// @todo We only check if we have more than one clip (assuming that it's the default "Output" clip...)
00216                         ///       instead of checking the number of input clips...
00217                         // if we have an input clip
00218                         if( currentNode.getClipImageSet().getClipsByName().size() > 1 )
00219                         {
00220                                 graph.connect( *nodes.back(), currentNode );
00221                         }
00222                 }
00223                 else
00224                 {
00225                         // The user has specified some clips to connect
00226 
00227                         BOOST_FOREACH( const ClipAndConnection& clip, clipsToConnect )
00228                         {
00229                                 //TUTTLE_TCOUT_VAR3( clip.second, currentNode.getName(), clip.first->getName() );
00230 
00231                                 if( clip.second.size() <= 1 && ( clip.second == " " || clip.second == "!" || clip.second == "/" || clip.second == "-" ) )
00232                                 {
00233                                         // these keywords allows to keep this clip unconnected
00234                                         //TUTTLE_TCOUT( "Don't connect the clip " << clip.first->getName() );
00235                                         continue;
00236                                 }
00237                                 try
00238                                 {
00239                                         //TUTTLE_TCOUT( "Connect the clip " << clip.first->getName() );
00240                                         // test if it's an index
00241                                         const int relativeIndex = std::abs( boost::lexical_cast<int>( clip.second ) );
00242                                         const int absIndex = nodes.size() - relativeIndex;
00243                                         if( absIndex < 0 || absIndex >= (int) nodes.size() )
00244                                         {
00245                                                 using namespace ttl;
00246                                                 using tuttle::quotes;
00247                                                 BOOST_THROW_EXCEPTION(
00248                                                         exception::Value() <<
00249                                                         exception::user() + "The relative index \"" + -relativeIndex + "\" for the connection of the clip " + quotes( clip.first->getName() ) + " on node " + quotes( currentNode.getName() ) + " is not valid." );
00250                                         }
00251                                         graph.connect( *nodes[absIndex], currentNode.getAttribute( clip.first->getName() ) );
00252                                 }
00253                                 catch( ... )
00254                                 {
00255                                         // It's not an index so we assume, it's the name/id of the clip.
00256                                         // If the node doesn't exist it will throw an exception.
00257                                         try
00258                                         {
00259                                                 graph.connect( graph.getNode( clip.second ), currentNode.getAttribute( clip.first->getName() ) );
00260                                         }
00261                                         catch( ... )
00262                                         {
00263                                                 using namespace ttl;
00264                                                 using tuttle::quotes;
00265                                                 
00266                                                 BOOST_THROW_EXCEPTION(
00267                                                         exception::Failed()
00268                                                         << exception::user() + "unable to connect clip " + quotes( clip.first->getName() ) + ", with the id " + quotes( clip.second ) + ". Possible id's are: " + boost::algorithm::join( idNames, ", " ) );
00269                                         }
00270                                 }
00271                         }
00272                 }
00273         }
00274 }
00275 
00276 }
00277 #endif