2016-03-18 20:03:27 +01:00
/*
TREE - MUTATOR - ATTRIBUTE - BINDING . hpp - diff : : TreeMutator implementation building block
Copyright ( C ) Lumiera . org
2016 , Hermann Vosseler < Ichthyostega @ web . de >
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 0213 9 , USA .
*/
/** @file tree-mutator-attribute-binding.hpp
* * Special binding implementation for TreeMutator , allowing to map
* * tree diff operations onto native object attributes . TreeMutator is a
* * customisable intermediary , which enables otherwise opaque implementation
* * data structures to receive and respond to generic structural change messages
* * ( " tree diff " ) .
* *
* * Each concrete TreeMutator instance will be configured differently , and this
* * adaptation is done by implementing binding templates , in the way of building
* * blocks , attached and customised through lambdas . It is possible to layer
* * several bindings on top of a single TreeMutator - - and this header defines
* * a building block for one such layer , especially for binding to object fields
* * through getter / setter lambdas .
* *
* * @ note the header tree - mutator - attribute - binding . hpp with specific builder templates
* * is included way down , after the class definitions . This is done so for sake
* * of readability .
* *
* * @ see tree - mutator - test . cpp
* * @ see TreeMutator : : build ( )
* *
*/
# ifndef LIB_DIFF_TREE_MUTATOR_ATTRIBUTE_BINDING_H
# define LIB_DIFF_TREE_MUTATOR_ATTRIBUTE_BINDING_H
# ifndef LIB_DIFF_TREE_MUTATOR_H
# error "this header shall not be used standalone (see tree-mutator.hpp)"
# endif
2016-03-18 20:52:35 +01:00
//== anonymous namespace...
2016-03-18 20:03:27 +01:00
template < class PAR , class CLO >
2016-05-27 03:33:56 +02:00
class ChangeOperation
: public PAR
2016-03-18 20:03:27 +01:00
{
2016-05-27 03:33:56 +02:00
using CloArgs = typename _ClosureType < CLO > : : Args ;
using ValueType = typename lib : : meta : : Pick < CloArgs , 0 > : : Type ;
ID attribID_ ; //////////////////////TODO ....consider to build and store a BareEntryID, to include nominal target type into the match
2016-03-18 20:03:27 +01:00
CLO change_ ;
2016-05-27 03:33:56 +02:00
/** hard wired "selector predicate" for this binding layer.
* We do only handle mutation operations pertaining attributes
* which additionally match the key defined at binding time .
* Any other operations are passed down the chain .
* @ param spec target specification given within the diff verb
* @ return if this binding is in charge for handling the spec
*/
bool
isApplicable ( GenNode const & spec )
{
return spec . isNamed ( )
and string ( attribID_ ) = = spec . idi . getSym ( ) ; /////TODO ....consider to build and store a BareEntryID
}
public :
2016-03-18 20:03:27 +01:00
virtual void
2016-05-27 03:33:56 +02:00
setAttribute ( ID id , Attribute & newValue ) ///< @deprecated about to be dropped in favour of a full binding layer
2016-03-18 20:03:27 +01:00
{
if ( id = = attribID_ )
change_ ( newValue . get < ValueType > ( ) ) ;
else // delegate to other closures (Decorator-style)
PAR : : setAttribute ( id , newValue ) ;
}
2016-03-26 02:01:31 +01:00
ChangeOperation ( ID id , CLO clo , PAR & & chain )
: PAR ( std : : forward < PAR > ( chain ) )
2016-03-18 20:03:27 +01:00
, attribID_ ( id )
, change_ ( clo )
{ }
2016-05-27 02:08:29 +02:00
/* ==== re-Implementation of the operation API ==== */
/** this binding to an object field can not support any reordering,
* inserting or deletion of " Elements " , since the structure of an object
* is fixed through the underlying class definition . For this reason ,
* we do not keep track of an " old " and " new " order ; rather there
* is always one single fixed element present to work on .
* @ return ` true ` always
*/
virtual bool
hasSrc ( ) override
{
return true ;
}
2016-05-27 03:33:56 +02:00
/** ensure the given spec is deemed appropriate at that point.
* Due to the hard wired nature of an object field binding , this can
* only be verified passively : a spec targeted at an unknown attribute
* will be rejected . But since there is no notion of " ordering " for
* ( object ) data fields , we can not verify the diff ' s completeness . */
2016-05-27 02:08:29 +02:00
virtual bool
matchSrc ( GenNode const & spec ) override
{
2016-05-27 03:33:56 +02:00
return isApplicable ( spec )
or PAR : : matchSrc ( spec ) ;
2016-05-27 02:08:29 +02:00
}
2016-05-27 03:33:56 +02:00
/** accept status quo, after verifying the spec from the diff verb */
2016-05-27 02:08:29 +02:00
virtual bool
2016-05-27 03:33:56 +02:00
acceptSrc ( GenNode const & spec ) override
2016-05-27 02:08:29 +02:00
{
2016-05-27 03:33:56 +02:00
return isApplicable ( spec ) ;
2016-05-27 02:08:29 +02:00
}
2016-05-27 03:33:56 +02:00
/** while, strictly speaking, one can not "insert" fields into a
* given class definition , this binding can tolerate an ` INS ` verb
* whenever this means to touch a field which is actually known and
* present in the class definition underlying this binding . In such
* a case , we just assign the given value . This implementation leeway
* is deliberate to support classes with optional / defaultable properties .
2016-05-27 02:08:29 +02:00
*/
2016-05-28 01:17:45 +02:00
virtual bool
2016-05-27 03:33:56 +02:00
injectNew ( GenNode const & spec ) override
2016-05-27 02:08:29 +02:00
{
2016-05-27 03:33:56 +02:00
if ( not isApplicable ( spec ) )
2016-05-28 01:17:45 +02:00
return PAR : : injectNew ( spec ) ;
change_ ( spec . data . get < ValueType > ( ) ) ;
return true ;
2016-05-27 02:08:29 +02:00
}
2016-05-27 03:33:56 +02:00
/** has no meaning, since object fields have no notion of "order".
* @ note it would be desirable to throw , but due to other restrictions
* in the " diff protocol " this operation does not get any arguments .
* Thus we ' re not able to determine , if this layer is in charge , so
* we need just to pass on the invocation .
2016-05-27 02:08:29 +02:00
*/
virtual void
skipSrc ( ) override
{
2016-05-27 03:33:56 +02:00
PAR : : skipSrc ( ) ;
2016-05-27 02:08:29 +02:00
}
2016-05-27 03:33:56 +02:00
/** any reordering or deletion of object fields is prohibited
* @ throw error : : Logic when this binding layer becomes responsible for
* handling the given diff spec , because a proper diff must be arranged
* in a way not to ask this binding to " reorder " a field from an
* existing class definition .
*/
2016-05-27 02:08:29 +02:00
virtual bool
findSrc ( GenNode const & refSpec ) override
{
2016-05-27 03:33:56 +02:00
if ( isApplicable ( refSpec ) )
throw error : : Logic ( " attempt to re-order attributes bound to object fields " ) ; //////TODO can we include _Fmt and give a precise message including the key?
else
return PAR : : findSrc ( refSpec ) ;
2016-05-27 02:08:29 +02:00
}
/** repeatedly accept, until after the designated location */
virtual bool
accept_until ( GenNode const & spec )
{
UNIMPLEMENTED ( " only support UNTIL END " ) ;
}
2016-05-27 03:33:56 +02:00
/** invoke the setter lambda, in case this binding layer is in charge */
2016-05-27 02:08:29 +02:00
virtual bool
assignElm ( GenNode const & spec )
{
2016-05-27 03:33:56 +02:00
if ( not isApplicable ( spec ) )
return PAR : : assignElm ( spec ) ;
2016-05-28 01:17:45 +02:00
change_ ( spec . data . get < ValueType > ( ) ) ;
return true ;
2016-05-27 02:08:29 +02:00
}
/** locate the designated target element and build a suitable
* sub - mutator for this element into the provided target buffer */
virtual bool
mutateChild ( GenNode const & spec , TreeMutator : : MutatorBuffer targetBuff )
{
UNIMPLEMENTED ( " invoke mutator builder " ) ;
}
/** verify all our pending (old) source elements where mentioned.
* @ note allows chained " onion-layers " to clean - up and verify . */
virtual bool
completeScope ( )
{
2016-05-27 03:33:56 +02:00
return PAR : : completeScope ( ) ;
2016-05-27 02:08:29 +02:00
}
2016-03-18 20:03:27 +01:00
} ;
# endif /*LIB_DIFF_TREE_MUTATOR_ATTRIBUTE_BINDING_H*/