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/util.hpp"
43 #include "lib/error.hpp"
44 #include "lib/symbol.hpp"
45 #include "lib/format-string.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::cStr;
61 using util::_Fmt;
62 
63 
64 namespace steam {
65 namespace control {
66  namespace error = lumiera::error;
67 
68  LUMIERA_ERROR_DEFINE (INVALID_COMMAND, "Unknown or insufficiently defined command");
69  LUMIERA_ERROR_DEFINE (DUPLICATE_COMMAND, "Attempt to redefine an already existing command definition");
70  LUMIERA_ERROR_DEFINE (INVALID_ARGUMENTS, "Arguments provided for binding doesn't match stored command function parameters");
71  LUMIERA_ERROR_DEFINE (UNBOUND_ARGUMENTS, "Command mutation functor not yet usable, because arguments aren't bound");
72  LUMIERA_ERROR_DEFINE (MISSING_MEMENTO, "Undo functor not yet usable, because no undo state has been captured");
73 
74 
75  namespace { // some common error checks...
76 
77  void
78  ___check_notBottom (const Command *handle, lib::Literal operation_descr)
79  {
80  REQUIRE (handle);
81  if (!handle->isValid())
82  throw error::Invalid (operation_descr+" an undefined command"
83  , LERR_(INVALID_COMMAND));
84  }
85 
86  void
87  ___check_isBound (const Command *handle)
88  {
89  REQUIRE (handle);
90  if (!handle->canExec())
91  throw error::State ("Lifecycle error: command arguments not bound"
92  , LERR_(UNBOUND_ARGUMENTS));
93  }
94 
95  void
96  ___check_canUndo (const Command *handle)
97  {
98  REQUIRE (handle);
99  if (!handle->canUndo())
100  throw error::State ("Lifecycle error: command has not yet captured UNDO information"
101  , LERR_(UNBOUND_ARGUMENTS));
102  }
103 
104  }
105 
106 
107 
108 
111 
112 
113  Command::~Command() { }
114  CommandImpl::~CommandImpl() { }
115 
116 
117 
118 
126  Command
128  {
129  Command cmd = CommandRegistry::instance().queryIndex (cmdID);
130  if (!cmd)
131  throw error::Invalid(_Fmt("Command \"%s\" not found") % cmdID
132  , LERR_(INVALID_COMMAND));
133 
134  ENSURE (cmdID == CommandRegistry::instance().findDefinition(cmd));
135  return cmd;
136  }
137 
138 
146  Command
148  {
149  Command cmd = CommandRegistry::instance().queryIndex (cmdID);
150  return cmd? cmd.newInstance()
151  : Command{};
152  }
153 
154 
156  Command
158  {
159  return CommandRegistry::instance().queryIndex (cmdID);
160  }
161 
162 
168  void
170  {
171  REQUIRE (implFrame);
172 
173  if (this->isValid())
174  duplicate_detected (cmdID);
175 
176  _Handle::activate (move (implFrame));
177  if (cmdID)
178  {
179  CommandRegistry::instance().track (cmdID, *this);
180  impl().cmdID = cmdID;
181  }
182 
183  TRACE (command, "%s defined OK", cStr(*this));
184  }
185 
186 
189  Command
190  Command::storeDef (Symbol newCmdID) const
191  {
193 
194  ___check_notBottom (this,"Cloning");
195  if (registry.queryIndex (newCmdID))
196  duplicate_detected (newCmdID);
197 
198  Command cloneDefinition;
199  cloneDefinition.activate (move (registry.createCloneImpl(this->impl())), newCmdID);
200  ENSURE (cloneDefinition);
201  return cloneDefinition;
202  }
203 
204 
206  Command
208  {
209  ___check_notBottom (this,"Cloning");
211  shared_ptr<CommandImpl> cloneImpl = registry.createCloneImpl(this->impl());
212 
213  Command clone;
214  clone.activate (move (cloneImpl));
215  ENSURE (clone);
216  return clone;
217  }
218 
219 
227  {
228  CommandImplCloneBuilder cloneBuilder(allocator_);
229  refObject.prepareClone(cloneBuilder);
230  return allocator_.create<CommandImpl> (refObject, cloneBuilder.clonedUndoMutation()
231  , cloneBuilder.clonedClosuere());
232  }
233 
234 
235 
236  void
237  Command::duplicate_detected (Symbol newCmdID) const
238  {
239  throw error::Logic (_Fmt("Unable to store %s as new command. "
240  "ID \"%s\" is already in use")
241  % *this
242  % newCmdID
243  , LERR_(DUPLICATE_COMMAND));
244  }
245 
246 
247  bool
248  Command::remove (Symbol cmdID)
249  {
250  return CommandRegistry::instance().remove (cmdID);
251  }
252 
253 
257  void
259  {
260  ___check_notBottom (this, "Binding arguments of");
261  _Handle::impl().setArguments(args);
262  }
263 
264 
270  void
272  {
273  ___check_notBottom (this, "Binding arguments of");
274  _Handle::impl().setArguments(paramData);
275  }
276 
277 
285  Command&
287  {
288  ___check_notBottom (this, "Un-binding arguments of");
289  _Handle::impl().discardArguments();
290  return *this;
291  }
292 
293 
295  size_t
297  {
298  return CommandRegistry::instance().index_size();
299  }
300 
301 
302 
304  size_t
306  {
307  return CommandRegistry::instance().instance_count();
308  }
309 
310 
311 
312  namespace {
313  inline bool
314  was_activated (Command const& com)
315  {
316  return com.isValid();
317  }
318 
319  inline Command
320  registered_for (Symbol id)
321  {
322  return CommandRegistry::instance().queryIndex (id);
323  }
324  }
325 
326 
334  {
335  if (!was_activated (prototype_))
336  CommandRegistry::instance().remove (this->id_);
337  }
338 
339 
346  bool
348  {
349  return (was_activated (prototype_))
350  && (prototype_ == registered_for (this->id_))
351  ;
352  }
353 
354 
355 
356  bool
357  Command::canExec() const
358  {
359  return isValid()
360  && impl().canExec();
361  }
362 
363 
364  bool
365  Command::canUndo() const
366  {
367  return isValid()
368  && impl().canUndo();
369  }
370 
371 
372  Symbol
373  Command::getID() const noexcept
374  {
375  return isValid()? impl().cmdID
376  : Symbol::FAILURE;
377  }
378 
379 
385  bool
387  {
388  return not CommandRegistry::instance().findDefinition (*this);
389  }
390 
391 
392 
393 
396  Command::operator string() const
397  {
398  ostringstream repr;
400  repr << "Command(\""<<getID()<<"\") ";
401  if (!isValid())
402  repr << "NIL";
403  else
404  if (canUndo())
405  repr << "{undo}";
406  else
407  if (canExec())
408  repr << "{exec}";
409  else
410  repr << "{def}";
411 
412  return repr.str();
413  }
414 
415 
416 
417 
418  ExecResult
419  Command::exec (HandlingPattern const& execPattern)
420  {
421  ___check_notBottom (this,"Invoking");
422  ___check_isBound (this);
423 
424  string cmdName{*this};
425  CommandImpl& thisCommand (_Handle::impl());
426  return execPattern.exec (thisCommand, cmdName);
427  }
428 
429 
430  ExecResult
431  Command::undo (HandlingPattern const& execPattern)
432  {
433  ___check_notBottom (this,"UNDOing");
434  ___check_canUndo (this);
435 
436  string cmdName{*this};
437  CommandImpl& thisCommand (_Handle::impl());
438  return execPattern.undo (thisCommand, cmdName);
439  }
440 
441 
442  ExecResult
443  Command::exec (HandlingPattern::ID pattID)
444  {
445  return exec (HandlingPattern::get(pattID));
446  }
447 
448 
449  ExecResult
450  Command::undo (HandlingPattern::ID pattID)
451  {
452  return undo (HandlingPattern::get(pattID));
453  }
454 
455 
456  ExecResult
458  {
459  return exec (HandlingPattern::get(HandlingPattern::DUMMY));
460  }
461 
462 
463  HandlingPattern::ID
465  {
466  ___check_notBottom (this,"Accessing");
467  return impl().getDefaultHandlingPattern();
468  }
469 
470 
471  HandlingPattern::ID
472  Command::setHandlingPattern (HandlingPattern::ID pattID)
473  {
474  ___check_notBottom (this, "Configuring");
475  return impl().setHandlingPattern(pattID);
476  }
477 
478 
479 
480 
481 
482 }} // 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
HandlingPattern::ID setHandlingPattern(HandlingPattern::ID)
define a handling pattern to be used by default
Definition: command.cpp:472
static size_t definition_count()
Definition: command.cpp:296
ExecResult exec(CommandImpl &command, string) const
main functionality: invoke a command, detect errors.
~CommandDef()
when starting a CommandDef, we immediately place a yet empty Command object into the index...
Definition: command.cpp:333
Top level of the command implementation.
inline string literal This is a marker type to indicate that
Definition: symbol.hpp:75
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:169
bool isValid() const
is this a valid command definition? especially.
Definition: command.cpp:347
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:207
const char * cStr(string const &org)
convenience shortcut: conversion to c-String via string.
Definition: util.hpp:423
static Command get(Symbol cmdID)
Access existing command for use.
Definition: command.cpp:127
Command storeDef(Symbol newCmdID) const
create a clone definition
Definition: command.cpp:190
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
static Command maybeGetNewInstance(Symbol cmdID)
try to access an existing command definition and immediately create a new clone copy by calling newIn...
Definition: command.cpp:147
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:196
static size_t instance_count()
Definition: command.cpp:305
Token or Atom with distinct identity.
Definition: symbol.hpp:116
Command queryIndex(Symbol cmdID)
query the command index by ID
Marker types to indicate a literal string and a Symbol.
Command & unbind()
discard any argument data previously bound.
Definition: command.cpp:286
Result (Status) of command execution.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
void setArguments(Arguments &)
Definition: command.cpp:258
Steam-Layer command frontend.
Visitor to support creating a CommandImpl clone.
Pre-defined command execution skeletons.
static Command fetchDef(Symbol cmdID)
Definition: command.cpp:157
Lumiera error handling (C++ interface).
Helper for creating an implementation clone, based on the visitor pattern.
ExecResult execSync()
invoke using a default "synchronous" execution pattern
Definition: command.cpp:457
Handle object representing a single Command instance to be used by client code.
Definition: command.hpp:125
shared_ptr< CommandImpl > createCloneImpl(CommandImpl const &refObject)
create an allocation for holding a clone of the given CommandImpl data.
Definition: command.cpp:226
HandlingPattern::ID getDefaultHandlingPattern() const
Definition: command.cpp:464
Steam-Layer Command implementation.
static HandlingPattern const & get(ID id)
retrieve the pre-configured pattern
bool isAnonymous() const
Definition: command.cpp:386
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.
static lib::Depend< CommandRegistry > instance
storage for the singleton factory used to access CommandRegistry
#define LUMIERA_ERROR_DEFINE(err, msg)
Definition and initialisation of an error constant.
Definition: error.h:80