OpenWalnut  1.4.0
WStrategyHelper.h
00001 //---------------------------------------------------------------------------
00002 //
00003 // Project: OpenWalnut ( http://www.openwalnut.org )
00004 //
00005 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
00006 // For more information see http://www.openwalnut.org/copying
00007 //
00008 // This file is part of OpenWalnut.
00009 //
00010 // OpenWalnut is free software: you can redistribute it and/or modify
00011 // it under the terms of the GNU Lesser General Public License as published by
00012 // the Free Software Foundation, either version 3 of the License, or
00013 // (at your option) any later version.
00014 //
00015 // OpenWalnut is distributed in the hope that it will be useful,
00016 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 // GNU Lesser General Public License for more details.
00019 //
00020 // You should have received a copy of the GNU Lesser General Public License
00021 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
00022 //
00023 //---------------------------------------------------------------------------
00024 
00025 #ifndef WSTRATEGYHELPER_H
00026 #define WSTRATEGYHELPER_H
00027 
00028 #include <string>
00029 #include <vector>
00030 
00031 #include <boost/shared_ptr.hpp>
00032 #include <boost/thread.hpp>
00033 
00034 #include "WLogger.h"
00035 #include "WItemSelection.h"
00036 #include "WItemSelector.h"
00037 #include "WSharedSequenceContainer.h"
00038 #include "WProperties.h"
00039 #include "WPropertyHelper.h"
00040 
00041 /**
00042  * This class allows for an easy strategy pattern-based switching between properties and strategy instances using a WPropSelection. The idea of
00043  * this class is that you specify the type of some class you want to serve as the base class of several strategies. Each of these strategies has
00044  * a name, description and several properties. An instance of this class automatically provides a WPropSelection containing an item for each strategy
00045  * instance you add. A switch in this property causes to automatically hide all properties not belonging to this strategy. This class will ease
00046  * the writing of modules that provide multiple "ways of doing it". If you utilize this class in your module, you should add ALL your strategies
00047  * before you add this WStrategyHelper's properties to your module's properties.
00048  *
00049  * \tparam StrategyType the base class type of your strategies.
00050  *
00051  * The type in StrategyType needs to comply to the following rules:
00052  * <ul>
00053  *  <li> provide a typedef SPtr, representing the pointer type of this class. Usually, this is a boost::shared_ptr
00054  *  <li> provide a typedef ConstSPtr, representing the pointer type of this class. Usually, this is a boost::shared_ptr< const >
00055  *  <li> provide a method std::string getName() const
00056  *  <li> provide a method std::string getDescription() const
00057  *  <li> provide a method const char** getXPMIcon() const
00058  *  <li> provide a method WProperties::SPtr getProperties() const;
00059  * </ul>
00060  * The cool thing is, this class complies to its own requirements on strategy base classes. This allows you to nest strategy selections.
00061  *
00062  * \note the class is thread-safe, although it might not be a nice idea to modify the strategy list while the user tries to select some. He will
00063  * probably be very annoyed.
00064  *
00065  * \note you should use \ref WObjectNDIP which complies to this rules. This furthermore eases the task of writing strategies.
00066  */
00067 template< class StrategyType >
00068 class WStrategyHelper
00069 {
00070 public:
00071     /**
00072      * Convenience typedef for a boost::shared_ptr< WStrategyHelper >.
00073      */
00074     typedef boost::shared_ptr< WStrategyHelper > SPtr;
00075 
00076     /**
00077      * Convenience typedef for a boost::shared_ptr< const WStrategyHelper >.
00078      */
00079     typedef boost::shared_ptr< const WStrategyHelper > ConstSPtr;
00080 
00081     /**
00082      * Constructs an empty strategy selector. Use one of the addStrategy methods to register strategies.
00083      *
00084      * \param name name of this strategy selector
00085      * \param description a description for this selection
00086      * \param icon an icon for this selection. Can be NULL.
00087      * \param selectorName the name of the selection property used to switch. If empty, the name of the WStrategyHelper will be used.
00088      * \param selectorDescription the description of the selection property used to switch. If empty, description of the WStrategyHelper is used.
00089      */
00090     WStrategyHelper( std::string name, std::string description, const char** icon = NULL, std::string selectorName = std::string(),
00091                                                                                           std::string selectorDescription = std::string() );
00092 
00093     /**
00094      * Destructor.
00095      */
00096     ~WStrategyHelper();
00097 
00098     /**
00099      * Gets the name of this strategy selector.
00100      *
00101      * \return the name.
00102      */
00103     std::string getName() const;
00104 
00105     /**
00106      * Gets the description for this strategy selector.
00107      *
00108      * \return the description
00109      */
00110     std::string getDescription() const;
00111 
00112     /**
00113      * Get the icon for this strategy selectior in XPM format.
00114      *
00115      * \return The icon.
00116      */
00117     const char** getXPMIcon() const;
00118 
00119     /**
00120      * Get this strategy selectors properties. This group contains the WPropSelection property to switch the strategy as well as groups for all
00121      * registered strategies.
00122      *
00123      * \return properties
00124      */
00125     WProperties::SPtr getProperties() const;
00126 
00127     /**
00128      * Adds the given strategy to the list of all strategies.
00129      *
00130      * \param strategy the strategy to add.
00131      */
00132     void addStrategy( typename StrategyType::SPtr strategy );
00133 
00134     /**
00135      * Return the currently active strategy.
00136      *
00137      * \return the active strategy
00138      */
00139     typename StrategyType::ConstSPtr operator()() const;
00140 
00141     /**
00142      * Return the currently active strategy.
00143      *
00144      * \return the active strategy
00145      */
00146     typename StrategyType::SPtr operator()();
00147 
00148 protected:
00149 private:
00150     const char** m_icon; //!< the icon pointer
00151     WProperties::SPtr m_properties; //!< stores the selection property and the strategy property groups
00152 
00153     /**
00154      * A list of items that can be selected. Will be extended for each added strategy.
00155      */
00156     WItemSelection::SPtr m_possibleSelections;
00157 
00158     /**
00159      * The property allowing the user to switch the strategy. Will be extended for each added strategy.
00160      */
00161     WPropSelection m_possibleSelectionProp;
00162 
00163     /**
00164      * The type used to securely manage the strategies
00165      */
00166     typedef WSharedSequenceContainer< std::vector< typename StrategyType::SPtr > > ContainerType;
00167 
00168     /**
00169      * This is the list of all strategies
00170      */
00171     ContainerType m_strategies;
00172 
00173     /**
00174      * This lock is needed to protect the addStrategy function. Although the m_strategies member is protected due to the use of a WSharedObject,
00175      * an update in the selection  (m_possibleSelectionProp) causes an update of the hide status of all property groups in m_strategies. This
00176      * would cause a deadlock if m_strategies is still locked. This lock is only locked if addStrategy is  called.
00177      */
00178     boost::mutex m_addLock;
00179 
00180     /**
00181      * Connection between \ref update and the update condition of  \ref m_possibleSelectionProp.
00182      */
00183     boost::signals2::connection m_updateConnection;
00184 
00185     /**
00186      * Update strategy's property hide status on updates in \ref m_possibleSelectionProp.
00187      */
00188     void update();
00189 };
00190 
00191 template< typename StrategyType >
00192 WStrategyHelper< StrategyType >::WStrategyHelper( std::string name, std::string description, const char** icon,
00193                                                   std::string selectorName, std::string selectorDescription ):
00194     m_icon( icon ),
00195     m_properties( new WProperties( name, description ) ),
00196     m_possibleSelections( new WItemSelection() )
00197 {
00198     // Create the main selector property:
00199     selectorName = selectorName.empty() ? name : selectorName;
00200     selectorDescription = selectorDescription.empty() ? name : selectorDescription;
00201     m_possibleSelectionProp = m_properties->addProperty( selectorName, selectorDescription, m_possibleSelections->getSelectorNone() );
00202     WPropertyHelper::PC_SELECTONLYONE::addTo( m_possibleSelectionProp );
00203     WPropertyHelper::PC_NOTEMPTY::addTo( m_possibleSelectionProp );
00204 
00205     // if the selection changes, we want to hide all not selected strategy groups. So we register a change callback
00206     m_updateConnection = m_possibleSelectionProp->getUpdateCondition()->subscribeSignal(
00207         boost::bind( &WStrategyHelper< StrategyType >::update, this )
00208     );
00209 }
00210 
00211 template< typename StrategyType >
00212 WStrategyHelper< StrategyType >::~WStrategyHelper()
00213 {
00214     // cleanup
00215 }
00216 
00217 template< typename StrategyType >
00218 void WStrategyHelper< StrategyType >::update()
00219 {
00220     // get lock
00221     typename ContainerType::WriteTicket w = m_strategies.getWriteTicket();
00222 
00223     // update each hide state
00224     size_t currentID = 0;
00225     size_t selectedID = m_possibleSelectionProp->get();
00226 
00227     for( typename ContainerType::Iterator i = w->get().begin(); i != w->get().end(); ++i )
00228     {
00229         ( *i )->getProperties()->setHidden( currentID != selectedID );
00230         currentID++;
00231     }
00232     // w unlocks automatically
00233 }
00234 
00235 template< typename StrategyType >
00236 std::string WStrategyHelper< StrategyType >::getName() const
00237 {
00238     return m_properties->getName();
00239 }
00240 
00241 template< typename StrategyType >
00242 std::string WStrategyHelper< StrategyType >::getDescription() const
00243 {
00244     return m_properties->getDescription();
00245 }
00246 
00247 template< typename StrategyType >
00248 const char** WStrategyHelper< StrategyType >::getXPMIcon() const
00249 {
00250     return m_icon;
00251 }
00252 
00253 template< typename StrategyType >
00254 WProperties::SPtr WStrategyHelper< StrategyType >::getProperties() const
00255 {
00256     return m_properties;
00257 }
00258 
00259 template< typename StrategyType >
00260 void WStrategyHelper< StrategyType >::addStrategy( typename StrategyType::SPtr strategy )
00261 {
00262     // lock this method.
00263     boost::lock_guard< boost::mutex > lock( m_addLock );
00264 
00265     // add strategy to list of strategies
00266     typename ContainerType::WriteTicket w = m_strategies.getWriteTicket();
00267     w->get().push_back( strategy );
00268     size_t size = w->get().size();
00269 
00270     // add strategy to selector:
00271     m_possibleSelections->addItem( strategy->getName(), strategy->getDescription(), strategy->getIcon() );
00272     m_properties->addProperty( strategy->getProperties() );
00273 
00274     // we can safely unlock m_strategies now. This is needed since an update in m_possibleSelectionProp requests a read lock and will deadlock if
00275     // w was not unlocked.
00276     w.reset();
00277 
00278     // the first strategy. Select it. If this somehow changes the selection, the update mechanism ensures proper hide/unhide on all property
00279     // groups.
00280     if( size == 1 )
00281     {
00282         m_possibleSelectionProp->set( m_possibleSelections->getSelectorFirst() );
00283     }
00284     else
00285     {
00286         m_possibleSelectionProp->set( m_possibleSelectionProp->get().newSelector() );
00287     }
00288 
00289     // lock unlocked automatically
00290 }
00291 
00292 template< typename StrategyType >
00293 typename StrategyType::ConstSPtr WStrategyHelper< StrategyType >::operator()() const
00294 {
00295     // get lock
00296     typename ContainerType::ReadTicket r = m_strategies.getReadTicket();
00297     return r->get()[ m_possibleSelectionProp->get() ];
00298     // r unlocks automatically
00299 }
00300 
00301 template< typename StrategyType >
00302 typename StrategyType::SPtr WStrategyHelper< StrategyType >::operator()()
00303 {
00304     // get lock
00305     typename ContainerType::WriteTicket w = m_strategies.getWriteTicket();
00306     return w->get()[ m_possibleSelectionProp->get() ];
00307     // w unlocks automatically
00308 }
00309 
00310 #endif  // WSTRATEGYHELPER_H
00311