/* STRUCT-FACTORY-IMPL.hpp - crating structural assets (impl details) Copyright (C) Lumiera.org 2008,2010, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /** @file struct-factory-impl.hpp ** Private implementation details of creating various structural assets. ** Details how to fabricate specific kinds of structural assets, based ** on capability information encoded into a query (goal). ** @internal to be used by struct.cpp ** ** @see ConfigQuery ** */ #ifndef ASSET_STRUCT_FACTORY_IMPL_H #define ASSET_STRUCT_FACTORY_IMPL_H #include "proc/mobject/session.hpp" #include "proc/mobject/session/binding.hpp" #include "proc/mobject/session/mobjectfactory.hpp" #include "proc/mobject/session/element-query.hpp" #include "proc/mobject/session/session-query.hpp" #include "proc/mobject/session/scope.hpp" #include "common/config-rules.hpp" #include "common/query.hpp" #include "proc/asset/timeline.hpp" #include "proc/asset/sequence.hpp" #include "proc/asset/procpatt.hpp" #include "proc/asset/pipe.hpp" #include "proc/asset/struct-scheme.hpp" #include "lib/format-string.hpp" #include "lib/query-util.hpp" #include "lib/symbol.hpp" #include "lib/error.hpp" #include "lib/util.hpp" #include namespace proc { namespace asset { using lib::Symbol; using util::_Fmt; using util::uNum; using util::isnil; using util::contains; using lumiera::Query; using lumiera::query::LUMIERA_ERROR_CAPABILITY_QUERY; using lib::query::extractID; using proc::mobject::Session; using proc::mobject::MObject; using proc::mobject::session::Scope; using proc::mobject::session::match_specificFork; using proc::mobject::session::RBinding; using proc::mobject::session::ForkID; using proc::mobject::session::RFork; using proc::mobject::session::Fork; using idi::StructTraits; namespace { Symbol genericIdSymbol ("id"); Symbol seqNrPredicate ("ord"); } /** * Implementation details, especially concerning how configuration * queries are resolved and when to create new objects automatically. * @todo better use a general struct traits class, esp.for creating the Ident * @todo shouldn't some of the generic factory logic be moved over into the struct baseclass? ////////////////TICKET #565 */ class StructFactoryImpl { /** @internal derive a sensible asset ident tuple when creating * structural asset instances based on a capability query */ template const Asset::Ident createIdent (Query const& query) { // does the query somehow specify the desired name-ID? string nameID = query.extractID (genericIdSymbol); if (isnil (nameID)) nameID = query.extractID (StructTraits::idSymbol()); if (isnil (nameID)) { // no name-ID contained in the query... // so we'll create a new one static int i=0; nameID = _Fmt("%s.%d") % StructTraits::namePrefix() % (++i); } ENSURE (!isnil (nameID)); // does the query actually demand the Nth instance/element? string seqID = query.extractID (seqNrPredicate); if (!isnil (seqID) && 1 < uNum(seqID)) nameID += "."+seqID; Category cat (STRUCT, StructTraits::catFolder()); return Asset::Ident (nameID, cat ); } /** either fetch or build a suitable fork root for a new sequence */ RFork getFork_forSequence (string const& desiredID) { RFork fork; if (!isnil (desiredID)) fork = Session::current->elements.pick (match_specificFork (desiredID)); if (fork && !Scope::containing (fork.getRef()).isRoot()) { UNIMPLEMENTED ("how to deal with 'stealing' a fork sub-tree to a new sequence??"); } if (!fork) fork = Session::current->getRoot().attach (MObject::create (ForkID (desiredID))); return fork; } /** used for issuing recursive create calls to top level */ StructFactory& recursive_create_; public: StructFactoryImpl (StructFactory& interface) : recursive_create_(interface) { } /** make a new structural asset instance. * @warning default/fallback implementation just throws. * @todo a real implementation using a resolution engine. */ template STRU* fabricate (Query const& caps) { throw error::Config ("The following Query could not be resolved: " + caps.asKey() , LUMIERA_ERROR_CAPABILITY_QUERY ); } }; /* ============= specialisations =========================== */ template<> inline const ProcPatt* StructFactoryImpl::fabricate (Query const& caps) { TODO ("actually extract properties/capabilities from the query..."); return new ProcPatt (createIdent (caps)); } ///////////////////////TICKET #565 maybe store the capabilities query within the Struct asset somehow? template<> inline Pipe* StructFactoryImpl::fabricate (Query const& caps) { const Asset::Ident idi (createIdent (caps)); string streamID = caps.extractID ("stream"); if (isnil (streamID)) streamID = "default"; PProcPatt processingPattern = Session::current->defaults (Query("stream("+streamID+")")); return new Pipe( idi , streamID , processingPattern ); ///////////////////////TICKET #565 maybe store the capabilities query within the Struct asset somehow? } template<> inline Timeline* StructFactoryImpl::fabricate (Query const& caps) { TODO ("extract additional properties/capabilities from the query..."); const Asset::Ident idi (createIdent (caps)); string sequenceID = caps.extractID ("sequence"); Query desiredSequence (isnil (sequenceID)? "" : "id("+sequenceID+")"); PSequence sequence = recursive_create_(desiredSequence); ASSERT (sequence); RBinding newBinding = Session::current->getRoot().attach (MObject::create (sequence)); ASSERT (newBinding); PTimeline newTimeline = Timeline::create (idi, newBinding); ENSURE (newTimeline); ///////////////////////TICKET #565 maybe store the capabilities query within the Struct asset somehow? return newTimeline.get(); } template<> inline Sequence* StructFactoryImpl::fabricate (Query const& caps) { // when we reach this point it is clear a suitable sequence doesn't yet exist in the model TODO ("actually extract properties/capabilities from the query..."); string forkID = caps.extractID ("fork"); Query desiredFork (isnil (forkID)? "" : "id("+forkID+")"); // PFork fork = Session::current->query (desiredFork); ///////////////////////////////////TICKET #639 // TODO: handle the following cases // - fork doesn't exist --> create and attach it as root // - fork exists and is root attached, but belongs already to a sequence --> throw // - fork exists, but isn't root attached ---> what do do here? steal it?? PSequence newSequence = Sequence::create (createIdent (caps)); ///////////TODO fed fork in here ENSURE (newSequence); ///////////////////////TICKET #565 maybe store the capabilities query within the Struct asset somehow? return newSequence.get(); } }} // namespace proc::asset #endif