#ifndef LOB
#define LOB
#include <OmHeaders.h>
#include <QObject>
#include <QVariant>
#include <QFile>
#include <QStringList>
#include <QMap>
#include "laddress.h"
#include "safesingleton.h"
class LobChange;
/** \brief Lob is short for Lurch Object, a tuning of OpenMath objects for
* Qt, Lurch, and scripting
*
* <h2>Purpose</h2>
* This class is a wrapper for an OpenMath object; it differs from OpenMath
* objects in three ways, matching this class's three purposes.
* <ol>
* <li>This class uses Qt data types (like QString, QFile, etc.) where the OpenMath C++
* library underlying it uses standard types (C FILE*, std list, etc.). Thus this
* class integrates OpenMath into a Qt environment more smoothly.</li>
* <li>This class contains some functions tailored to how Lurch makes use of OpenMath,
* corresponding to definitions such as Lurch Object, Lurch Reference, Lurch Script,
* and Lurch Document.</li>
* <li>This class uses properties and public slots to expose to scripting environments
* just those members that should be script-accessible.</li>
* </ol>
* A more fine-grained breakdown of the above three purposes appears in a few paragraphs.
*
* \anchor LobMemoryManagement
* <h2>Memory management</h2>
* Creating a Lob wrapper for an OmNode* introduces the tree rooted at that OmNode* to the
* Lob memory management system, which takes ownership of the OmNode and all its children,
* and will delete them later, when no Lob refers to them or to any other OmNode in the
* same tree. Creating a Lob wrapper for an OmNode* therefore gives ownership to the Lob
* (and to the rest of the Lobs, as a collective) and you should not subsequently delete the
* OmNode*, nor any other node in the same tree, because Lobs can navigate to other nodes in
* the same tree using methods like parent() and nextSibling() and so on.
* If you wish to maintain ownership, create a Lob
* wrapper for an OmNode::clone() of the desired OmNode* instead.
*
* It is not necessary for clients to learn the details of the Lob memory management system.
* You are free to create and destroy Lobs as you see fit without worrying about cleaning
* up their contents. You are also free to assign one Lob to another or create a copy()
* as you need to, and the Lob class will handle cleaning up any
* OmNodes created in such a process, provided that you ensure the Lobs themselves are
* deleted. The class is designed to be used without pointers; that is, you typically
* declare variables of type Lob, not type Lob*. If you are curious about the details of
* the Lob memory management system anyway, see hold() and release().
*
* For instance, here is an example of typical, intended C++ use of Lobs. Analogous code is
* acceptable in scripts.
* \code
* Lob myLob = Lob::fromXML( "<OMI>5</OMI><OMV name=\"someVariable\"/>" );
* int x = myLob.numChildren(); // x is now == 2
* myLob = myLob.child().nextSibling();
* QVariant v = myLob.toVariant(); // v is now the QVariant string "someVariable"
* \endcode
* At no point in this code is any of the OmNode tree deleted, because myLob still points
* to part of it. When myLob goes out of scope, the whole tree will be deleted.
*
* <h2>Categories of functionality</h2>
* The three purposes of this class, as stated above, break down into the following more
* specific categories of functionality which
* roughly partition this class's members, with links to the corresponding properties
* and functions in each category.
* <ul>
* <li><b>Reading and writing OM trees:</b>
* Create OpenMath trees from QStrings or QFiles, and write representations of them
* into QStrings or QFiles with the four routines
* fromXML(QString), fromXML(QFile&), toString(), and save().</li>
* <li><b>Navigating OM trees:</b>
* The underlying OpenMath tree structure is accessible through this class's methods,
* specifically numChildren(), child(), parent(), nextSibling(), previousSibling(),
* etc. Although code like
* <code>myLob.child().nextSibling().nextSibling().getID()</code>
* creates many intermediate objects to do its work, all are created on the stack
* and will thus be automatically cleaned up in the same scope. Such code is
* well within the confines of the expected use of such functions, in C++ or in
* scripts.</li>
* <li><b>Modifying OM trees:</b>
* Use functions such as addChild(), insertChild(), removeChild(),
* setAttribute(), removeAttribute(), set(), and remove().</li>
* <li><b>Converting basic data types:</b>
* The properties basicType (accessed through isBasicType()) and basicValue (accessed
* through toVariant()) check whether this Lob points to
* an OpenMath object of a basic type, and if so, convert it to a suitable Qt type.
* A corresponding Lob::fromVariant(QVariant) method allows creation of OpenMath
* objects from Qt data.</li>
* <li><b>Attributes and comments:</b>
* Attributes can be read using the attribute() method.
* Comments can be read and written using the routines numComments(), comment(),
* comments(), setComments(), insertComment(), appendComment(), and removeComment().
* <li><b>Lurch references:</b>
* You can create an ID reference to a Lob with reference(), and dereference such a
* reference (obtaining the referent Lob) with referent(). You can get a Lob's
* nickname with getNickname() and read nickname references
* from Lurch Reference objects with getNicknameReference().</li>
* <li><b>Lurch Scripts:</b>
* You can check whether a Lob wraps a script using isScript(), and if it returns
* true, then get the corresponding parts of the script with scriptLanguage(),
* scriptCode(), and scriptType().</li>
* <li><b>Lurch documents:</b>
* The method isDocument() tells whether the wrapped node represents a Lurch Document,
* and if so, the methods getAuthor(), getTitle(), getVersion(), getURN(),
* and getDependencies() can be used on it.</li>
* </ul>
*
* This class is tested by methods in the class test_lob.
*/
class Lob : public QObject
{
Q_OBJECT
/** \brief Give scripts access to read the "empty" state (through isEmpty())
* \see isEmpty()
*/
Q_PROPERTY( bool empty READ isEmpty STORED false )
/** \brief Give scripts access to read whether this Lob is modifiable
*
* All routines that reference this are tested in test_lob_edit::test_read_only().
*
* \see isEditable(), setEditable()
*/
Q_PROPERTY( bool editable READ isEditable STORED false )
/** \brief Give scripts access to read the type of the wrapped node
* \see scriptNodeType()
*/
Q_PROPERTY( int type READ scriptNodeType STORED false )
/** \brief Give scripts access to convert a wrapped basic node to a QVariant
* \see toVariant()
*/
Q_PROPERTY( QVariant basicValue READ toVariant STORED false )
/** \brief Give scripts access to read the attribute keys of the wrapped node
* \see scriptAttributeKeys()
*/
Q_PROPERTY( QVariantList keys READ scriptAttributeKeys STORED false )
/** \brief Give scripts access to read the number of attribute keys of the wrapped node
*
* Script code like <code>myLob.numKeys</code> will give the same value as
* <code>myLob.keys.length</code>, but will be more efficient, because the whole
* keys array does not need to be created.
*
* \see numAttributes()
*/
Q_PROPERTY( uint numKeys READ numAttributes STORED false )
/** \brief Give scripts access to read the nickname attribute of a Lurch Object
* \see getNickname(), setNickname()
*/
Q_PROPERTY( QString nickname READ getNickname WRITE setNickname STORED false )
/** \brief Give scripts access to read the author field of a Lurch Document
* \see getAuthor(), setAuthor()
*/
Q_PROPERTY( QString author READ getAuthor WRITE setAuthor STORED false )
/** \brief Give scripts access to read the title field of a Lurch Document
* \see getTitle(), setTitle()
*/
Q_PROPERTY( QString title READ getTitle WRITE setTitle STORED false )
/** \brief Give scripts access to read the encoding language field of a Lurch Document
* \see getEncodingLanguage(), setEncodingLanguage()
*/
Q_PROPERTY( QString encoding READ getEncodingLanguage WRITE setEncodingLanguage
STORED false )
/** \brief Give scripts access to read the version field of a Lurch Document
* \see getVersion(), setVersion()
*/
Q_PROPERTY( QString version READ getVersion WRITE setVersion STORED false )
/** \brief Give scripts access to read the dependencies list of a Lurch Document
* \see getDependencies(), setDependencies()
*/
Q_PROPERTY( QStringList dependencies READ getDependencies WRITE setDependencies
STORED false )
/** \brief Give scripts access to read the URN of a Lurch Document
* \see getURN(), setURN()
*/
Q_PROPERTY( QString URN READ getURN WRITE setURN STORED false )
/** \brief Give scripts access to read the code of a Lurch Script
* \see scriptCode(), setScriptCode()
*/
Q_PROPERTY( QString code READ scriptCode WRITE setScriptCode STORED false )
/** \brief Give scripts access to read the scripting language of a Lurch Script
* \see scriptLanguage(), setScriptLanguage()
*/
Q_PROPERTY( QString language READ scriptLanguage
WRITE setScriptLanguage STORED false )
/** \brief Give scripts access to read the type of a Lurch Script
* \see scriptType(), setScriptType()
*/
Q_PROPERTY( QString scriptType READ scriptType
WRITE setScriptType STORED false )
/** \brief Give scripts access to read the nickname from a Lurch Reference
* \see getNicknameReference(), setNicknameReference()
*/
Q_PROPERTY( QString nicknameReference READ getNicknameReference
WRITE setNicknameReference STORED false )
/** \brief Give scripts access to this Lob's comments, as a single array
* \see comments()
*/
Q_PROPERTY( QStringList comments READ comments WRITE setComments STORED false )
public:
/** \brief Constructor that initializes this to point to the given OpenMath object
*
* \param wrapThis Defaults to no object, which makes this Lob "empty"
* (meaning it refers to nothing, and any calls to any methods herein
* will return undefined/useless information and/or not do anything).
* \param editable Initial value for the editable property
*
* This is tested by test_lob::test_basics().
*
* \see empty, editable
*/
Lob ( OmNode* wrapThis = 0, bool editable = true );
/** \brief Copy constructor, initializes this to wrap the same object as the given Lob
*
* Thus this creates a new Lob, but not a new OmNode tree. Only the wrapper is cloned.
* To create a full tree copy, see copy().
*
* You can safely copy Lobs in this way as much as you like without worrying about
* memory management issues; if you delete all your Lobs, all their wrapped OmNodes
* will also be deleted, none too soon or too late. See \ref LobMemoryManagement
* "the notes on memory management".
*
* This is tested by test_lob::test_copies().
*/
Lob ( const Lob& copyThis );
/** \brief Destroys the Lob, and possibly the wrapped node, if it is time to do so
*
* For information on the memory management system Lobs use to only delete wrapped
* OmNodes when it is time to do so, \ref LobMemoryManagement "see above". In general,
* an OmNode* is only deleted if no other Lob in existence wraps it or any other node
* in the same tree (including attribute nodes).
*/
~Lob ();
/** \brief Change this Lob to wrap the same OpenMath node as a different Lob
*
* It is safe to assign one Lob to another; no memory leaks can happen, because
* no new objects are created in the process. This assignment simply modifies this
* Lob to wrap the same OpenMath object as the parameter \a rhs.
*
* This is tested by test_lob::test_copies().
*/
Lob& operator= ( const Lob& rhs );
/** \brief Check whether two Lobs wrap the same OpenMath object
*
* Two Lobs are considered equal if they wrap the same OpenMath object.
* The reason for this is that the Lob is really just an OpenMath object in the
* first place, with convenience functions for our particular uses of OpenMath,
* Qt, scripting, etc. Thus the only data in a Lob is the OpenMath object, making two
* Lobs equal if and only if their wrapped objects are equal.
*
* This is tested by test_lob::test_copies().
*
* Script authors can access this with the <code>.equals()</code> member function
* of Lob objects in script.
*
* \see equivalentTo()
*/
friend bool operator== ( const Lob& a, const Lob& b );
/** \brief Simply the negation of the == operator for Lobs
*
* This is tested by test_lob::test_copies().
*
* \see operator==(), equivalentTo()
*/
friend bool operator!= ( const Lob& a, const Lob& b );
/** \brief This operator is not very useful practically, except for enabling hashing Lobs
*/
friend bool operator< ( const Lob& a, const Lob& b );
/** \brief Saves a representation of the node in OpenMath's XML encoding to the file
*
* Writes the OpenMath XML representation to the given file. The file is closed again
* at the end of this routine if and only if it was closed when the routine was called.
* The \a message parameter is used as in fromXML(QFile&).
* \return true on success, false for any type of failure
*
* This is tested by test_lob::test_qfile_qstring().
*
* \see fromXML(QFile&), toString()
*/
bool save ( QFile& out, QString* message = 0 );
/** \brief Assuming the wrapped OpenMath object has basic type, convert it to a QVariant
*
* If this object wraps an OpenMath object of a basic type, then it can be converted to
* a QVariant according to the following convention. If it is not of a basic type, then
* an empty QVariant() is returned.
* <table border=1>
* <tr><th>OpenMath type</th> <th>Create a QVariant from this data</th></tr>
* <tr><td>OmIntegerType</td> <td>the int given by OmIntegerNode::getValue()</td>
* </tr>
* <tr><td>OmBigIntegerType</td><td>the QString of digits created from
* OmBigIntegerNode::getDigits(),
* possibly preceded by a minus sign, if
* OmBigIntegerNode::getSign() is negative</td></tr>
* <tr><td>OmFloatType</td> <td>the double given by
* OmFloatNode::getValue()</td></tr>
* <tr><td>OmByteArrayType</td> <td>the QByteArray constructed from the output
* parameters of OmByteArrayNode::getBuffer()</td>
* </tr>
* <tr><td>OmVariableType</td> <td>the QString created from
* OmVariableNode::getName()</td></tr>
* <tr><td>OmStringType</td> <td>the QString created from
* OmStringNode::getBuffer()</td></tr>
* <tr><td>OmWStringType</td> <td>the QString created by QString::fromWCharArray()
* from OmWStringNode::getBuffer()</td></tr>
* <tr><td>OmSymbolType</td> <td>the QStringList containing the two strings
* OmSymbolNode::getName() and
* OmSymbolNode::getCD(), in that order</td></tr>
* </table>
*
* This is tested in test_lob::test_conversions().
*
* \see isBasicType()
*/
QVariant toVariant () const;
/** \brief Whether this wraps an actual OpenMath object
*
* Because this class is a wrapper for OpenMath, it's possible that it was constructed
* with no actual content. This method detects one kind of invalid content (one kind
* of pointer error) by returning whether the internal OmNode pointer is zero.
*
* This is tested by test_lob::test_basics().
*
* \see empty
*/
bool isEmpty () const;
/** \brief Whether this Lob may be modified by routines like set() and setAttribute()
*
* Some Lobs will need to be read-only, so that one can inspect read-only documents
* in Lob format. In such a case, no modifier method such as set() or setAttribute()
* has any effect on the Lob. This function tells you whether the Lob is editable,
* the opposite of read-only.
*
* \see editable
*/
bool isEditable () const;
/** \brief Set this Lob to be modifiable/editable
*
* Although it may seem silly to have a read-only status if you can change it with a
* public method, the main purpose of this status is to make some Lobs read-only from
* scripts. Since scripts cannot access the public methods of a class (only its
* public slots), this is secure. This method is not the setter for the
* <code>editable</code> property; that is intended.
*
* \see editable
*/
void setEditable ( bool on = true );
/** \brief Type of the internal OpenMath node
*
* Simply calls the OmNode::type() function of the wrapped object and returns its result.
*
* Script authors should use the Lob::type property instead.
*
* \return Possible return values are listed
* <a href='http://lurch.sourceforge.net/omcppdoc/all-globals.html'>here</a>.
* If this Lob is empty, returns OmUnknownType.
*
* \see empty
*/
OmType nodeType () const;
/** \brief Changes the wrapped OmNode (in position in its tree) to the given OmNode
*
* This routine replaces the subtree whose root is this Lob's wrapped OmNode with the
* tree whose root is the given OmNode \a s. This involves several steps.
* <ol>
* <li>If the OmNode wrapped in this Lob is another node's child or attribute key or
* value, then extract this subtree from its parent tree. (If it was a key or
* value, extract its partner as well, of course.)</li>
* <li>If \a s is another node's child or attribute key or value,
* then extract it from its parent tree in the same way.</li>
* <li>Insert \a s in the place where this Lob's wrapped OmNode had been;
* this may be a location in its parent's child list, or a location in its
* parent's attribute list (as either a key or a value). In the latter case,
* the formerly extracted partner key/value will be reinserted,
* now partnered with \a s.</li>
* <li>Change this Lob to wrap \a s, and wrap the OmNode that this Lob previously
* wrapped in a temporary Lob, so that it is still owned and cared for by
* \ref LobMemoryManagement "the Lob memory management system".</li>
* </ol>
*
* This routine fails (i.e., does nothing) in the following cases.
* <ul>
* <li>if this Lob wraps the symbol in an
* attribute key-value pair, and \a s is not an OmSymbolNode</li>
* <li>if this Lob wraps an OmNode that is a child or attribute key/value,
* and \a s is either a null pointer or has type OmUnknownType</li>
* <li>if performing the operation would create a
* cyclic parenting or attribution relationship (e.g.,
* <code>myLob.attribute( "a", "b" ).set( myLob.parent() );</code>)</li>
* <li>if this Lob is read-only (not isEditable())</li>
* </ul>
*/
void set ( OmNode* s );
/** \brief Fetch a list of attribute keys, as OpenMath symbols
*
* This will be an ordered list of attribute keys, each of which is an OpenMath
* symbol node with a name and content dictionary. There may be duplicates in this
* list, because the OpenMath Standard permits duplicate keys in the attribute list,
* but querying the attribute returns a unique value because one value takes precedence
* over the others.
*
* Because the Lobs in the returned list are copies of the originals, they are always
* editable, even if the originals were not; modifying them does not alter the
* actual symbols that key this Lob's attributes.
*
* Script authors should use the Lob::keys property instead.
*
* \see numAttributes()
*/
QList<Lob> attributeKeys () const;
/** \brief Convenience function that yields length of attributeKeys()
*
* Rather than call attributeKeys() and ask for its length, this function is more
* efficient because it doesn't involve constructing the full array. Yet the results
* are the same. Returns zero for an empty Lob.
*
* \see attributeKeys()
*/
unsigned int numAttributes () const;
/** \brief Whether the wrapped OpenMath object has the given attribute
*
* \param symbolName the name of the symbol that is the attribute's key
* \param symbolCD the content dictionary of the symbol that is the attribute's key
* \return true if and only if the wrapped OpenMath object is attributed with a
* key-value pair whose key is the symbol described by the given parameters
*
* This is tested by test_lob::test_attributes().
*
* \see attribute(), moveToAttribute()
*/
bool hasAttribute ( QString symbolName, QString symbolCD ) const;
/** \brief Create a new Lob wrapping the specified attribute node within this one
*
* Return a new Lob pointing to the OpenMath object
* in this object's attribute list indexed by the symbol with the given name and
* content dictionary.
*
* If no attribute indexed by the specified symbol exists in the current
* node, an empty Lob is returned.
*
* If this Lob is not editable, then the returned attribute Lob will not be either.
*
* This is tested by test_lob::test_attributes().
*
* \see empty, hasAttribute(), moveToAttribute()
*/
Lob attribute ( QString symbolName, QString symbolCD ) const;
/** \brief Adds or modifies the given attribute in this Lob
*
* If this Lob has an attribute with the specified key, this routine finds it and
* replaces its value with \a newValue. In this case, it functions just as would the
* code <code>attribute( keyName, keyCD ).set( newValue )</code>. For this reason,
* caveats present in the documentation for Lob::set() apply.
*
* If this Lob has no attribute with the
* specified key, this routine adds a new attribute with the specified key, with
* value \a newValue. If this Lob wraps an unattributable node (e.g., unknown type
* or empty Lob), or \a newValue is empty or of unknown type, this routine does nothing.
*
* This routine also does nothing if this Lob is read-only (not isEditable()).
*
* \see removeAttribute()
*/
void setAttribute ( QString keyName, QString keyCD, Lob newValue );
/** \brief Removes the key and value of the given attribute
*
* The parameters \a keyName and \a keyCD identify the key to the attribute, but both
* the key and the value are removed. Note that an OpenMath object may be attributed
* with the same key several times, and this removes the first pair, the one that was
* used in any recent lookups of the attribute; a hidden attribute may then become
* visible.
*
* If this Lob does not have an attribute with the given key, this routine does nothing.
* This includes the case when this Lob isEmpty().
*
* This routine also does nothing if this Lob is read-only (not isEditable()).
*
* \see setAttribute()
*/
void removeAttribute ( QString keyName, QString keyCD );
/** \brief The nickname that is an alias for this Lurch Object's ID
*
* Some Lobs may wish to have a nickname that refers to them rather than just an
* ID. For instance, a Lurch Object representing a theorem may wish to be called by
* the name of the theorem. Although this class does not provide any kind of management
* or searching by such nicknames, it provides a place to store them should clients need
* them.
*
* This function looks up a Lob's nickname (the alias for its ID), which
* can be any nonempty string, and returns it. The nickname is stored in the wrapped
* OpenMath object, as an attribute whose key is "nickname" with content dictionary
* "LurchCore," in an OmStringNode. An empty string is returned if no such attribute
* exists.
*
* This is tested in test_ref::test_nicknames().
*
* \see isNicknameReference(), getNicknameReference()
*/
QString getNickname () const;
/** \brief Set the nickname of this Lurch Object
*
* Nicknames are described in getNickname().
* You can set this object's nickname with this function, passing it any string.
* This writes to the nickname attribute (content dictionary LurchCore) as described in
* getNickname().
*
* This routine does nothing if this Lob is read-only (not isEditable()).
*
* This is tested in test_script::test_setters().
*
* \see getNickname()
*/
void setNickname ( QString nickname );
/** \brief Assuming the Lob wraps a document, get the document's author
*
* \return if the Lob passes the isDocument() test, then its author is returned as a
* QString; otherwise, the result is undefined (usually an empty QString())
*
* This is tested in test_doc::test_getters().
*
* \see author, title, version, dependencies, URN
*/
QString getAuthor () const;
/** \brief Set the author of this Lurch Object, as if it is a Lurch Document
*
* Although you can set the author of any Lurch Object, it is designed to be used
* with Lurch Documents, as specified in the description of isDocument().
* You can set this object's author with this function, passing it any string.
* This writes to the author attribute (content dictionary LurchCore) as described in
* getAuthor().
*
* This routine does nothing if this Lob is read-only (not isEditable()).
*
* This is tested in test_script::test_setters().
*
* \see getAuthor()
*/
void setAuthor ( QString author );
/** \brief Assuming the Lob wraps a document, get the document's title
*
* \return if the Lob has a string attribute called "title" (in content dictionary
* "LurchCore"), as all documents do, then its title is returned as a
* QString; otherwise, the result is undefined (usually an empty QString())
*
* This is tested in test_doc::test_getters().
*
* \see author, title, encoding, version, dependencies, URN
*/
QString getTitle () const;
/** \brief Set the title of this Lurch Object, as if it is a Lurch Document
*
* Although you can set the title of any Lurch Object, it is designed to be used
* with Lurch Documents, as specified in the description of isDocument().
* You can set this object's title with this function, passing it any string.
* This writes to the title attribute (content dictionary LurchCore) as described in
* getTitle().
*
* This routine does nothing if this Lob is read-only (not isEditable()).
*
* This is tested in test_script::test_setters().
*
* \see getTitle()
*/
void setTitle ( QString title );
/** \brief Assuming the Lob wraps a document, get the document's encoding language
*
* \return if the Lob has a string attribute called "title" (in content dictionary
* "LurchCore"), as all documents do, then its encoding language is returned as a
* QString; otherwise, the result is undefined (usually an empty QString())
*
* This is tested in test_doc::test_getters().
*
* \see author, title, encoding, version, dependencies, URN
*/
QString getEncodingLanguage () const;
/** \brief Set the encoding language of this Lurch Object, as if it is a Lurch Document
*
* Although you can set the encoding language of any Lurch Object, it is designed to be
* used with Lurch Documents, as specified in the description of isDocument().
* You can set this object's encoding language with this function, passing it any string.
* This writes to the language attribute (content dictionary LurchCore) as described in
* getEncodingLanguage().
*
* This routine does nothing if this Lob is read-only (not isEditable()).
*
* This is tested in test_script::test_setters().
*
* \see getEncodingLanguage()
*/
void setEncodingLanguage ( QString lang );
/** \brief Assuming the Lob wraps a document, get the document's version
*
* \return if the Lob passes the isDocument() test, then its version is returned as a
* QString; otherwise, the result is undefined (usually an empty QString())
*
* This is tested in test_doc::test_getters().
*
* \see author, title, version, dependencies, URN
*/
QString getVersion () const;
/** \brief Set the version of this Lurch Object, as if it is a Lurch Document
*
* Although you can set the version of any Lurch Object, it is designed to be used
* with Lurch Documents, as specified in the description of isDocument().
* You can set this object's version with this function,
* provided that you supply a valid version, which is a string containing only
* alphanumerics and dots.
* If you do not provide a string of the correct form, this function does
* nothing.
* This writes to the version attribute (content dictionary LurchCore) as described in
* getVersion().
*
* This routine also does nothing if this Lob is read-only (not isEditable()).
*
* This is tested in test_script::test_setters().
*
* \see getVersion()
*/
void setVersion ( QString version );
/** \brief Assuming the Lob wraps a document, get the document's dependencies
*
* \return if the Lob passes the isDocument() test, then its dependencies are returned as
* a QStringList (the actual dependencies string split by spaces); otherwise, the
* result is undefined (usually an empty QStringList())
*
* This is tested in test_doc::test_getters().
*
* \see author, title, version, dependencies, URN
*/
QStringList getDependencies () const;
/** \brief Set the dependencies of a Lurch Object, as if it is a Lurch Document
*
* Although you can set the dependencies of any Lurch Object, it is designed to be
* used with Lurch Documents, as specified in the description of isDocument().
* You can set this object's dependencies with this function,
* provided that you supply a QStringList, each element of which is a valid Lurch URN,
* as described in isDocument().
* If you do not provide an argument of the correct form, this function does nothing.
* This writes to the dependencies attribute (content dictionary LurchCore) as described
* in getDependencies().
*
* This routine also does nothing if this Lob is read-only (not isEditable()).
*
* This is tested in test_script::test_setters().
*
* \see getDependencies()
*/
void setDependencies ( QStringList dependencies );
/** \brief Assuming the Lob wraps a document, get its URN
*
* \return for a Lob that passes the isDocument() test, its URN is returned as a
* QString, assembled from the requisite parts using makeLurchURN(); for other
* Lobs, the result is undefined (usually an empty QString())
*
* Note that a document does not internally store the optional identifier, because
* that identifier can change with context; see, for example, how LurchEnvironment
* uses those identifiers to label documents with something equivalent to namespaces.
* Thus the return value for this function will never have the
* <code>identifier=urn</code> form mentioned in the documentation for isLurchURN().
*
* This is tested in test_doc::test_getters().
*
* \see author, title, version, dependencies, URN
*/
QString getURN () const;
/** \brief Set the URN of a Lurch Object, as if it is a Lurch Document
*
* Although you can set the URN of any Lurch Object, it is designed to be
* used with Lurch Documents, as specified in the description of isDocument().
* You can set this object's URN with this function,
* provided that you supply a valid one, as described in isLurchURN().
* If you do not provide an argument of the correct form, this function does nothing.
* This writes to the author, title, and version attributes (all from content dictionary
* LurchCore) as described in getURN(); it calls the functions setTitle(), setAuthor(),
* and setVersion() to do its work.
*
* This routine also does nothing if this Lob is read-only (not isEditable()).
*
* This is tested in test_script::test_setters().
*
* \see getURN()
*/
void setURN ( QString urn );
/** \brief Assuming the Lob wraps a Lurch Script, get the code for the script
*
* For instance, in the Lurch Script object whose XML representation is given in the
* description of the isScript() member, scriptCode() would return just
* a QString like the following.
* \code
* "function myFunction ( a, b ) {\n"
* " c = a + b;\n"
* " d = 2 * c;\n"
* " return d;\n"
* "}\n"
* "myFunction(7,10);"
* \endcode
*
* If the scriptLanguage() is CoffeeScript, the result will be prefixed with the
* string <code>"# CoffeeScript\n#</code>, to ensure it is distinguishable from JavaScript
* by both human-readable comments and machine-checkable syntax. No other languages
* (of which JavaScript is currently the only option) transform the result in any way.
*
* If the wrapped Lob is not a Lurch Script, the empty QString() is returned.
*
* This is tested by test_func::test_all().
*
* \see isScript(), scriptLanguage(), scriptType()
*/
QString scriptCode () const;
/** \brief Set the code of this Lurch Object, as if it is a Lurch Script
*
* Although you can set the code of any Lurch Object that wraps an OpenMath string
* node, it is designed to be used with Lurch Script,
* as specified in the description of isScript().
* You can set this object's code with this function, passing it any string.
* This changes the contents of the wrapped string node, or does nothing if the
* wrapped object is not a string node.
*
* This routine also does nothing if this Lob is read-only (not isEditable()).
*
* This is tested in test_script::test_setters().
*
* \see scriptCode()
*/
void setScriptCode ( QString code );
/** \brief Assuming the Lob wraps a Lurch Script, get the language attribute
*
* For instance, in the Lurch Script object whose XML representation is given in the
* description of the isScript() member, scriptLanguage() would return
* just the QString "ECMAScript".
*
* If the wrapped Lob is not a Lurch Script, the empty QString() is returned.
*
* This is tested by test_func::test_all().
*
* \see isScript(), scriptCode(), scriptType()
*/
QString scriptLanguage () const;
/** \brief Set the script language of this Lurch Object, as if it is a Lurch Script
*
* Although you can set the script language of any Lurch Object,
* it is designed to be used with Lurch Scripts,
* as specified in the description of isScript().
* You can set this object's script language with this function,
* passing it any string, but currently only the language
* "ECMAScript" is supported; Python may be coming soon.
* This changes the "script language" attribute (content dictionary LurchCore).
*
* This routine does nothing if this Lob is read-only (not isEditable()).
*
* This is tested in test_script::test_setters().
*
* \see scriptLanguage()
*/
void setScriptLanguage ( QString language );
/** \brief Assuming the Lob wraps a Lurch Script, get the script type attribute
*
* For instance, in the Lurch Script object whose XML representation is given in the
* description of the isScript() member, scriptType() would return
* just the QString "auto-run".
*
* If the wrapped Lob is not a Lurch Script, the empty QString() is returned.
*
* This is tested by test_func::test_all().
*
* \see isScript(), scriptCode(), scriptLanguage()
*/
QString scriptType () const;
/** \brief Set the script type of this Lurch Object, as if it is a Lurch Script
*
* Although you can set the script type of any Lurch Object,
* it is designed to be used with Lurch Scripts,
* as specified in the description of isScript().
* You can set this object's script type with this function,
* passing it any string. See LurchEnvironment for how these values are used.
* This changes the "script type" attribute (content dictionary LurchCore).
*
* This routine does nothing if this Lob is read-only (not isEditable()).
*
* This is tested in test_script::test_setters().
*
* \see scriptType()
*/
void setScriptType ( QString type );
/** \brief Whether this Lob wraps an OmBindingType node that binds the given variable
*
* \param var the name of the variable in question
* \return true if and only if isBinding() and one of the bound variables is
* \a var
*
* This routine simply constructs a variable Lob from \a var and calls binds(Lob).
*
* This routine is tested by test_bind::test_basics().
*/
bool binds ( QString var ) const;
/** \brief Whether the named variable occurs free in this Lob
*
* Simply checks to see if \a var is on the list freeVariableNames(\a context).
*
* \see occursFree(Lob,Lob)
*
* This routine is tested in test_bind::test_all_free().
*/
bool occursFree ( QString var, Lob context = Lob() ) const;
/** \brief All free occurrences of the named variable inside this Lob
*
* \param var the name of the variable in question
* \param context Used when computing freeness/boundness, as in boundVariables()
*
* This routine simply constructs a variable Lob from \a var and calls
* freeOccurrences(Lob).
*
* This routine is tested by test_bind::test_all_free().
*/
QVariantList freeOccurrences ( QString var, Lob context = Lob() ) const;
/** \brief Replace every free occurrence of \a var in this Lob with \a term, in place
*
* \param var the name of the variable in question
* \param term the term that will be used to substitute in
* for free occurrences of \a var
* \param context the context in which freeness is computed
*
* This routine simply constructs a variable Lob from \a var and calls
* freeSubstitute(Lob,Lob,Lob).
*
* This routine is tested by test_bind::test_substitution().
*/
void freeSubstitute ( QString var, Lob term, Lob context = Lob() );
/** \brief Rename all instances of a variable bound by this Lob, if it is a quantifier
*
* \param var the name of the variable to replace
* \param newvar the name of the variable with which to replace it
*
* This routine simply constructs a variable Lob from \a var and another from \a newvar
* and calls renameBound(Lob,Lob).
*
* This routine is tested by test_bind::test_substitution().
*/
void renameBound ( QString var, QString newvar );
/** \brief Prepares this object to emit the modified() signal accurately
*
* This registers this Lob as one that should be checked when any Lob makes any
* modifications to any OpenMath object. Thus when one of this Lob's children is
* altered, the Lob that does the modification will notice that it was a child
* (or grandchild, etc.) of this Lob, and tell this Lob to emit the modified() signal.
* To prepare for such lookups, call this function.
*
* Read the description of modified() for more information.
*
* This routine is tested in test_lob_edit::test_read_only().
*
* \see modified(), watchForModifications
*/
void prepareModifiedSignal ();
/** \brief Creates a new Lob wrapping a new OpenMath object from the XML code in the file
*
* Reads the OpenMath XML representation in the given file and creates an OpenMath
* node from it. This node is wrapped in a new Lob, which is
* returned. If any step fails, a new empty Lob is returned. The file is closed
* again at the end of this routine if and only if it was closed when the routine
* was called.
*
* If you pass a nonzero parameter to \a message, this function will store any
* error it encounters in the string to which that pointer points. Ignoring this
* optional parameter will not provide error information, but can be convenient.
*
* Note that whatever nodes are represented in the file will be wrapped in a node of
* unknown type, so that just one node can be returned.
* Even if the file contains the XML representation of only an OpenMath integer, that
* integer will be wraped in a dummy node (type OmUnknownType) before being wrapped in
* the Lob, for consistency's sake. Read more on \link openmath my page of notes about
* OpenMath\endlink, but also note this important C++ pitfall:
* Because of the order of destruction of global/static objects in C++, it is imperative
* not to declare a Lob in the following way.
* \code
* static Lob myLob = Lob::fromXML( "<OMSTR>example</OMSTR>" ).child(); // BAD!!!
* \endcode
* That will occasionally cause a segmentation fault on application exit, on some platforms.
* The reason, I believe, is that the memory allocator sometimes cleans up the global
* object that is the parent before cleaning up the global object that is the child. Instead,
* always do this:
* \code
* static Lob myLob = Lob::fromXML( "<OMSTR>example</OMSTR>" ).child().copy(); // safe
* \endcode
* Note that this is only for static/global objects. Objects declared on the stack are
* cleaned up appropriately at the end of scope, and objects declared on the heap and
* correctly memory managed in the usual way do not cause problems.
*
* This is tested by test_lob::test_qfile_qstring().
*
* \see empty, save(), fromXML(QString)
*/
static Lob fromXML ( QFile& in, QString* message = 0 );
/** \brief Creates a new Lob wrapping a new OpenMath object from the XML code given
*
* Reads the OpenMath XML representation in the given string and creates an OpenMath
* node from it. This node is wrapped in a new Lob, which is
* returned. If any step fails, a new empty Lob is returned.
*
* The notes under fromXML(QFile&) about wrapping in dummy nodes apply here as well.
* Note particularly those about causing segmentation faults by doing so in the wrong context.
*
* This is tested by test_lob::test_qfile_qstring().
*
* \see empty, toString(), fromXML(QFile&)
*/
static Lob fromXML ( QString xml );
/** \brief Creates a new OpenMath object and a new Lob to wrap it, from the given datum
*
* The result will be a Lob that returns true for isBasicType(), according to the
* following table. However, if the given QVariant is not of one of the listed types,
* then the resulting Lob is empty, and thus does not pass isBasicType().
* <table border=1>
* <tr><th>QVariant given</th> <th>Create an OpenMath object of this type</th></tr>
* <tr><td>QVariant::ByteArray</td><td>OmByteArrayType</td></tr>
* <tr><td>QVariant::Double</td> <td>OmFloatType</td></tr>
* <tr><td>QVariant::Int</td> <td>OmIntegerType</td></tr>
* <tr><td>QVariant::LongLong</td> <td>OmBigIntegerType</td></tr>
* <tr><td>QVariant::String</td> <td>OmStringType</td></tr>
* <tr><td>QVariant::UInt</td> <td>OmBigIntegerType</td></tr>
* <tr><td>QVariant::ULongLong</td><td>OmBigIntegerType</td></tr>
* </table>
*
* This is tested in test_lob::test_conversions().
*
* \see isBasicType()
*/
static Lob fromVariant ( QVariant v );
/** \brief Whether the given string contains a Lurch URN
*
* A basic Lurch URN is of the form
* <code>urn:publicid:-:lurch.sourceforge.net:Y+by+X:en:version</code>,
* with X and Y replaced by document title and author email respectively,
* after each has been percent-encoded (see QUrl::percentEncoding()).
* Here is an example.<br>
* <code
* >urn:publicid:-:lurch.sourceforge.net:Basic%20Document+by+jim%40foo.com:en:version
* </code>
*
* This routine verifies that the string splits into five parts with the following
* properties.
* <ol>
* <li>one exactly equal to <code>urn:publicid:-:lurch.sourceforge.net:</code></li>
* <li>the next any nonempty sequence of alphanumerics and dashes, dots, underscores,
* tildes, and percents</li>
* <li>the next exactly equal to <code>+by+</code></li>
* <li>the next any nonempty sequence of alphanumerics plus the same extra characters
* as before</li>
* <li>the last of the form <code>:L:V</code>,
* where L is any two-or-more-letter code (language), and V is any sequence of
* alphanumerics and dots (version)</li>
* </ol>
*
* There is one enhancement to this basic concept; we also define a string of the form
* <code>identifier=urn</code> to be a Lurch URN, if <code>urn</code> is a basic Lurch
* URN, as given above, and <code>identifier</code> is a sequence of alphanumerics
* and underscores that begins with a letter. This is useful in some applications
* (e.g. LurchEnvironment) in giving names to URNs, as in<br>
* <code
* >myAlias_1=urn:publicid:-:lurch.sourceforge.net:Document+by+Jane:en:1.0
* </code>
*
* For fetching the data out of such an URN, see splitLurchURN().
*
* This is tested in test_doc::test_urns().
*/
static bool isLurchURN ( QString testThis );
/** \brief Splits a Lurch URN into its contstituent data
*
* A Lurch URN contains a document title, author, language (optional), and version
* (optional). It may or may not be preceded by an identifier.
* This function splits a valid Lurch URN into these constituent parts,
* and returns them, precent-decoded (see QUrl::fromPercentEncoding()).
*
* \return a QStringList with four or five entries, in which entries three and four
* may be empty strings if the optional language and/or version are not present.
* The fifth component will be present if and only if the Lurch URN came
* preceded by an identifier, in which case it will be that identifier.
* The result will be a completely empty QStringList if the
* given string is not a valid Lurch URN (see isLurchURN()).
*
* This is tested in test_doc::test_urns().
*
* \see isLurchURN()
*/
static QStringList splitLurchURN ( QString splitThis );
/** \brief Create a Lurch URN from author, title, language, and version information
*
* \param title Document title (any string)
* \param author Author's email address (any string with no spaces in it)
* \param language Language code for document content (e.g., "en") (may be left blank)
* \param version Version number (alphanumerics plus periods, may be left blank)
* \param identifier Optional identifier to precede URN, as documented in isLurchURN()
*
* Author email and document title are converted using QUrl::toPercentEncoding().
* No checks are done for whether the parameters fit the requirements just given,
* but if they do not, the resulting string may not pass isLurchURN().
* Optional identifier is only placed at the front of the result if it is nonempty;
* in such a case, the equals sign is inserted and should not be included in the
* \a identifier parameter itself.
*
* This is tested in test_doc::test_urns().
*
* \see isLurchURN(), splitLurchURN()
*/
static QString makeLurchURN ( QString title, QString author,
QString language = QString(), QString version = QString(),
QString identifier = QString() );
/** \brief Remove the optional identifier from in front of a Lurch URN
*
* If \a urn is a Lurch URN preceded by an identifier, as in
* <code>identifier=urn</code>, as described in the documentation for isLurchURN(),
* this function strips off the <code>identifier=</code> part,
* returning just the basic URN. If \a urn has no identifier, it is returned as is.
*/
static QString basicLurchURN ( QString urn );
/** \brief Return the optional identifier preceding an URN, if there is one
*
* If \a urn is a Lurch URN preceded by an identifier, as in
* <code>identifier=urn</code>, as described in the documentation for isLurchURN(),
* this function returns the identifier.
* If \a urn has no identifier, an empty string is returned.
*/
static QString identifierOfURN ( QString urn );
/** \brief Creates a new Lob that satisfies isDocument() and has the given URN
*
* \param urn Should be a basic Lurch URN, as described in isLurchURN().
* If omitted, an URN will be created, with title "New Document",
* author "unknown", language empty, and version "0".
* \param dependencies A list of dependency URNs, which may or may not be basic.
* If omitted, an empty list of dependencies will be used.
*/
static Lob newDocument ( QString urn = QString(),
QStringList dependencies = QStringList() );
signals:
/** \brief Emitted when part of this Lob is modified (read caveats about this signal
* below)
*
* Because a Lob is a wrapper for an OpenMath tree, which does not emit signals when it
* is modified, the Lob class must jump through some hoops to implement this signal's
* emission reliably. Thus you as a client must ensure two conditions hold in order
* for this signal to behave as documented below.
* <ol>
* <li>Ensure that in any Lob in which you wish to use this signal, you call
* prepareModifiedSignal(). (Refer to its documentation for more
* information.)</li>
* <li>Ensure that all edits you perform on OpenMath trees are done through calls to
* member functions of the Lob class. Although you may set up an OpenMath tree
* in another way, after handing it to a Lob and asking it to watch for edits
* (and emit this signal when it sees them), restrict your editing to only
* calling functions in the Lob class.</li>
* </ol>
* If you ensure both of these conditions, then this signal will be emitted whenever
* this Lob's node, any of its children (or grandchildren, and so on), or any of
* their attributes (and so on recursively) are modified in any way.
* For details about the parameter \a change, see the LobChange class documentation.
*
* This signal is tested in test_lob_edit::test_read_only().
*/
void modified ( const LobChange& change );
public slots:
/** \brief Create a deep copy of the Lob, by copying the entire wrapped OpenMath tree
*
* While the copy constructor (or the assignment operator) just copies the wrapper
* object (the Lob), this function also copies the
* internal OpenMath object. Note the consequences of the following code.
* \code
* Lob A = Lob::fromXML( "<OMI>5</OMI>" ); // A == Unknown(Integer 5)
* A = A.child().copy(); // A == Integer 5
* // at this point in the script, the original tree Unknown(Integer 5) is deleted,
* // because there is no way to reference it with Lobs, so memory management nixes it
* \endcode
*
* A deep copy of any node is editable, even if the original was read-only (the
* opposite of editable).
*
* \param changeIDs If this is true (which is the default, if you omit the parameter)
* then after the copy is made, changeIDs() will be called on it,
* with context equal to this Lob. This is useful if you intend to
* later (possibly after modifications) insert the copy into the same
* context as the original, and do not want ID conflicts. If you
* want all IDs preserved in your copy, or wish to call changeIDs()
* yourself with a different context, you can set this parameter to
* false. Read more details about IDs on the
* <a href='idsystem.html'>Lurch ID System page</a>.
*
* This is tested by test_lob::test_copies().
*
* \see copy()
*/
Lob copy ( bool changeIDs = true ) const;
/** \brief Check whether two wrapped OpenMath objects have exactly the same structure
*
* This is different from the equality test in operator==(), in that it does not care if
* the two wrapped objects are <i>the same object in memory</i>, which is what the
* operator tests, but rather this tests for structural equivalence. So two different
* OpenMath trees that have the same nodes in the same arrangement are equivalent,
* but are not operator==().
*
* \param other The Lob to which to compare this object
* \param compareAttributes If this flag is set to true, then the two OpenMath trees are
* checked not only for identical tree structures, but also that
* each node's attributes also match those of the other node.
* Attribute order is only relevant in that later occurrences
* of the same attribute supercede earlier ones; order is
* irrelevant between differently-keyed attributes.
* Values are, of course, compared with equivalent().
* \param exclusions When present, this is a list of name/content dictionary pairs that
* should not be part of the attribute comparison. The exclusions list
* should be a name, its content dictionary, the next name, its content
* dictionary, and so on. For instance, if you wish to compare two
* OpenMath nodes, including all their attributes except their IDs and
* script languages, you might call <code>equivalent( A, B, true,
* QStringList() << "ID" << "LurchCore" << "script language"
* << "LurchCore" )</code>. This parameter is obviously only
* relevant when \a compareAttributes is true.
*
* This routine simply calls equivalent(const OmNode*,const OmNode*,bool,QStringList).
*
* Script writers should be aware of the convenience function "exclusions" described
* in the documentation for addLobsToScriptEngine().
*
* This class is tested by test_lob::test_equivalence(),
* test_lob::test_attributes(), and test_ref::test_exclusion().
*
* \see operator==(), operator!=()
*/
bool equivalentTo ( const Lob& other, bool compareAttributes = false,
QStringList exclusions = QStringList() ) const;
/** \brief Creates a string representation of the node in the XML OpenMath encoding
*
* This is tested by test_lob::test_qfile_qstring().
*
* \see fromXML(QString), save()
*/
QString toString () const;
/** \brief Find the address of this Lob (in an optional parent or container)
*
* A Lob's address is the path from the root of the OpenMath tree to the Lob.
* See the LobAddress class for more information. This routine computes the address
* of this Lob inside a given root, regardless of how many ancestor steps (of any
* type, parent or container) need to be made to get there.
*
* Note that if the optional \a root is not provided, this routine uses its outermost
* parent/container/ancestor as the root. The same behavior holds if the given \a root
* is empty.
*
* If this Lob is not actually a descendant of the root in question, an empty
* LobAddress is returned. This same behavior takes place if you ask for the address
* of a Lob within itself, so take care to distinguish the two in the calling routine.
*/
LobAddress address ( Lob inThis = Lob() ) const;
/** \brief Use a LobAddress to find a descendant of this Lob
*
* A LobAddress class specifies the path from one node in an OpenMath tree downwards,
* through children and/or attributes, to a descendant node. (See the documentation
* for LobAddress for full details.)
*
* This routine takes an \a address and follows that path from this Lob until its end,
* and returns the Lob found there if there is one, or an empty Lob if the address
* turns out to be invalid (i.e., contain steps that are not possible in the tree
* under this Lob).
*
* The \a offset parameter is largely for internal use during recursion, but callers
* may use it; if it is nonzero, the routine only pays attention to the steps in the
* address beginning with \a offset. For example, if \a offset == 2, then the first
* two steps of the address are ignored, and the path from steps 2 through the end
* is used instead.
*/
Lob index ( LobAddress address, unsigned int offset = 0 );
/** \brief Whether the Lurch Object wraps an OpenMath object of a basic type
*
* The following OpenMath types from the C++ library represent OpenMath basic types.
* This function returns true if this object is not empty (Lob::empty) and its type is
* one of these types.
* <ul>
* <li>OmIntegerType</li>
* <li>OmBigIntegerType</li>
* <li>OmFloatType</li>
* <li>OmByteArrayType</li>
* <li>OmVariableType</li>
* <li>OmStringType</li>
* <li>OmWStringType</li>
* <li>OmSymbolType</li>
* </ul>
*
* This is tested in test_lob::test_conversions().
*
* \see toVariant()
*/
bool isBasicType () const;
/** \brief Number of children of the wrapped OpenMath node
*
* This will be zero for a final (leaf) node, and for an empty application node.
* It is zero if this Lob is empty. It will be nonzero otherwise.
*
* This is tested by test_lob::test_tree().
*
* \see empty
*/
unsigned int numChildren () const;
/** \brief Creates a new Lob wrapping the chosen child
*
* \param index The index of the desired child. It defaults to zero, meaning the first
* child. Any value up to but not including numChildren() is a valid value.
* \return A new Lob in any case, wrapping the corresponding child if the index is
* less than numChildren(). Otherwise, the new Lob returned is empty.
*
* If this Lob is not editable, then the returned child Lob will not be either.
*
* This is tested by test_lob::test_tree().
*
* \see empty, numChildren()
*/
Lob child ( unsigned int index = 0 ) const;
/** \brief Whether the wrapped OpenMath object has a parent (is another node's child)
*
* This is tested by test_lob::test_tree().
*
* \see parent(), isDocument(), hasContainer()
*/
bool hasParent () const;
/** \brief Creates a new Lob wrapping the parent of this one
*
* If this object wraps a node that is no other node's child, then this will return an
* empty Lob, meaning there is no parent. You can check this first with hasParent().
*
* If this Lob is not editable, then the returned parent Lob will not be either.
*
* This is tested by test_lob::test_tree().
*
* \see empty, document
*/
Lob parent () const;
/** \brief Whether the wrapped OpenMath object has a container (is an attribute key/value)
*
* If a node N is attributed by the symbol/node (key/value) pair (K,V), then we say
* that N is the "container" of K and also of V. So <code>K.container()</code> and
* <code>V.container()</code> should both be N. This is distinct from the parent()
* function, which is used for parent/child relationships.
*
* This is tested by test_lob::test_tree().
*
* \see container(), isDocument(), hasParent()
*/
bool hasContainer () const;
/** \brief Creates a new Lob wrapping the container of this one
*
* If this object wraps a node that is not used as a key or value to attribute another
* node, then this will return an empty Lob, meaning there is no container.
* So "container" here means "the node which this node is attributing."
* You can check this first with hasContainer().
*
* If this Lob is not editable, then the returned container Lob will not be either.
*
* This is tested by test_lob::test_tree().
*
* \see empty, document
*/
Lob container () const;
/** Whether \a maybeAncestor is an ancestor of this Lob
*
* This check involves a walk upward through both parent() and container() relationships,
* searching for \a maybeAncestor. It returns true iff \a maybeAncestor is found before
* reaching a Lob with no parent/container. A Lob counts as its own ancestor.
*
* This is used internally for guarding against creating non-trees
* in routines such as addChild(), insertChild(), insertPreviousSibling(), and
* insertNextSibling(). You can use it externally for other things, of course.
*/
bool hasAsAncestor ( const Lob maybeAncestor ) const;
/** \brief Whether the wrapped OpenMath object has a next sibling
*
* If this object is empty or is no other node's child, this defaults to false.
* Otherwise it is determined by
* the position of this object within the children array of its parent.
* To get the next sibling, call nextSibling().
*
* This is tested by test_lob::test_tree().
*
* \see empty, nextSibling()
*/
bool hasNextSibling () const;
/** \brief Create a new Lob wrapping the next sibling of this one
*
* If this object has no next sibling, then this returns an empty Lob.
* You can check this first by calling hasNextSibling().
*
* If this Lob is not editable, then the returned sibling Lob will not be either.
*
* This is tested by test_lob::test_tree().
*
* \see hasNextSibling()
*/
Lob nextSibling () const;
/** \brief Whether the wrapped OpenMath object has a previous sibling
*
* If this object is empty or is no other node's child, this defaults to false.
* Otherwise it is determined by
* the position of this object within the children array of its parent.
* To get the previous sibling, call previousSibling().
*
* This is tested by test_lob::test_tree().
*
* \see empty, previousSibling()
*/
bool hasPreviousSibling () const;
/** \brief Create a new Lob wrapping the previous sibling of this one
*
* If this object has no previous sibling, then this returns an empty Lob.
* You can check this first by calling hasPreviousSibling().
*
* If this Lob is not editable, then the returned sibling Lob will not be either.
*
* This is tested by test_lob::test_tree().
*
* \see hasPreviousSibling()
*/
Lob previousSibling () const;
/** \brief Convenience function that calls hasAttribute(QString,QString)
*
* This function lifts out the symbol's name and content dictionary,
* then calls hasAttribute(QString,QString) with those values.
* This is the function exposed to script writers, who usually obtain attribute keys
* as symbol Lobs, perhaps using attributeKeys().
* If \a symbolLob is not a Lob that wraps an OmSymbolNode, this function returns false.
*
* \see hasAttribute(QString,QString)
*/
bool hasAttribute ( Lob symbolLob ) const;
/** \brief Convenience function that calls attribute(QString,QString)
*
* This function lifts out the symbol's name and content dictionary,
* then calls attribute(QString,QString) with those values.
* This is the function exposed to script writers, who usually obtain attribute keys
* as symbol Lobs, perhaps using attributeKeys().
* If \a symbolLob is not a Lob that wraps an OmSymbolNode, this function returns an
* empty Lob.
*
* \see attribute(QString,QString)
*/
Lob attribute ( const Lob& symbolLob ) const;
/** \brief Convenience function that calls setAttribute(QString,QString,Lob)
*
* This function lifts out the symbol's (key's) name and content dictionary,
* then calls setAttribute(QString,QString,Lob) with those values.
* This is the function exposed to script writers, who usually obtain attribute keys
* as symbol Lobs, perhaps using attributeKeys().
* If \a key is not a Lob that wraps an OmSymbolNode, this function does nothing.
*
* \see setAttribute(QString,QString,Lob)
*/
void setAttribute ( Lob key, Lob value );
/** \brief Convenience function that calls removeAttribute(QString,QString)
*
* This function lifts out the symbol's name and content dictionary,
* then calls removeAttribute(QString,QString) with those values.
* This is the function exposed to script writers, who usually obtain attribute keys
* as symbol Lobs, perhaps using attributeKeys().
* If \a symbolLob is not a Lob that wraps an OmSymbolNode, this function does nothing.
*
* \see removeAttribute(QString,QString)
*/
void removeAttribute ( Lob symbolLob );
/** \brief Changes the current Lob (in position in a tree) to the given Lob
*
* This is just a convenience function that calls set(OmNode*) with the wrapped
* OmNode from \a s as its parameter. Refer to the documentation for that function
* for full details.
*
* \see set(OmNode*)
*/
void set ( Lob s );
/** \brief Remove this Lob from its parent or container
*
* If this Lob is a child of another Lob, then it will be deparented as if
* <code>parent().removeChild( i )</code> were called,
* for the appropriate value of <code>i</code>.
*
* If this Lob is an attribute key or value of another Lob,
* then it will be removed from the attributed Lob by removing the entire
* key-value pair in which it sits.
*
* If the Lob satisfies neither hasParent() nor hasContainer(), this function
* does nothing.
*/
void remove ();
/** \brief Inserts the given Lob as a child of the current one
*
* After this routine, <code>child(index)</code> will give a Lob equal (==) to
* \a newChild.
* Exceptions include if this Lob is empty, or a final Lob, so that it may not have
* children (e.g., an integer or a symbol), or if it has type OmUnknownType,
* or if \a index is larger than the current
* number of children.
* Further exceptions include if \a newChild is empty or of type OmUnknownType,
* or an ancestor of this Lob.
* In those cases, this routine does nothing.
*
* This routine also does nothing if this Lob is read-only (not isEditable()).
*
* If \a newChild had a parent before this routine was called, then this routine will
* reparent it using the routine Lob::set(Lob).
*
* \see removeChild(), addChild()
*/
void insertChild ( uint index, Lob newChild );
/** \brief Appends the given Lob as this Lob's new last child
*
* After this routine, <code>child(numChildren()-1)</code> will give a Lob equal (==) to
* \a newChild.
* Exceptions include if this Lob is empty or a final Lob, so that it may not have
* children (e.g., an integer or a symbol), or when it has type OmUnknownType.
* Further exceptions include if \a newChild is empty or of type OmUnknownType,
* or an ancestor of this Lob.
* In those cases, this routine does nothing.
*
* This routine also does nothing if this Lob is read-only (not isEditable()).
*
* If \a newChild had a parent before this routine was called, then this routine will
* reparent it using the routine Lob::set(Lob).
*
* \see removeChild(), addChild()
*/
void addChild ( Lob newChild );
/** \brief Removes the specified child
*
* After this routine, the node that had been <code>child(index)</code> will be
* deparented from this one, and deleted if and only if no other Lobs wrap it or any
* other nodes to which it is connected. This node will have one fewer child.
*
* If \a index is greater than or equal to <code>numChildren()</code>, this routine
* does nothing.
*
* This routine also does nothing if this Lob is read-only (not isEditable()).
*
* \see insertChild(), addChild()
*/
void removeChild ( uint index = 0 );
/** \brief Inserts the given Lob after this one in the parent's list of children
*
* After this routine, <code>nextSibling()</code> will give a Lob equal (==) to \a s.
* There are two exceptions.
* If this Lob is not any other Lob's child, in which case the
* notion of siblings is meaningless, then this routine does nothing. This happens when
* <code>!hasParent()</code> and when this node is one of its parent's attributes, as
* opposed to one of its parent's children.
* Also, if the parent is of type OmUnknownType, one cannot append children to it, so
* this routine does nothing.
*
* This routine also does nothing if this Lob is read-only (not isEditable()),
* or if \a newSibling is one of its ancestors.
*
* If \a newSibling had a parent before being assigned, then this routine will reparent
* it using the routine Lob::set(Lob).
*
* \see insertPreviousSibling(), removeNextSibling(), removePreviousSibling()
*/
void insertNextSibling ( Lob newSibling );
/** \brief Inserts the given Lob before this one in the parent's list of children
*
* After this routine, <code>previousSibling()</code> will give a Lob equal (==) to \a s.
* There are two exceptions.
* If this Lob is not any other Lob's child, in which case the
* notion of siblings is meaningless, then this routine does nothing. This happens when
* <code>!hasParent()</code> and when this node is one of its parent's attributes, as
* opposed to one of its parent's children.
* Also, if the parent is of type OmUnknownType, one cannot append children to it, so
* this routine does nothing.
*
* This routine also does nothing if this Lob is read-only (not isEditable()),
* or if \a newSibling is one of its ancestors.
*
* If \a newSibling had a parent before being assigned, then this routine will reparent
* it using the routine Lob::set(Lob).
*
* \see insertNextSibling(), removeNextSibling(), removePreviousSibling()
*/
void insertPreviousSibling ( Lob newSibling );
/** \brief Removes the Lob after this one in the parent's list of children
*
* After this routine, there will be one fewer child to this Lob's parent, and the
* missing one will be what was <code>nextSibling()</code> before this routine was
* called.
* There are a few exceptions. If this Lob is not any other Lob's child,
* in which case the notion of siblings is meaningless, then this routine does nothing.
* This happens when <code>!hasParent()</code> and when this node is one of its parent's
* attributes, as opposed to one of its parent's children.
* The routine also does nothing if <code>!hasNextSibling()</code>.
*
* This routine also does nothing if this Lob is read-only (not isEditable()).
*
* \see insertNextSibling(), removePreviousSibling()
*/
void removeNextSibling ();
/** \brief Removes the Lob before this one in the parent's list of children
*
* After this routine, there will be one fewer child to this Lob's parent, and the
* missing one will be what was <code>previousSibling()</code> before this routine was
* called.
* There are a few exceptions. If this Lob is not any other Lob's child,
* in which case the notion of siblings is meaningless, then this routine does nothing.
* This happens when <code>!hasParent()</code> and when this node is one of its parent's
* attributes, as opposed to one of its parent's children.
* The routine also does nothing if <code>!hasPreviousSibling()</code>.
*
* This routine also does nothing if this Lob is read-only (not isEditable()).
*
* \see insertPreviousSibling(), removeNextSibling()
*/
void removePreviousSibling ();
/** \brief How many comments are attached to this Lob
*
* A comment is attached to a Lob if it precedes it in the XML encoding of the
* Lob. For instance, the following XML describes a Lob whose first child has
* one comment and whose second child has two.
* <pre>
* <OMA>
* <!-- This comment modifies the addition symbol. -->
* <OMS name="addition" cd="arithmetic"/>
* <!-- This comment modifies the number one. -->
* <!-- So does this one; no comment modifies the two. -->
* <OMI>1</OMI>
* <OMI>2</OMI>
* </OMA>
* </pre>
* The comments that precede a Lob are stored in it, and this function returns how
* many there are, zero or more.
*/
unsigned int numComments ();
/** \brief The comment at the given index, returned as a string
*
* See numComments() for a description of how XML comments are associated with (and
* stored in) the Lobs that follow them.
*
* This function returns a string containing the <i>i</i><sup>th</sup> comment for
* this Lob, or an empty string if the Lob is empty or \a i is greater than or equal
* to the number of comments the Lob contains. (Zero-based indexing.)
* Any leading and trailing space will be removed from the string before it is returned.
* (See documentation for changeComment() for details on why.)
*/
QString comment ( unsigned int i );
/** \brief Delete the comment at the given index
*
* See numComments() for a description of how XML comments are associated with (and
* stored in) the Lobs that follow them.
*
* This function deletes the <i>i</i><sup>th</sup> comment for this Lob, or does
* nothing if the Lob is empty or \a i is greater than or equal
* to the number of comments the Lob contains. (Zero-based indexing.)
*/
void removeComment ( unsigned int i );
/** \brief Replace the comment at the given index with the given text
*
* See numComments() for a description of how XML comments are associated with (and
* stored in) the Lobs that follow them.
*
* This function overwrites the <i>i</i><sup>th</sup> comment for this Lob with
* \a text, or does nothing if the Lob is empty or \a i is greater than or equal
* to the number of comments the Lob contains. (Zero-based indexing.)
*
* To every \a text parameter is prepended and appended one space character,
* because otherwise the INRIA OM library does not correctly handle comments.
* For instance, if you set a comment as "foo" it creates XML code for that comment
* as <code><!--foo--></code>, which it cannot then read back in.
* Note how this works sensibly together with the removal of leading and trailing
* space in routines such as comment().
*/
void changeComment ( unsigned int i, QString text );
/** \brief Insert a new comment before the comment at the given index
*
* See numComments() for a description of how XML comments are associated with (and
* stored in) the Lobs that follow them.
*
* This function inserts a new comment as the <i>i</i><sup>th</sup> comment for
* this Lob, bumping the former <i>i</i><sup>th</sup> comment and all later ones
* to one-higher indexes. This routine does nothing if the Lob is empty or \a i is
* greater than to the number of comments the Lob contains. (Zero-based indexing.)
* If \a i equals the number of comments before this routine is executed, then its
* effect is the same as that of appendComment().
*
* To every \a text parameter is prepended and appended one space character,
* because otherwise the INRIA OM library does not correctly handle comments.
* For instance, if you set a comment as "foo" it creates XML code for that comment
* as <code><!--foo--></code>, which it cannot then read back in.
* Note how this works sensibly together with the removal of leading and trailing
* space in routines such as comment().
*/
void insertComment ( unsigned int i, QString text );
/** \brief Add a new comment to the end of this Lob's comment list
*
* See numComments() for a description of how XML comments are associated with (and
* stored in) the Lobs that follow them.
*
* This function adds the given text as a new comment with index equal to the former
* number of comments. This function does nothing if this Lob isEmpty().
*
* To every \a text parameter is prepended and appended one space character,
* because otherwise the INRIA OM library does not correctly handle comments.
* For instance, if you set a comment as "foo" it creates XML code for that comment
* as <code><!--foo--></code>, which it cannot then read back in.
* Note how this works sensibly together with the removal of leading and trailing
* space in routines such as comment().
*/
void appendComment ( QString text );
/** \brief This Lob's comments, returned all together as one list
*
* See numComments() for a description of how XML comments are associated with (and
* stored in) the Lobs that follow them.
*
* This function returns the comments for this Lob in the same order that they would
* be queried using comment(). For example, the text of all comments could be assembled
* together into paragraphs easily using <code>comments().join( "\n" );</code>.
* Any leading and trailing space will be removed from each comment before the list
* is returned, as in comment().
*/
QStringList comments ();
/** \brief Replace the full list of this Lob's comments with a new list
*
* See numComments() for a description of how XML comments are associated with (and
* stored in) the Lobs that follow them.
*
* This function does nothing if this Lob isEmpty(). Otherwise, it begins by erasing
* all existing comments, and then proceeds to append all comments in \a all, in order,
* so that when this routine is finished, commeent(i) == all[i] for each index i.
*
* To every element of \a all is prepended and appended one space character,
* because otherwise the INRIA OM library does not correctly handle comments.
* For instance, if you set a comment as "foo" it creates XML code for that comment
* as <code><!--foo--></code>, which it cannot then read back in.
* Note how this works sensibly together with the removal of leading and trailing
* space in routines such as comment().
*/
void setComments ( QStringList all );
/** \brief Whether the wrapped object fits the definition of a Lurch Document
*
* The OpenMath node is a Lurch Document if and only if the following criteria all hold.
* <ol>
* <li>The Lob is nonempty, and its node has no parent.</li>
* <li>The node is of type OmApplicationType and its first child is the symbol
* with name "document" in content dictionary "LurchCore".</li>
* <li>It has an attribute whose key is
* <code><OMS name="author" cd="LurchCore"/></code> and whose value is an
* OmStringNode that is nonempty (the author, usually as an email address).</li>
* <li>It has an attribute whose key is
* <code><OMS name="title" cd="LurchCore"/></code> and whose value is an
* OmStringNode that is nonempty (the document's title).</li>
* <li>It has an attribute whose key is
* <code><OMS name="dependencies" cd="LurchCore"/></code> and whose value is an
* OmStringNode that is a list of URNs separated by spaces (those documents on
* which this one depends). Each of these URNs must satisfy isLurchURN().</li>
* </ol>
*
* Lurch Documents may also have the optional attribute "version" (with cd LurchCore).
* If this option is present, it should be a string of alphanumerics and dots.
* But this requirement is not checked by isDocument().
*
* This is tested in test_doc::test_isdoc().
*/
bool isDocument () const;
/** \brief Whether the wrapped OpenMath object is a Lurch Script
*
* An OpenMath object can be interpreted as a Lurch Script if all the following
* criteria are met.
* <ul>
* <li>It is an OmStringNode.</li>
* <li>It has an attribute keyed by the symbol named "script language" in content
* dictionary "LurchCore" whose value is a nonempty OmStringNode.</li>
* </ul>
* It may optionally have an attribute keyed by the symbol named "script type" in
* content dictionary "LurchCore" whose value is a string node containing information
* about how this script is to be used; see LurchEnvironment for details on the use
* of this attribute.
*
* The script itself should be a string containing any code in the scripting language
* specified in the "script language" attribute. For instance, here is a valid Lurch
* Script OpenMath object.
* <pre>
* <OMATTR>
* <OMATP>
* <OMS name="script language" cd="LurchCore"/>
* <OMSTR>Javascript</OMSTR>
* <OMS name="script type" cd="LurchCore"/>
* <OMSTR>auto-run</OMSTR>
* </OMATP>
* <OMSTR>function myFunction ( a, b ) {
* c = a + b;
* d = 2 * c;
* return d;
* }
* myFunction(7,10);</OMSTR>
* </OMATTR>
* </pre>
*
* This is tested by test_func::test_all().
*
* \see scriptCode(), scriptLanguage(), scriptType()
*/
bool isScript () const;
/** \brief Whether any subtree of this Lob is a script of the given type
*
* \param type If provided, then only scripts whose scriptType() equals this parameter
* are considered; others will not cause a return value of true.
* \return True if and only if there is some descendant of this Lob that passes the
* isScript() test and whose scriptType() equals \a type. (This latter check
* is only done if !type.isEmpty()).
*/
bool containsScript ( QString type = QString() ) const;
/** \brief Create a Lurch ID Reference to this object
*
* This returns a Lob that wraps a newly created OpenMath object, which is a Lurch ID
* Reference that refers to this Lob.
* To learn how Lurch ID References are formatted, see the documentation for
* isIDReference().
*
* For full information on the Lurch ID System, refer to the
* <a href='idsystem.html'>Lurch ID System page</a>.
*
* \see isIDReference(), referent()
*/
Lob reference ();
/** \brief Find the Lob that this object (treated as a Lurch ID Reference) references
*
* This returns the Lob to which this object refers, if there is one.
* This object must be a Lurch ID Reference (see isIDReference()) or the result will
* be an empty Lob. There must be a Lob with the ID this object references in the
* same OpenMath tree as this object. It returns the closest one, where "closest"
* means "has the closest common ancestor."
*
* For full information on the Lurch ID System, refer to the
* <a href='idsystem.html'>Lurch ID System page</a>.
*
* \see isIDReference(), reference()
*/
Lob referent () const;
/** \brief Whether this Lurch Object is a Lurch Reference to another Lurch Object's ID
*
* A Lurch Reference is a Lurch Object that wraps an OmStringNode containing the unique
* ID or nickname of some Lurch Object (the object being referenced). It must be one
* one of the following two types.
* <ul>
* <li><em>an ID reference</em>:
* the ID of the object being referenced is a sequence of decimal digits
* (the result of applying Lob::getID to the object being referened) preceded
* by the two characters "id". In this
* case this Lurch Reference object must have an attribute
* whose key is the symbol "reference" in content dictionary "LurchCore" and whose
* value is the OmStringNode containing the literal string "ID"</li>
* <li><em>a nickname reference</em>: the nickname of the object being referenced is
* any string which is interpreted as a nickname for that object. In this case
* this Lurch Reference object must have an attribute whose
* key is the symbol "reference" in content dictionary "LurchCore" and whose
* value is the OmStringNode containing the literal string "nickname"</li>
* </ul>
*
* \return true if and only if the object satisfies the criterion for an ID reference,
* the first of the two types mentioned above
*
* This is tested in test_ref::test_ids().
*
* \see getIDReference(), isNicknameReference(), reference(), referent()
*/
bool isIDReference () const;
/** \brief Whether this Lurch Object is a Lurch Reference to another Lurch Object's
* nickname
*
* A Lurch Reference is a Lurch Object that wraps an OmStringNode of one of two types,
* as documented in the isIDReference() function.
*
* \return true if and only if the object satisfies the criterion for a nickname
* reference, the second of the two types documented in the isIDReference()
* function
*
* This is tested in test_ref::test_nicknames().
*
* \see getNicknameReference(), getNickname(), isIDReference()
*/
bool isNicknameReference () const;
/** \brief Assuming this Lurch Object is an ID reference, this returns the ID referenced
*
* A Lob is an ID reference if it fits the description of such objects given in
* the documentation for the function isIDReference(). If it is one, then this
* function returns the ID referenced, as a string. Otherwise, it returns an
* empty string.
*
* This is tested in test_ref::test_ids().
*
* \see getNicknameReference(), isIDReference(), reference(), referent()
*/
QString getIDReference () const;
/** \brief Set the ID to which this Lurch Object refers, as if it is a Lurch ID Reference
*
* Although you can set the ID referent for any Lurch Object that wraps an OpenMath
* string node, it is designed to be used with Lurch ID References,
* as specified in the description of isIDReference().
* You can set this object's ID referent with this function, passing it any valid ID,
* which means any string of digits.
* This changes the contents of the wrapped string node, or does nothing if the
* wrapped object is not a string node or the given id is invalid.
*
* This routine also does nothing if this Lob is read-only (not isEditable()).
*
* This is tested in test_script::test_setters().
*
* \see getIDReference()
*/
void setIDReference ( QString id );
/** \brief Assuming this Lurch Object is a nickname reference, this returns the nickname
*
* A Lob is a nickname reference if it fits the description of such objects given
* in the documentation for the function isNicknameReference(). If it is one, then this
* function returns the nickname. If it is not one, then this function returns
* QString(), an invalid nickname.
*
* This is tested in test_ref::test_nicknames().
*
* \see getIDReference(), isNicknameReference(), getNickname()
*/
QString getNicknameReference() const;
/** \brief Set the nickname to which this Lurch Object refers,
* as if it is a Lurch Nickname Reference
*
* Although you can set the nickname referent for any Lurch Object that wraps an
* OpenMath string node, it is designed to be used with Lurch Nickname References,
* as specified in the description of isNicknameReference().
* You can set this object's nickname referent with this function, passing it any
* string. This changes the contents of the wrapped string node, or does nothing if
* the wrapped object is not a string node.
*
* This routine also does nothing if this Lob is read-only (not isEditable()).
*
* This is tested in test_script::test_setters().
*
* \see getNicknameReference()
*/
void setNicknameReference ( QString nickname );
/** \brief Instruct this Lob to act as a context for Lurch IDs
*
* The Lurch ID system is complex enough that it has its own page in the source code
* documentation. See <a href='idsystem.html'>The Lurch ID System page</a> for more
* information about what a context for IDs is.
*
* \param on Instruct this Lob to act as a context for IDs if and only if this parameter
* is true. Otherwise, instruct it to stop acting as an ID context.
*/
void setIDContext ( bool on = true );
/** \brief Look up this Lob's ID
*
* The ID is stored in the attribute whose key has name ID and content dictionary
* LurchCore.
* The attribute's value is an OmStringNode containing "id" followed by
* a sequence of decimal digits.
* (This is distinct from the OpenMath id attribute, which is for
* OpenMath References, which are for compacting documents for storage,
* and have no actual meaning for the mathematics represented by the document.)
* If the content of the specified attribute is not of this form,
* then the Lob is said to not have an ID, and this function will return an empty
* string, as if there were no ID attribute at all.
*
* The Lob class provides the public functions reference() and referent() for dealing
* with IDs; script authors only need to deal with those.
* For a detailed description of the Lurch ID System,
* see the <a href='idsystem.html'>Lurch ID System page</a>.
*
* \see setID(), reference(), referent()
*/
QString getID () const;
/** \brief Ask whether this Lob is acting as a context for Lurch IDs
*
* \see setIDContext()
*/
bool isIDContext () const;
/** \brief Change each ID in this tree to a new one and keep internal references synced
*
* This routine traverses the OpenMath tree wrapped by this Lob, and for each node
* with an ID, it replaces the ID with <code>context.getNewID()</code>.
* As it does so, it creates a conversion function from old ID values to new ones.
* The tree is then traversed again, applying that conversion
* function to all ID references, to keep them up to date.
* ID references whose IDs are not in the domain of such a function are external
* (non-local) references, and are not converted.
*
* \param context If not supplied, this defaults to an empty Lob, which gets converted
* internally to this Lob, and thus the default means "use this Lob's
* context," in the sense of setIDContext() and getNewID().
* If supplied, the new IDs will be computed in the context
* \a context, in the sense that it will be the Lob in which getNewID()
* is called, as stated above.
*
* This is called after making a copy of a Lob unless you explicitly ask for that
* not to happen; see copy().
*/
void changeIDs ( Lob context = Lob() );
/** \brief Ensure no two descendant nodes under this tree have duplicate Lurch IDs
*
* This function traverses the subtree under this Lob and ensures that no ID is used
* for more than one Lurch Object.
*
* \return True if and only if no ID is used more than once.
*/
bool checkUniqueIDs () const;
/** \brief Ensure no two descendant nodes under this tree have duplicate nicknames
*
* This function is analogous to checkUniqueIDs(), except for nicknames.
*
* This routine is tested in test_lenv::test_doc_and_deps(),
* because it is used by LurchEnvironment::load().
*
* \see checkUniqueIDs()
*/
bool checkUniqueNicknames () const;
/** \brief Remove all unused IDs in all descendants
*
* Nodes can be assigned Lurch IDs with ensureUniqueIDs(); this adds an attribute to
* every node, and perhaps even nearly every attribute as well.
* This makes the tree take up much more space in memory and take longer to process
* for various operations (such as conversion of old IDs into new ones in
* ensureUniqueIDs()). It is good to be able to drop unused IDs sometimes, for example
* before saving a document to the filesystem.
*
* This routine removes the Lurch ID of every descendant node except those whose IDs
* are referenced in a Lurch ID Reference elsewhere in this tree.
*/
void removeExtraIDs ();
/** \brief List all IDs or Nicknames in the wrapped OpenMath tree
*
* This does not list IDs or nicknames that appear only in references; it lists all
* the IDs and nicknames that actually appear as attributes of nodes in the tree.
*
* \param nicknameOrID Set this to the string "ID" to search for IDs, or the string
* "nickname" to search for nicknames. It is case sensitive.
* Behavior for other values of this parameter is undefined.
*/
QStringList listAllLabels ( QString nicknameOrID ) const;
/** \brief Whether this Lob wraps an OmBindingType OpenMath object
*
* Simply returns the comparison of nodeType() with OmBindingType.
*
* This routine is tested by test_bind::test_basics().
*/
bool isBinding () const;
/** \brief Whether this Lob wraps an OmBindingType node that binds the given variable
*
* \param var the variable in question
* \return true if and only if isBinding() and one of the bound variables matches
* \a var when compared using compareVariables()
*
* This routine is tested by test_bind::test_basics().
*/
bool binds ( Lob var ) const;
/** \brief All variables bound by this Lob, or any of its ancestors, up to \a context
*
* \param context The context in which "bound" is to be interpreted; for instance,
* even if this Lob is within some outer Lob that binds x, if that
* outer Lob is outside of \a context, x does not count as bound.
* This defaults to the outermost context (the root of the tree),
* a default which takes effect if a context is given that is not
* actually an ancestor of this Lob.
* \return A list of Lobs (as a QVariantList, because a Lob can be converted to and
* from a QVariant) that are the bound variables. If R is the result, access
* each of its members as a Lob using R[i].value<Lob>().
*
* This routine is tested in test_bind::test_all_bound().
*/
QVariantList boundVariables ( Lob context = Lob() ) const;
/** \brief Names of all variables bound by this Lob or its ancestors up to \a context
*
* This functions just like boundVariables(), except that only the variable names
* are returned. This is all that is relevant in most cases, unless attributes are
* taken into account by compareVariables(), which is not the default.
*
* This routine is tested in test_bind::test_all_bound().
*/
QStringList boundVariableNames ( Lob context = Lob() ) const;
/** \brief All variables that appear free in this Lob, with reference to \a context
*
* \param context The context in which freeness/boundness is determined. A quantifier
* only counts as binding a variable (for the purposes of this call) if
* it appears inside the tree \a context. If not supplied, this defaults
* to the whole tree, meaning all quantifiers are relevant.
* \param boundAbove Callers should not supply this; it is used internally for
* recursion. It is either false, meaning the data has yet to be
* computed, or it is a QVariantList, meaning the list of variables
* bound by quantifiers strictly above the current Lob (and inside
* \a context).
*
* This routine is tested in test_bind::test_all_free().
*/
QVariantList freeVariables ( Lob context = Lob(), QVariant boundAbove = false ) const;
/** \brief Names of all variables that appear free in this Lob, within \a context
*
* This functions just like freeVariables(), except that only the variable names
* are returned. This is all that is relevant in most cases, unless attributes are
* taken into account by compareVariables(), which is not the default.
*
* This routine is tested in test_bind::test_all_free().
*/
QStringList freeVariableNames ( Lob context = Lob() ) const;
/** \brief Whether the variable occurs free in this Lob
*
* Simply checks to see if \a var is on the list freeVariables(\a context).
*
* \see occursFree(QString,Lob)
*
* This routine is tested in test_bind::test_all_free().
*/
bool occursFree ( Lob var, Lob context = Lob() ) const;
/** \brief Whether this Lob (which must wrap a variable) is free in the given context
*
* \param context Functions as in freeVariables() and related routines.
* If not provided, it is assumed to be the whole tree in which this
* Lob sits.
* \return True if and only if this Lob is a variable and it is free in the given
* context. In particular, if this Lob is not a variable, false is returned.
*
* This routine is tested in test_bind::test_all_free().
*/
bool isFree ( Lob context = Lob() ) const;
/** \brief The ancestor Lob which binds this Lob (if it is a variable)
*
* \return If this Lob is a variable, then this function returns the Lob which is the
* closest ancestor of this one that binds this variable (as in binds()),
* if one exists. If one does not exist, or if the Lob is not a variable,
* then an empty Lob is returned.
* \param context If provided, the computation is done only within the given subtree
* \a context; otherwise, the entire tree in which this Lob resides
* is used for the computation. In other words, no ancestor strictly
* above \a context is searched.
*
* This routine is tested in test_bind::test_all_bound().
*/
Lob binderOf ( Lob context = Lob() ) const;
/** \brief All free occurrences of the given variable inside this Lob
*
* Comparisons are done using compareVariables(), as always, but the resulting list may
* contain repetitions in the compareVariables() sense, because the point is to return
* occurrences, and no occurrence will appear on the list more than once. As with
* freeVariables() and similar routines, the result is a list of variants, each element
* \a v of the list convertible to a Lob using v.value<Lob>().
*
* \param var The variables whose occurrences are to be returned
* \param context Used when computing freeness/boundness, as in boundVariables()
*
* This routine is tested by test_bind::test_all_free().
*/
QVariantList freeOccurrences ( Lob var, Lob context = Lob() ) const;
/** \brief Replaces every free occurrence of \a var in this Lob with \a term, in place
*
* This is equivalent to calling L.set(term.copy()) on every L in freeOccurrences(var).
* This routine in fact does exactly that, passing the \a context parameter directly
* on to the call to freeOccurrences().
*
* Note that the subsitution is done regardless of whether variables in \a term become
* bound in this Lob by the substitution; you can perform your own more subtle
* substitution by code like the following.
* \code
* foreach ( const QVariant& v, myLob.freeOccurrences( var, context ) ) {
* Lob occ = v.value<Lob>();
* if ( occ.freeToSubstitute( term, context ) ) // or any decision function here
* occ.set( term.copy() );
* }
* \endcode
*
* This routine is simply code like the above, but with no "if" clause.
* Thus the substitution is done in-place, in the sense that it modifies this Lob
* and/or its descendants. To return a modified copy instead, create the copy and
* then call freeSubstitute() in the copy.
*
* This routine is tested by test_bind::test_substitution().
*/
void freeSubstitute ( Lob var, Lob term, Lob context = Lob() );
/** \brief Whether the given term is free to substitute in place of this Lob in context
*
* A term is free to replace this Lob in context if no variable in term would become
* bound by a quantifier within context because of the substitution. Thust the
* \a context parameter here functions much as it does in several other routines in
* this class, such as freeVariables().
*
* Although this routine is intended to be used when this Lob is a variable,
* it can be used in other circumstances as well; in such cases, "substitution"
* is perhaps an inaccurate phrase, but the idea is the same: a future call to
* Lob::set() is anticipated, and its ramifications on \a term are to be determined.
*
* This routine is tested by test_bind::test_substitution().
*/
bool freeToSubstitute ( Lob term, Lob context = Lob() ) const;
/** \brief Rename all instances of a variable bound by this Lob, if it is a quantifier
*
* Assuming this Lob is a quantifier one of whose bound variables is \a var,
* then find all occurrences of \a var inside this Lob that are bound by this particular
* quantifier, and replace each one with a copy of \a newvar.
*
* Note that nested quantifications of the same variable are not changed.
* For instance, if you call this routine on Forall x Exists x P(x), to change x to y,
* you will end up with Forall y Exists x P(x), because the inner x's were not bound
* by the outer quantifier. Only instances of the variable bound by <i>this</i>
* Lob are changed.
*
* This routine is tested by test_bind::test_substitution().
*/
void renameBound ( Lob var, Lob newvar );
/** \brief Move all attributes from this Lob to another Lob
*
* This does not copy the attributes, it moves them. This Lob will no longer have the
* attributes, but \a other will.
*
* This can be useful in combination with set(), so that if you wish to replace one Lob
* with another while preserving attributes, you can proceed as follows.
* \code
* original.moveAttributesTo( replacement );
* original.set( replacement );
* \endcode
*
* If you execute the second line without the first, then the replacement takes place
* wholesale, and no attributes are preserved.
*
* This routine is not yet tested in any unit tests, except very indirectly in that it is
* used in renameBound().
*
* \see set(), renameBound()
*/
void moveAttributesTo ( Lob other );
private:
#ifdef LURCH_UNIT_TEST
friend class LURCH_UNIT_TEST;
#endif
/** Allow LobChange objects to read omobj, so they can do detailed and/or efficient edits
*/
friend class LobChange;
/** \brief The pointer to the OpenMath object for which this is a wrapper
*/
OmNode* omobj;
/** \brief Whether the Lob is editable (the opposite of read-only)
*/
bool editable;
/** A helper routine for getNewID() and changeIDs(), which finds a Lob's ID context
*/
Lob findIDContext () const;
/** A helper routine for changeIDs(), which does its first phase of work
*/
void buildIDConversion ( QString& nextID, Lob context,
QMap<QString,QString>& conversion );
/** A helper routine for resetAllIDs(), which does its second phase of work
*/
void convertIDReferences ( const QMap<QString,QString> conversion );
/** A helper routine for checkUniqueIDs() and checkUniqueNicknames()
*/
bool checkUnique ( QString nicknameOrID ) const;
/** A helper routine for removeExtraIDs(), which finds all referenced IDs
*/
void findReferencedIDs ( QStringList& result );
/** A helper routine for ensureUniqueIDs(), which removes all unreferenced IDs
*/
void removeUnreferencedIDs ( const QStringList referenced );
/** \brief A subroutine to check if two variables are equal
*
* Breaking this out into a subroutine makes it possible that later it may,
* rather than handling the comparison itself, delegate it to a script routine,
* by emitting a signal with a bool& parameter, say.
*
* This routine assumes the two given Lobs are variables, so it simply compares their
* values as variants, using toVariant().
*/
bool compareVariables ( Lob var1, Lob var2 ) const;
/** \brief Whether the variable appears on the list, according to compareVariables()
*
* This functionality is put here to factor it out of several routines that make use
* of it, including routines in this class as well as unit tests.
*
* \return true if and only if there is some element of \a list which, when converted
* to a Lob using list[i].value<Lob>(), is equal to \a var according to
* the compareVariables() method in this Lob
*
* This routine is indirectly tested by test_bind::test_all_bound().
*/
bool variableOnList ( Lob var, QVariantList list ) const;
/** \brief Merge two lists of variables, preserving uniqueness as per compareVariables()
*
* Return a new list containing all variables from both list \a a and list \a b,
* except without any duplicates, where equality is tested using the compareVariables()
* method of this Lob.
*/
QVariantList mergeVariableLists ( QVariantList a, QVariantList b ) const;
/** \brief Getter for the Lob::type property, for use in scripts
*
* This is different from the nodeType() method only in that it converts to an integer,
* so that no OmType enum is necessary in-script.
*
* \see nodeType()
*/
int scriptNodeType () const;
/** \brief Getter for the Lob::keys property, for use in scripts
*
* This is different from the attributeKeys() method only in that it converts to a
* QObjectList so that the script environment will not be confused by a QList<Lob>
*
* \see attributeKeys()
*/
QVariantList scriptAttributeKeys () const;
/** \brief Inserts an attribute in the attribute list
*
* This is declared private because clients of this class have access to the
* setAttribute() method, which should be all they need. But the LobChange class
* needs to be able to guarantee indexes do not change when changes are applied,
* undone, reapplied, etc. Therefore fine control over indexing is provided here.
*
* Although LobChange could simply manipulate the underlying OpenMath tree, that
* would not give this object a chance to emit change signals correctly, which this
* method does.
*/
void insertAttribute ( uint index, QString keyName, QString keyCD, Lob value );
/** \brief Removes an attribute from the attribute list
*
* This is declared private because clients of this class have access to the
* removeAttribute() method, which should be all they need. But the LobChange class
* needs to be able to guarantee indexes do not change when changes are applied,
* undone, reapplied, etc. Therefore fine control over indexing is provided here.
*
* Although LobChange could simply manipulate the underlying OpenMath tree, that
* would not give this object a chance to emit change signals correctly, which this
* method does.
*/
void removeAttribute ( uint index );
/** \brief Convenience function to simplify fetching string properties internally
*
* Gets the given attribute and returns its value as a string, unless it is not a
* string node, in which case the empty string is returned. The XHTML escapes
* <code>&nbsp;</code>, <code>&quot;</code>, <code>&apos;</code>,
* <code>&lt;</code>, <code>&gt;</code>, and <code>&amp;</code> are replaced
* with their meanings (in that order).
*/
QString getStringAttribute ( QString symbolName, QString symbolCD ) const;
/** \brief Convenience function to simplify writing string properties internally
*
* Sets the given attribute to the given string value, overwriting whatever is
* currently at that place in the OmNode tree, if anything. The XHTML characters
* &, >, <, ", ', and non-breaking space; are replaced with their
* escaped versions (in that order).
*/
void setStringAttribute ( QString symbolName, QString symbolCD, QString value );
/** \brief Lookup table to store reference counts for all wrapped OmNode pointers
*
* \see hold(), release()
*/
static SafeSingleton<QMap<OmNode*, unsigned long int> > referenceCounts;
/** \brief Whether the given OmNode* nor any of its descendents nor their attributes
* are wrapped by Lobs
*/
static bool descendentsUnreferenced ( OmNode* node );
/** \brief A Lob calls this when it begins wrapping an OmNode*, to add one to the
* reference count
*
* \see referenceCounts, release()
*/
static void hold ( OmNode* node );
/** \brief A Lob calls this when it stops wrapping an OmNode*, possibly triggering
* deletion
*
* A Lob calls this in exactly two situations. When it is destroyed, it releases
* the node it wraps. When it is assigned to a new Lob, and thus begins wrapping
* that Lob's OmNode*, it releases the one it formerly wrapped.
*
* If this node belongs to a tree in which no other node is wrapped by any Lob,
* then the entire tree of OmNodes is deleted. Otherwise, none is, and we wait
* until the last Lob wrapping any of them is deleted. (This way, Lobs can use
* their parent(), child(), nextSibling(), etc. functions to navigate a tree without
* fear that some of it may be deleted.)
*
* \see referenceCounts, hold()
*/
static void release ( OmNode* node );
/** \brief The list of Lobs that wish to emit the modified() signal accurately
*/
static SafeSingleton<QList<Lob*> > watchForModifications;
/** \brief Set this Lob's ID
*
* The ID is stored in the attribute whose key has name ID and content dictionary
* LurchCore.
* The attribute's value must be an OmStringNode containing "id" followed by
* a sequence of decimal digits.
* (This is distinct from the OpenMath id attribute, which is for
* OpenMath References, which are for compacting documents for storage,
* and have no actual meaning for the mathematics represented by the document.)
*
* If \a id is not of the specified form, then this function does nothing.
* (It also does nothing if the object is not editable, or an empty Lob.)
*
* The Lob class provides the public functions reference() and referent() for dealing
* with IDs; script authors only need to deal with those.
* For a detailed description of the Lurch ID System,
* see the <a href='idsystem.html'>Lurch ID System page</a>.
*
* \see getID(), reference(), referent()
*/
void setID ( QString id );
/** \brief A map from Lurch ID contexts to the highest used ID in that context
*/
static QMap<Lob,QString> highestIDs;
/** \brief Find the next ID after the largest used in this Lob's context
*
* If this Lob has a context with a cached largest ID, this will be efficient.
* If this Lob has no context, then the time this routine takes is based on how large
* of an OpenMath tree this Lob sits inside.
*
* Read more about this routine and the whole Lurch ID System on
* <a href='idsystem.html'>The Lurch ID System page</a>.
*/
QString getNewID () const;
/** A helper routine for referent(), which looks up referenced IDs recurisely
*/
Lob recursiveReferent ( QString id, OmNode* skip = NULL ) const;
/** \brief If the modification takes place below a watched Lob, have it emit modified()
*
* This routine is tested in test_change::test_recording() and
* test_lob_edit::test_read_only().
*/
void maybeEmitModified ( const LobChange& change,
OmNode::OwnershipType fromHere = OmNode::None,
unsigned int fromIndex = 0 ) const;
/** For use when this Lob is an ID context and needs to watch for changes that may
* necessitate changing its highest cached ID
*/
void updateHighestID () const;
private slots:
/** For use when this Lob is an ID context and needs to watch for changes that may
* necessitate changing its highest cached ID
*/
void updateHighestID ( const LobChange& change ) const;
};
/** \brief Tests structural equivalence of two OpenMath trees
*
* See the description of Lob::equivalentTo(); this is its implementation.
* Two different OpenMath trees that have the same nodes in the same arrangement are
* equivalent(), but are not operator==().
*
* \param a Pointer to an OpenMath node, the entire subtree under which
* will be compared to \a b
* \param b Pointer to an OpenMath node, the entire subtree under which
* will be compared to \a a
* \param compareAttributes When true, the two nodes' attributes are checked to see if they
* also match; otherwise, attributes may be different and the two
* nodes still be equivalent(). See Lob::equivalentTo() for more.
* \param exclusions Allows you to exclude certain name/CD pairs from consideration.
* See Lob::equivalentTo() for more.
*
* This routine obviously contains an implementation of comparing two OpenMath leaf nodes
* (OmFinalNode), and the following rules explain that comparison.
* <ul>
* <li>Two integer nodes are equivalent() if and only if they have the same value
* (OmIntegerNode::getValue()).</li>
* <li>Two float nodes are equivalent() if and only if they have the same value
* (OmFloatNode::getValue()).</li>
* <li>Two big integer nodes are equivalent() if and only if they meet the criteria I
* describe on \link openmath the page on OpenMath notes\endlink.</li>
* <li>Two byte array nodes are equivalent() if and only if they have the same lengths
* and each byte in one array matches the byte of the same index in the other array
* (both length and content fetched via OmByteArrayNode::getBuffer()).</li>
* <li>Two string nodes are equivalent() if and only if their buffers
* (OmStringNode::getBuffer()) match under the C <code>strcmp</code> function.</li>
* <li>Two variable nodes are equivalent() if and only if their names
* (OmVariableNode::getName()) match under the C <code>strcmp</code> function.</li>
* <li>Two processing instruction nodes are equivalent() if and only if their buffers
* (OmPInstructionNode::getBuffer()) match under the C <code>strcmp</code>
* function.</li>
* <li>Two symbol nodes are equivalent() if and only if their names
* (OmSymbolNode::getName()) match under the C <code>strcmp</code> function,
* and their content dictionaries (OmSymbolNode::getCD()) do as well.</li>
* <li>Two wide string nodes are equivalent() if and only if their buffers
* (OmWStringNode::getBuffer()), after conversion to QString objects via
* QString::fromWCharArray(), pass the <code>==</code> comparison test with one
* another (see QString::operator==()).</li>
* </ul>
* A non-final node is equivalent to another non-final node if each child of the first
* is equivalent() to the corresponding (same-indexed) child of the second.
*
* See \link openmath the page on OpenMath notes\endlink for information about how this
* routine interprets big integer objects.
*
* This class is tested by test_lob::test_equivalence(),
* test_lob::test_attributes(), and test_ref::test_exclusion().
*
* \see Lob::equivalentTo()
*/
bool equivalent ( const OmNode* a, const OmNode* b,
bool compareAttributes = false, QStringList exclusions = QStringList() );
/** \brief Deletes an OmNode
*
* No destructors in any class inheriting OmNode are public, except for OmDocument.
* Therefore you cannot create an OmNode descendant in either of the ways exemplified
* in the following block of code, because both require calling destructors, to which
* you have no access.
* \code
* void functionThatDoesNotCompile ()
* {
* OmNode example( OmIntegerType );
* OmNode* another = new OmBigIntegerNode();
* // do something
* delete another;
* }
* \endcode
* Although you can create them using new and then not delete them, this is obviously
* not what you want, because it is a memory leak. The intent is for you to create an
* OmDocument and place them in it as children. It takes ownership, and deletes its children
* when it is deleted. The following code is acceptable.
* \code
* void functionThatCompiles ()
* {
* OmDocument doc;
* doc.append( new OmIntegerNode( 5 ) );
* // do stuff, then doc deletes its children when it is deleted
* }
* \endcode
* So this routine was created to place any OmNode inside a dummy OmDocumentNode, which
* will be deleted (with its one child, the parameter) at the end of this routine's
* execution. Note that you cannot call it like this.
* \code
* void anotherNonCompilableOne ()
* {
* OmNode example( OmIntegerType );
* deleteOmNode( &example );
* }
* \endcode
* Thankfully, the compiler will not permit such shenanigans, because (not knowing that you
* have already freed the memory) it tries to do so again at the end of the routine, and
* cannot, because you can't access the destructor. Instead, write code like this.
* \code
* void theRightWay ()
* {
* OmNode* example = new OmIntegerNode( 42 );
* // do stuff
* deleteOmNode( example );
* }
* \endcode
* The only exception to this is that the parameter cannot be of type OmUnknownType,
* unless it is an OmDocumentNode object. In this latter case, the object is simply deleted
* without any wrapping needed; <b>otherwise this function takes no action</b>.
*
* This routine is untested; it seems straightforward, but testing it doesn't..
*/
void deleteOmNode ( OmNode* doomed );
/** \brief Convert OmType to QString for debugging purposes
*/
QString nameOfOmType ( OmType type );
/** \brief Escape the XML metacharacters <, >, &, etc.
*
* This is used when putting textual data into an XML expression, such as an OpenMath
* <OMSTR> object. It replaces characters that would invalidate the XML with their
* appropriately escaped versions, &quot;, &apos;, &lt;, &gt;,
* &amp;, and any unicode character not in Latin1.
*/
QString escapeXML ( QString notSafeForXML );
/** \brief Unescape the XML metacharacters <, >, &, etc.
*
* Reverse the operation of escapeXML(), useful for data that comes from inside an XML
* tag and thus may have been encoded using escapeXML().
*/
QString unescapeXML ( QString escaped );
/** \brief Increments a Lurch ID string to hold an ID value one higher
*
* Read more details on <a href='idsystem.html'>The Lurch ID System page</a>.
*
* This routine is tested in test_lob::test_non_members().
*/
QString incrementID ( QString id, unsigned int index = 0 );
/** \brief Compares two ids and determines if A < B numerically
*
* Read more details on <a href='idsystem.html'>The Lurch ID System page</a>.
*
* This routine is tested in test_lob::test_non_members().
*/
bool lessThanID ( const QString& idA, const QString& idB );
/** \brief Creates a Lurch ID whose numerical value is zero
*
* Read more details on <a href='idsystem.html'>The Lurch ID System page</a>.
*
* This routine is tested in test_lob::test_non_members().
*/
QString zeroID ();
Q_DECLARE_METATYPE( Lob* )
Q_DECLARE_METATYPE( Lob )
#endif // LOB