Lumiera  0.pre.03
»edit your freedom«
command.cpp
Go to the documentation of this file.
1 /*
2  Command - Key abstraction for steam/edit operations and UNDO management
3 
4  Copyright (C) Lumiera.org
5  2009, Hermann Vosseler <Ichthyostega@web.de>
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of
10  the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 * *****************************************************/
22 
23 
42 #include "lib/error.hpp"
43 #include "lib/symbol.hpp"
44 #include "lib/format-string.hpp"
51 #include "lib/util.hpp"
52 
53 #include <utility>
54 #include <sstream>
55 #include <string>
56 
57 using std::ostringstream;
58 using std::string;
59 using std::move;
60 using util::_Fmt;
61 
62 
63 namespace lumiera {
64 namespace error {
65  LUMIERA_ERROR_DEFINE (INVALID_COMMAND, "Unknown or insufficiently defined command");
66  LUMIERA_ERROR_DEFINE (DUPLICATE_COMMAND, "Attempt to redefine an already existing command definition");
67  LUMIERA_ERROR_DEFINE (INVALID_ARGUMENTS, "Arguments provided for binding doesn't match stored command function parameters");
68  LUMIERA_ERROR_DEFINE (UNBOUND_ARGUMENTS, "Command mutation functor not yet usable, because arguments aren't bound");
69  LUMIERA_ERROR_DEFINE (MISSING_MEMENTO, "Undo functor not yet usable, because no undo state has been captured");
70 }}
71 
72 namespace steam {
73 namespace control {
74  namespace error = lumiera::error;
75 
76 
77  namespace { // some common error checks...
78 
79  void
80  ___check_notBottom (const Command *handle, lib::Literal operation_descr)
81  {
82  REQUIRE (handle);
83  if (!handle->isValid())
84  throw error::Invalid (operation_descr+" an undefined command"
85  , LERR_(INVALID_COMMAND));
86  }
87 
88  void
89  ___check_isBound (const Command *handle)
90  {
91  REQUIRE (handle);
92  if (!handle->canExec())
93  throw error::State ("Lifecycle error: command arguments not bound"
94  , LERR_(UNBOUND_ARGUMENTS));
95  }
96 
97  void
98  ___check_canUndo (const Command *handle)
99  {
100  REQUIRE (handle);
101  if (!handle->canUndo())
102  throw error::State ("Lifecycle error: command has not yet captured UNDO information"
103  , LERR_(UNBOUND_ARGUMENTS));
104  }
105 
106  }
107 
108 
109 
110 
112  lib::Depend<CommandRegistry> CommandRegistry::instance;
113 
114 
115  Command::~Command() { }
116  CommandImpl::~CommandImpl() { }
117 
118 
119 
120 
128  Command
129  Command::get (Symbol cmdID)
130  {
131  Command cmd = CommandRegistry::instance().queryIndex (cmdID);
132  if (!cmd)
133  throw error::Invalid(_Fmt("Command \"%s\" not found") % cmdID
134  , LERR_(INVALID_COMMAND));
135 
136  ENSURE (cmdID == CommandRegistry::instance().findDefinition(cmd));
137  return cmd;
138  }
139 
140 
148  Command
149  Command::maybeGetNewInstance (Symbol cmdID)
150  {
151  Command cmd = CommandRegistry::instance().queryIndex (cmdID);
152  return cmd? cmd.newInstance()
153  : Command{};
154  }
155 
156 
158  Command
159  Command::fetchDef (Symbol cmdID)
160  {
161  return CommandRegistry::instance().queryIndex (cmdID);
162  }
163 
164 
170  void
171  Command::activate (shared_ptr<CommandImpl> && implFrame, Symbol cmdID)
172  {
173  REQUIRE (implFrame);
174 
175  if (this->isValid())
176  duplicate_detected (cmdID);
177 
178  _Handle::activate (move (implFrame));
179  if (cmdID)
180  {
181  CommandRegistry::instance().track (cmdID, *this);
182  impl().cmdID = cmdID;
183  }
184 
185  TRACE (command, "%s defined OK", cStr(*this));
186  }
187 
188 
191  Command
192  Command::storeDef (Symbol newCmdID) const
193  {
194  CommandRegistry& registry = CommandRegistry::instance();
195 
196  ___check_notBottom (this,"Cloning");
197  if (registry.queryIndex (newCmdID))
198  duplicate_detected (newCmdID);
199 
200  Command cloneDefinition;
201  cloneDefinition.activate (move (registry.createCloneImpl(this->impl())), newCmdID);
202  ENSURE (cloneDefinition);
203  return cloneDefinition;
204  }
205 
206 
208  Command
209  Command::newInstance () const
210  {
211  ___check_notBottom (this,"Cloning");
212  CommandRegistry& registry = CommandRegistry::instance();
213  shared_ptr<CommandImpl> cloneImpl = registry.createCloneImpl(this->impl());
214 
215  Command clone;
216  clone.activate (move (cloneImpl));
217  ENSURE (clone);
218  return clone;
219  }
220 
221 
228  CommandRegistry::createCloneImpl (CommandImpl const& refObject)
229  {
230  CommandImplCloneBuilder cloneBuilder(allocator_);
231  refObject.prepareClone(cloneBuilder);
232  return allocator_.create<CommandImpl> (refObject, cloneBuilder.clonedUndoMutation()
233  , cloneBuilder.clonedClosuere());
234  }
235 
236 
237 
238  void
239  Command::duplicate_detected (Symbol newCmdID) const
240  {
241  throw error::Logic (_Fmt("Unable to store %s as new command. "
242  "ID \"%s\" is already in use")
243  % *this
244  % newCmdID
245  , LERR_(DUPLICATE_COMMAND));
246  }
247 
248 
249  bool
250  Command::remove (Symbol cmdID)
251  {
252  return CommandRegistry::instance().remove (cmdID);
253  }
254 
255 
259  void
260  Command::setArguments (Arguments& args)
261  {
262  ___check_notBottom (this, "Binding arguments of");
263  _Handle::impl().setArguments(args);
264  }
265 
266 
272  void
273  Command::setArguments (lib::diff::Rec const& paramData)
274  {
275  ___check_notBottom (this, "Binding arguments of");
276  _Handle::impl().setArguments(paramData);
277  }
278 
279 
287  Command&
288  Command::unbind()
289  {
290  ___check_notBottom (this, "Un-binding arguments of");
291  _Handle::impl().discardArguments();
292  return *this;
293  }
294 
295 
297  size_t
298  Command::definition_count()
299  {
300  return CommandRegistry::instance().index_size();
301  }
302 
303 
304 
306  size_t
307  Command::instance_count()
308  {
309  return CommandRegistry::instance().instance_count();
310  }
311 
312 
313 
314  namespace {
315  inline bool
316  was_activated (Command const& com)
317  {
318  return com.isValid();
319  }
320 
321  inline Command
322  registered_for (Symbol id)
323  {
324  return CommandRegistry::instance().queryIndex (id);
325  }
326  }
327 
328 
335  CommandDef::~CommandDef()
336  {
337  if (!was_activated (prototype_))
338  CommandRegistry::instance().remove (this->id_);
339  }
340 
341 
348  bool
349  CommandDef::isValid() const
350  {
351  return (was_activated (prototype_))
352  && (prototype_ == registered_for (this->id_))
353  ;
354  }
355 
356 
357 
358  bool
359  Command::canExec() const
360  {
361  return isValid()
362  && impl().canExec();
363  }
364 
365 
366  bool
367  Command::canUndo() const
368  {
369  return isValid()
370  && impl().canUndo();
371  }
372 
373 
374  Symbol
375  Command::getID() const noexcept
376  {
377  return isValid()? impl().cmdID
378  : Symbol::FAILURE;
379  }
380 
381 
387  bool
388  Command::isAnonymous() const
389  {
390  return not CommandRegistry::instance().findDefinition (*this);
391  }
392 
393 
394 
395 
398  Command::operator string() const
399  {
400  ostringstream repr;
402  repr << "Command(\""<<getID()<<"\") ";
403  if (!isValid())
404  repr << "NIL";
405  else
406  if (canUndo())
407  repr << "{undo}";
408  else
409  if (canExec())
410  repr << "{exec}";
411  else
412  repr << "{def}";
413 
414  return repr.str();
415  }
416 
417 
418 
419 
420  ExecResult
421  Command::exec (HandlingPattern const& execPattern)
422  {
423  ___check_notBottom (this,"Invoking");
424  ___check_isBound (this);
425 
426  string cmdName{*this};
427  CommandImpl& thisCommand (_Handle::impl());
428  return execPattern.exec (thisCommand, cmdName);
429  }
430 
431 
432  ExecResult
433  Command::undo (HandlingPattern const& execPattern)
434  {
435  ___check_notBottom (this,"UNDOing");
436  ___check_canUndo (this);
437 
438  string cmdName{*this};
439  CommandImpl& thisCommand (_Handle::impl());
440  return execPattern.undo (thisCommand, cmdName);
441  }
442 
443 
444  ExecResult
445  Command::exec (HandlingPattern::ID pattID)
446  {
447  return exec (HandlingPattern::get(pattID));
448  }
449 
450 
451  ExecResult
452  Command::undo (HandlingPattern::ID pattID)
453  {
454  return undo (HandlingPattern::get(pattID));
455  }
456 
457 
458  ExecResult
459  Command::execSync ()
460  {
461  return exec (HandlingPattern::get(HandlingPattern::DUMMY));
462  }
463 
464 
465  HandlingPattern::ID
466  Command::getDefaultHandlingPattern() const
467  {
468  ___check_notBottom (this,"Accessing");
469  return impl().getDefaultHandlingPattern();
470  }
471 
472 
473  HandlingPattern::ID
474  Command::setHandlingPattern (HandlingPattern::ID pattID)
475  {
476  ___check_notBottom (this, "Configuring");
477  return impl().setHandlingPattern(pattID);
478  }
479 
480 
481 
482 
483 
484 }} // namespace steam::control
ExecResult undo(CommandImpl &command, string) const
likewise invoke the configured UNDO operation
UndoMutation const & clonedUndoMutation()
after visitation: use pre-built bits to provide a cloned UndoFunctor
CStr cStr(std::string const &rendered)
convenience shortcut: forced conversion to c-String via string.
Definition: symbol.hpp:68
ExecResult exec(CommandImpl &command, string) const
main functionality: invoke a command, detect errors.
Top level of the command implementation.
inline string literal This is a marker type to indicate that
Definition: symbol.hpp:85
PClo const & clonedClosuere()
after visitation: provide cloned StorageHolder, but already stripped down to the generic usage type ...
void activate(shared_ptr< CommandImpl > &&, Symbol cmdID=0)
Definition: command.cpp:171
Front-end for printf-style string template interpolation.
Managing command definitions and the storage of individual command objects.
void prepareClone(CommandImplCloneBuilder &visitor) const
assist with building a clone copy of this CommandImpl.
Command newInstance() const
create independent (anonymous) clone copy of this command
Definition: command.cpp:209
Registry managing command implementation objects (Singleton).
Steam-Layer implementation namespace root.
A front-end for using printf-style formatting.
Access point to singletons and other kinds of dependencies designated by type.
Definition: depend.hpp:289
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:199
Token or Atom with distinct identity.
Definition: symbol.hpp:126
Command queryIndex(Symbol cmdID)
query the command index by ID
Marker types to indicate a literal string and a Symbol.
Result (Status) of command execution.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
Steam-Layer command frontend.
Visitor to support creating a CommandImpl clone.
Pre-defined command execution skeletons.
Lumiera error handling (C++ interface).
Helper for creating an implementation clone, based on the visitor pattern.
Handle object representing a single Command instance to be used by client code.
Definition: command.hpp:124
Lumiera public interface.
Definition: advice.cpp:113
shared_ptr< CommandImpl > createCloneImpl(CommandImpl const &refObject)
create an allocation for holding a clone of the given CommandImpl data.
Definition: command.cpp:228
Steam-Layer Command implementation.
Actually defining a command and binding it to execution parameters.
object-like record of data.
Definition: record.hpp:150
Interface: Operation Skeleton how to invoke or undo a command.
#define LUMIERA_ERROR_DEFINE(err, msg)
Definition and initialisation of an error constant.
Definition: error.h:80