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)
5  2009, Hermann Vosseler <Ichthyostega@web.de>
6 
7   **Lumiera** is free software; you can redistribute it and/or modify it
8   under the terms of the GNU General Public License as published by the
9   Free Software Foundation; either version 2 of the License, or (at your
10   option) any later version. See the file COPYING for further details.
11 
12 * *****************************************************************/
13 
14 
33 #include "lib/error.hpp"
34 #include "lib/symbol.hpp"
35 #include "lib/format-string.hpp"
42 #include "lib/util.hpp"
43 
44 #include <utility>
45 #include <sstream>
46 #include <string>
47 
48 using std::ostringstream;
49 using std::string;
50 using std::move;
51 using util::_Fmt;
52 
53 
54 namespace lumiera {
55 namespace error {
56  LUMIERA_ERROR_DEFINE (INVALID_COMMAND, "Unknown or insufficiently defined command");
57  LUMIERA_ERROR_DEFINE (DUPLICATE_COMMAND, "Attempt to redefine an already existing command definition");
58  LUMIERA_ERROR_DEFINE (INVALID_ARGUMENTS, "Arguments provided for binding doesn't match stored command function parameters");
59  LUMIERA_ERROR_DEFINE (UNBOUND_ARGUMENTS, "Command mutation functor not yet usable, because arguments aren't bound");
60  LUMIERA_ERROR_DEFINE (MISSING_MEMENTO, "Undo functor not yet usable, because no undo state has been captured");
61 }}
62 
63 namespace steam {
64 namespace control {
65  namespace error = lumiera::error;
66 
67 
68  namespace { // some common error checks...
69 
70  void
71  ___check_notBottom (const Command *handle, lib::Literal operation_descr)
72  {
73  REQUIRE (handle);
74  if (!handle->isValid())
75  throw error::Invalid (operation_descr+" an undefined command"
76  , LERR_(INVALID_COMMAND));
77  }
78 
79  void
80  ___check_isBound (const Command *handle)
81  {
82  REQUIRE (handle);
83  if (!handle->canExec())
84  throw error::State ("Lifecycle error: command arguments not bound"
85  , LERR_(UNBOUND_ARGUMENTS));
86  }
87 
88  void
89  ___check_canUndo (const Command *handle)
90  {
91  REQUIRE (handle);
92  if (!handle->canUndo())
93  throw error::State ("Lifecycle error: command has not yet captured UNDO information"
94  , LERR_(UNBOUND_ARGUMENTS));
95  }
96 
97  }
98 
99 
100 
101 
103  lib::Depend<CommandRegistry> CommandRegistry::instance;
104 
105 
106  Command::~Command() { }
107  CommandImpl::~CommandImpl() { }
108 
109 
110 
111 
119  Command
120  Command::get (Symbol cmdID)
121  {
122  Command cmd = CommandRegistry::instance().queryIndex (cmdID);
123  if (!cmd)
124  throw error::Invalid(_Fmt("Command \"%s\" not found") % cmdID
125  , LERR_(INVALID_COMMAND));
126 
127  ENSURE (cmdID == CommandRegistry::instance().findDefinition(cmd));
128  return cmd;
129  }
130 
131 
139  Command
140  Command::maybeGetNewInstance (Symbol cmdID)
141  {
142  Command cmd = CommandRegistry::instance().queryIndex (cmdID);
143  return cmd? cmd.newInstance()
144  : Command{};
145  }
146 
147 
149  Command
150  Command::fetchDef (Symbol cmdID)
151  {
152  return CommandRegistry::instance().queryIndex (cmdID);
153  }
154 
155 
161  void
162  Command::activate (shared_ptr<CommandImpl> && implFrame, Symbol cmdID)
163  {
164  REQUIRE (implFrame);
165 
166  if (this->isValid())
167  duplicate_detected (cmdID);
168 
169  _Handle::activate (move (implFrame));
170  if (cmdID)
171  {
172  CommandRegistry::instance().track (cmdID, *this);
173  impl().cmdID = cmdID;
174  }
175 
176  TRACE (command, "%s defined OK", cStr(*this));
177  }
178 
179 
182  Command
183  Command::storeDef (Symbol newCmdID) const
184  {
185  CommandRegistry& registry = CommandRegistry::instance();
186 
187  ___check_notBottom (this,"Cloning");
188  if (registry.queryIndex (newCmdID))
189  duplicate_detected (newCmdID);
190 
191  Command cloneDefinition;
192  cloneDefinition.activate (move (registry.createCloneImpl(this->impl())), newCmdID);
193  ENSURE (cloneDefinition);
194  return cloneDefinition;
195  }
196 
197 
199  Command
200  Command::newInstance () const
201  {
202  ___check_notBottom (this,"Cloning");
203  CommandRegistry& registry = CommandRegistry::instance();
204  shared_ptr<CommandImpl> cloneImpl = registry.createCloneImpl(this->impl());
205 
206  Command clone;
207  clone.activate (move (cloneImpl));
208  ENSURE (clone);
209  return clone;
210  }
211 
212 
219  CommandRegistry::createCloneImpl (CommandImpl const& refObject)
220  {
221  CommandImplCloneBuilder cloneBuilder(allocator_);
222  refObject.prepareClone(cloneBuilder);
223  return allocator_.create<CommandImpl> (refObject, cloneBuilder.clonedUndoMutation()
224  , cloneBuilder.clonedClosuere());
225  }
226 
227 
228 
229  void
230  Command::duplicate_detected (Symbol newCmdID) const
231  {
232  throw error::Logic (_Fmt("Unable to store %s as new command. "
233  "ID \"%s\" is already in use")
234  % *this
235  % newCmdID
236  , LERR_(DUPLICATE_COMMAND));
237  }
238 
239 
240  bool
241  Command::remove (Symbol cmdID)
242  {
243  return CommandRegistry::instance().remove (cmdID);
244  }
245 
246 
250  void
251  Command::setArguments (Arguments& args)
252  {
253  ___check_notBottom (this, "Binding arguments of");
254  _Handle::impl().setArguments(args);
255  }
256 
257 
263  void
264  Command::setArguments (lib::diff::Rec const& paramData)
265  {
266  ___check_notBottom (this, "Binding arguments of");
267  _Handle::impl().setArguments(paramData);
268  }
269 
270 
278  Command&
279  Command::unbind()
280  {
281  ___check_notBottom (this, "Un-binding arguments of");
282  _Handle::impl().discardArguments();
283  return *this;
284  }
285 
286 
288  size_t
289  Command::definition_count()
290  {
291  return CommandRegistry::instance().index_size();
292  }
293 
294 
295 
297  size_t
298  Command::instance_count()
299  {
300  return CommandRegistry::instance().instance_count();
301  }
302 
303 
304 
305  namespace {
306  inline bool
307  was_activated (Command const& com)
308  {
309  return com.isValid();
310  }
311 
312  inline Command
313  registered_for (Symbol id)
314  {
315  return CommandRegistry::instance().queryIndex (id);
316  }
317  }
318 
319 
326  CommandDef::~CommandDef()
327  {
328  if (!was_activated (prototype_))
329  CommandRegistry::instance().remove (this->id_);
330  }
331 
332 
339  bool
340  CommandDef::isValid() const
341  {
342  return (was_activated (prototype_))
343  && (prototype_ == registered_for (this->id_))
344  ;
345  }
346 
347 
348 
349  bool
350  Command::canExec() const
351  {
352  return isValid()
353  && impl().canExec();
354  }
355 
356 
357  bool
358  Command::canUndo() const
359  {
360  return isValid()
361  && impl().canUndo();
362  }
363 
364 
365  Symbol
366  Command::getID() const noexcept
367  {
368  return isValid()? impl().cmdID
369  : Symbol::FAILURE;
370  }
371 
372 
378  bool
379  Command::isAnonymous() const
380  {
381  return not CommandRegistry::instance().findDefinition (*this);
382  }
383 
384 
385 
386 
389  Command::operator string() const
390  {
391  ostringstream repr;
393  repr << "Command(\""<<getID()<<"\") ";
394  if (!isValid())
395  repr << "NIL";
396  else
397  if (canUndo())
398  repr << "{undo}";
399  else
400  if (canExec())
401  repr << "{exec}";
402  else
403  repr << "{def}";
404 
405  return repr.str();
406  }
407 
408 
409 
410 
411  ExecResult
412  Command::exec (HandlingPattern const& execPattern)
413  {
414  ___check_notBottom (this,"Invoking");
415  ___check_isBound (this);
416 
417  string cmdName{*this};
418  CommandImpl& thisCommand (_Handle::impl());
419  return execPattern.exec (thisCommand, cmdName);
420  }
421 
422 
423  ExecResult
424  Command::undo (HandlingPattern const& execPattern)
425  {
426  ___check_notBottom (this,"UNDOing");
427  ___check_canUndo (this);
428 
429  string cmdName{*this};
430  CommandImpl& thisCommand (_Handle::impl());
431  return execPattern.undo (thisCommand, cmdName);
432  }
433 
434 
435  ExecResult
436  Command::exec (HandlingPattern::ID pattID)
437  {
438  return exec (HandlingPattern::get(pattID));
439  }
440 
441 
442  ExecResult
443  Command::undo (HandlingPattern::ID pattID)
444  {
445  return undo (HandlingPattern::get(pattID));
446  }
447 
448 
449  ExecResult
450  Command::execSync ()
451  {
452  return exec (HandlingPattern::get(HandlingPattern::DUMMY));
453  }
454 
455 
456  HandlingPattern::ID
457  Command::getDefaultHandlingPattern() const
458  {
459  ___check_notBottom (this,"Accessing");
460  return impl().getDefaultHandlingPattern();
461  }
462 
463 
464  HandlingPattern::ID
465  Command::setHandlingPattern (HandlingPattern::ID pattID)
466  {
467  ___check_notBottom (this, "Configuring");
468  return impl().setHandlingPattern(pattID);
469  }
470 
471 
472 
473 
474 
475 }} // 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:59
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:76
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:162
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:200
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:280
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:190
Token or Atom with distinct identity.
Definition: symbol.hpp:117
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:115
Lumiera public interface.
Definition: advice.cpp:104
shared_ptr< CommandImpl > createCloneImpl(CommandImpl const &refObject)
create an allocation for holding a clone of the given CommandImpl data.
Definition: command.cpp:219
Steam-Layer Command implementation.
Actually defining a command and binding it to execution parameters.
object-like record of data.
Definition: record.hpp:141
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:71