#ifndef LOB_CHANGE
#define LOB_CHANGE
#include <OmHeaders.h>
#include <QObject>
#include "lob.h"
/** \brief A class embodying an editing action (atomic or compound) on a Lob
*
* <h2>Atomic Changes</h2>
*
* There are three types of atomic editing actions one can do to a Lob:
* insert new content, remove content, and change existing content.
* Here they are in greater detail.
* <ol>
* <li>There are two different ways to insert new content, either inserting a
* child node of some existing node in the tree or inserting a new key-value
* pair to attribute an existing node in the tree.</li>
* <li>There are two different ways to remove content, corresponding to the
* inverses of the two insertion mechanisms. You can either remove an
* existing subtree or remove an existing key-value pair from a node's
* attribute list.</li>
* <li>Changing a subtree means replacing an existing subtree with a different one.
* Although this is equivalent to removing the old subtree and inserting the
* new one, I keep this as its own atomic operation because users editing
* interactively will want to think of such an action as one step. The
* changed node may be the tree root, a child, a key, or a value.</li>
* </ol>
*
* \anchor LobChangeTypes
* <h2>Change Types and Data</h2>
*
* Changes are recorded relative to a given root, and every LobChange object remembers
* that root and the address from the root to the subtree that changed. Specifically,
* the three types above are stored in a LobChange object according to the following
* details, and you can fetch this data using the functions mentioned here.
* <ol>
* <li>A LobChange object with type() == LobChange::Insert will know the address()
* where the new subtree was inserted, and its newData() will be the inserted Lob.</li>
* <li>A LobChange object with type() == LobChange::InsertPair will know the address()
* of the pair's value, and its newData() will be the inserted value, and
* keyName() and keyCD() will store the name and content dictionary for the key.</li>
* <li>A LobChange object with type() == LobChange::Remove will know the address()
* where the new subtree was before removal,
* and its oldData() will be the removed Lob.</li>
* <li>A LobChange object with type() == LobChange::RemovePair will know the address()
* where the pair's value was before removal,
* and its oldData() will be the removed value. The functions keyName() and keyCD()
* store the name and content dictionary for the removed key.</li>
* <li>A LobChange object with type() == LobChange::Change will know the address()
* of the subtree to change, and oldData() will be the subtree before the change
* while newData() will be the subtree after the change.</li>
* <li>A LobChange object with type() == LobChange::Compound is a sequence of atomic
* changes combined into one object. Use count() and step() to query the
* individual atomic changes.</li>
* <li>A LobChange object with type() == LobChange::NoChange is the null action.
* It is the identity element for operator+().</li>
* </ol>
*
* You can use apply() to perform a specified LobChange on a Lob (either the original
* or a different one, if you know what you're doing). You can record in a LobChange
* object edits as you perform them using startRecording(). You can combine two LobChange
* objects into a compound one with combine(), which is the same as operator+().
* Lastly, you can turn any LobChange into the corresponding "undo" action with inverse().
*/
class LobChange : public QObject
{
Q_OBJECT
public:
/** All LobChange objects are one of these types.
* \ref LobChangeTypes "See the LobChange class description for details on types."
*/
typedef enum { Insert, InsertPair, Remove, RemovePair, Change, Compound, NoChange } Type;
/** \brief Default constructor creates a null change object
*
* This object will have type() == LobChange::NoChange and calling apply() does
* nothing.
*/
LobChange ();
/** \brief Default copy operator, restricted to this class's fields
*/
LobChange ( const LobChange& other );
/** \brief Default assignment operator, restricted to this class's fields
*/
LobChange& operator= ( const LobChange& other );
/** \brief Compares two change objects by comparing all their fields
*
* All fields are compared with operator==() except data0 and data1, which are compared
* with Lob::equivalentTo().
*/
bool operator== ( const LobChange& other ) const;
/** \brief The tree in which the change will take place
*
* A change is described/created to take place within a certain Lob tree.
* Although it doesn't need to be applied to that tree, that is the default,
* and the LobChange object remembers that context. You can get that context
* by calling this function.
*
* The address to the specific location within the context() tree at which this change
* takes place (assuming it is an atomic change) can be retrieved using address(),
* and the Lob at that address can be retrieved with result(),
* assuming such a Lob exists; it may not, depending on whether this change is an
* insertion or removal, whether or not the change has been applied, etc.
*/
Lob context () const;
/** \brief The actual Lob that results from this change
*
* While newData() only applies to certain changes, and even in those cases it returns
* a copy of the data, this function gives the actual Lob that was modified, in its
* current state. It is equivalent to calling context().index( address() ).
*
* Note that if the change was of type Remove, RemovePair, or Compound, this will
* return the empty Lob; to see why, read the documentation for address().
*/
Lob result () const;
/** \brief The type of change this object represents
*
* It will be one of the choices defined in the LobChange::Type enum.
* \ref LobChangeTypes "See the LobChange class description for details on types."
*/
Type type () const;
/** \brief The address within context() to which this change applies
*
* The result of this function is based on this object's type().
* <table>
* <tr><th>type()</th><th>Result</th></tr>
* <tr><td>Insert</td>
* <td>the address the inserted Lob will have
* (which may be invalid before applying the change)</td></tr>
* <tr><td>InsertPair</td>
* <td>the address the inserted value from the pair will have
* (which may be invalid before applying the change)</td></tr>
* <tr><td>Remove</td>
* <td>the address of the Lob to remove has/had before its removal
* (which may be invalid after applying the change)</td></tr>
* <tr><td>RemovePair</td>
* <td>the address of the value from the pair to remove has/had before its removal
* (which may be invalid after applying the change)</td></tr>
* <tr><td>Change</td>
* <td>the address of the subtree that will be changed</td></tr>
* <tr><td>NoChange</td>
* <td>the empty address, because it does not matter</td></tr>
* <tr><td>Compound</td>
* <td>the empty address, but the addresses of individual steps
* can be queried; each may be different than the others</td></tr>
* </table>
*/
LobAddress address () const;
/** \brief The new data introduced by this change, if any
*
* The result of this function is based on this object's type().
* <table>
* <tr><th>type()</th><th>Result</th></tr>
* <tr><td>Insert</td><td>the new Lob the change inserts</td></tr>
* <tr><td>InsertPair</td><td>the value from the pair the change inserts</td></tr>
* <tr><td>Change</td><td>the value to which the Lob is changed</td></tr>
* <tr><td>any other type</td><td>an empty Lob</td></tr>
* </table>
*
* Note that this is only a copy of the modified data, when it applies. See also
* result(), which is more useful in some situations.
*/
Lob newData () const;
/** \brief The old data removed by this change, if any
*
* The result of this function is based on this object's type().
* <table>
* <tr><th>type()</th><th>Result</th></tr>
* <tr><td>Remove</td><td>the Lob the change removes</td></tr>
* <tr><td>RemovePair</td><td>the value from the pair the change removes</td></tr>
* <tr><td>Change</td><td>the value from which the Lob is changed</td></tr>
* <tr><td>any other type</td><td>an empty Lob</td></tr>
* </table>
*/
Lob oldData () const;
/** \brief If the change involves a pair, this is the name of the pair's key
*
* The result of this function is based on this object's type().
* <table>
* <tr><th>type()</th><th>Result</th></tr>
* <tr><td>InsertPair</td><td>the name of the key in the pair to be inserted</td></tr>
* <tr><td>RemovePair</td><td>the name of the key in the pair to be removed</td></tr>
* <tr><td>any other type</td><td>an empty string</td></tr>
* </table>
*/
QString keyName () const;
/** \brief If the change involves a pair, this is the content dictionary of the pair's key
*
* The result of this function is based on this object's type().
* <table>
* <tr><th>type()</th><th>Result</th></tr>
* <tr><td>InsertPair</td>
* <td>the content dictionary of the key in the pair to be inserted</td></tr>
* <tr><td>RemovePair</td>
* <td>the content dictionary of the key in the pair to be removed</td></tr>
* <tr><td>any other type</td><td>an empty string</td></tr>
* </table>
*/
QString keyCD () const;
/** \brief If the change is a compound one, this is the number of atomic steps in it
*
* If type() == LobChange::Compound, then this change is comprised of zero or more
* atomic steps, and this is how many there are. Use step() to query them. If type()
* is any other value, this returns zero.
*/
unsigned int count () const;
/** \brief Retrieves one of the steps in a compound change
*
* If this object has type() == LobChange::Compound, then it is comprised of zero or
* more atomic steps.
*
* Pass this routine any \a n between zero and count()-1 and it
* will return the corresponding step. The steps are to be interpreted as if step 0
* were the first step the change would execute (under apply()) and step count()-1
* the last. If \a n is too large, a newly constructed LobChange is returned (the
* null action, of type LobChange::NoChange).
*
* Each step is guaranteed to be atomic, so for no \a n should you find
* that step(n).type() == LobChange::Compound. That is, this is a flat structure,
* not a hierarchy.
*/
LobChange step ( unsigned int n ) const;
/** \brief Perform the action in this change object on the given Lob
*
* If the \a toThis parameter is not given (and thus defaults to an empty Lob),
* it will first be replaced by context() before performing the change. Thus
* the simplest way to call this routine is with no parameters, and it will act
* on the Lob from which it was created/recorded. If \a toThis is provided, it is
* used as the context instead, so that the change affects toThis.index( address() ).
*
* If this is a compound object, all steps are executed in the context provided in
* \a toThis (which defaults to context() if not provided). That is, none of the
* contexts of the steps are inspected or used at all.
*
* In the execution of this routine, this object may change. For instance, applying
* a LobChange::Change type() object to a Lob will inspect the subtree that's about
* to be changed, and store it for later retrieval using oldData(). This may or may
* not result in a change to that internally stored value.
*
* \return True if the change took place successfully. False if it failed, because
* the \a toThis object (or context() if no \a toThis was given) did not have
* the appropriate structure; for instance, trying to add an attribute to a
* subtree that isn't there, or trying to add child #7 to a node with only
* 3 children. NoChange actions always succeed, and Compound changes succeed
* if and only if all parts did; if one fails, none after that are attempted.
*/
bool apply ( Lob toThis = Lob() );
/** \brief Creates a new LobChange that would undo this one, if applied after it
*
* Mathematically speaking, this creates a "right inverse," as in the following code.
* \code
* Lob backup = myLob.copy();
* A.apply( myLob );
* A.inverse().apply( myLob );
* // here myLob.equivalentTo( backup, true ) is true
* \endcode
* It does not create a "left inverse" in the sense that the two apply lines above
* could not necessarily be reversed and expect the result to be the same.
*
* In common computer parlance, A.inverse() is a change that could "undo" A, if
* performed immediately after it. The inverse of an Insert is a Remove, the inverse
* of an InsertPair is a RemovePair, and the inverse of a Change, Compound, or NoChange
* is of the same type as the original. In each case, the internal data may be
* different (e.g., oldData() may become newData(), etc., as needed).
*/
LobChange inverse () const;
/** \brief Combine this object with the given one to create a compound LobChange
*
* This LobChange may be atomic or compound, as may \a withThis, but the result
* is guaranteed to be of type() LobChange::Compound. It will be a flat list,
* so that all atomic steps are accessible through step(). That is, no step()
* will be of type() LobChange::Compound. The order is that all steps in this
* object are first, followed by all steps in \a withThis.
*
* The new LobChange objects returned has the same context() as this one, meaning
* that the context() of \a withThis, if different, was disregarded.
*/
LobChange combine ( const LobChange& withThis ) const;
/** \brief Convenient way to access the combine() function.
*
* That is, <code>change1 + change2</code> is the same as
* <code>change1.combine( change2 )</code>.
*/
LobChange operator+ ( const LobChange& other ) const;
/** \brief Watch for any changes to the given Lob, and combine them into this object
*
* This provides a way of recording any kind of changes to Lobs for later undoing
* or playback. Simply call <code>myLobChange.startRecording( myLob );</code>, make any
* changes, and then call <code>myLobChanges.stopRecording();</code>. To undo the
* changes, call <code>myLobChanges.inverse().apply();</code>. To redo them, call
* <code>myLobChanges.apply();</code>.
*
* Calling this function sets the context() of this LobChange object to \a target.
* You can't be recording changes from more than one Lob at a time.
* Calling startRecording() twice without an intervening call to stopRecording()
* generates a silent stopRecording() call before the second start.
*/
void startRecording ( Lob target );
/** \brief Turn off recording changes to the given Lob
*
* This function is explained in the documentation for startRecording().
*/
void stopRecording ();
/** \brief Whether this change object is recording changes to a Lob
*
* Returns true if and only if the most recent call to startRecording() has not been
* followed up (yet) by a call to stopRecording().
*/
bool isRecording ();
/** For use in debugging
*/
QString toString ( bool verbose = false ) const;
public slots:
/** For use when recording changes
*/
void recordStep ( const LobChange& step );
private:
#ifdef LURCH_UNIT_TEST
friend class LURCH_UNIT_TEST;
#endif
// so that Lob can set up these objects however it sees fit, when emitting change signals
friend class Lob;
/** The tree (context) in which this LobChange object was recorded/created
*/
Lob root;
/** The location within the root (context) at which this change takes place
*/
LobAddress addr;
/** The type of this change.
* \ref LobChangeTypes "See the LobChange class description for details on types."
*/
Type ty;
/** Where any Lob data about the initial state (before the change) is stored.
*/
Lob data0;
/** Where any Lob data about the final state (after the change) is stored.
*/
Lob data1;
/** If the change involves an attribute key, this is its name.
*/
QString name;
/** If the change involves an attribute key, this is its content dictionary.
*/
QString cd;
/** If this change is a compound change, the atomic steps are stored in this list.
*/
QList<LobChange> steps;
/** Stores whether or not recording is in progress
*/
bool recordingInProgress;
/** Create a new LobChange object of Insert type, with all required data
*/
static LobChange newInsert ( Lob data );
/** Create a new LobChange object of InsertPair type, with all required data
*/
static LobChange newInsertPair ( QString keyName, QString keyCD, Lob value );
/** Create a new LobChange object of Remove type, with all required data
*/
static LobChange newRemove ( Lob data );
/** Create a new LobChange object of RemovePair type, with all required data
*/
static LobChange newRemovePair ( QString keyName, QString keyCD, Lob value );
/** Create a new LobChange object of Change type, with all required data
*/
static LobChange newChange ( Lob before, Lob after );
};
#endif // LOB_CHANGE