1 /*************************************************************************** 2 robject - description 3 ------------------- 4 begin : Thu Aug 19 2004 5 copyright : (C) 2004-2019 by Thomas Friedrichsmeier 6 email : thomas.friedrichsmeier@kdemail.net 7 ***************************************************************************/ 8 9 /*************************************************************************** 10 * * 11 * This program is free software; you can redistribute it and/or modify * 12 * it under the terms of the GNU General Public License as published by * 13 * the Free Software Foundation; either version 2 of the License, or * 14 * (at your option) any later version. * 15 * * 16 ***************************************************************************/ 17 #ifndef ROBJECT_H 18 #define ROBJECT_H 19 20 #include <qobject.h> 21 22 #include <qstring.h> 23 #include <qmap.h> 24 #include <QHash> 25 26 #include "../rbackend/rcommandreceiver.h" 27 28 class RSlotsPseudoObject; 29 class REnvironmentObject; 30 class RContainerObject; 31 class RKRowNames; 32 class RCommandChain; 33 class RKEditor; 34 class RData; 35 36 #define ROBJECT_UDPATE_STRUCTURE_COMMAND 1 37 38 /** 39 Base class for representations of objects in the R-workspace. RObject is never used directly (contains pure virtual functions). 40 41 @author Thomas Friedrichsmeier 42 */ 43 44 class RObject : public RCommandReceiver { 45 public: 46 RObject (RObject *parent, const QString &name); 47 virtual ~RObject (); 48 49 /** types of objects, RKWard knows about */ 50 enum RObjectType { 51 DataFrame=1, 52 Matrix=1 << 1, 53 Array=1 << 2, 54 List=1 << 3, 55 Container=1 << 4, 56 Variable=1 << 5, 57 Workspace=1 << 6, 58 Function=1 << 7, 59 Environment=1 << 8, 60 GlobalEnv=1 << 9, 61 ToplevelEnv=1 << 10, 62 PackageEnv=1 << 11, 63 Misplaced=1 << 12, /** < the object is not in the namespace where it would be expected */ 64 S4Object=1 << 13, 65 Numeric=1 << 14, 66 Factor=2 << 14, 67 Character=3 << 14, 68 Logical=4 << 14, 69 DataTypeMask=Numeric | Factor | Character | Logical, 70 PseudoObject = 1 << 26, /** < The object is an internal representation, only, and does not exist in R. Currently, this is the case only for the slots-pseudo object */ 71 Updating=1 << 27, /** < The object is about to be updated from R */ 72 Incomplete=1 << 28, /** < The information on this object is not complete (typically, it's children have not been scanned, yet). */ 73 NonVisibleObject=1 << 29, /** < the object is not listed in the object list. Currently, this is only the case for row.names()-objects */ 74 NeedDataUpdate=1 << 30, /** < the object's data should be (re-) fetched from R. The main purpose of this flag is to make sure the data is synced *after* the structure has been synced */ 75 Pending=1 << 31 /** < the object is pending, i.e. it has been created in the object list, but we have not seen it in R, yet. This is used by data editors to create the illusion that a new object was added immediately, while in fact it takes some time to create it in the backend. */ 76 }; 77 78 enum RDataType { 79 DataUnknown=0, 80 DataNumeric=1, 81 DataFactor=2, 82 DataCharacter=3, 83 DataLogical=4, 84 85 MinKnownDataType = DataNumeric, 86 MaxKnownDataType = DataLogical 87 }; 88 89 /** For passing data between RKStructureGetter and RObject. Be very careful about changing the values in this enum. It is for better readability / searchability of the code, only. */ 90 enum { 91 StoragePositionName = 0, 92 StoragePositionType = 1, 93 StoragePositionClass = 2, 94 StoragePositionMeta = 3, 95 StoragePositionDims = 4, 96 StoragePositionSlots = 5, 97 StoragePositionChildren = 6, 98 StoragePositionNamespace = 7, 99 StoragePositionFunArgs = 6, 100 StoragePositionFunValues = 7, 101 StorageSizeBasicInfo = 6, 102 }; 103 104 enum PseudoObjectType { 105 InvalidPseudoObject = 0, 106 SlotsObject = 1, 107 NamespaceObject = 1 << 1, 108 OrphanNamespacesObject = 1 << 2, 109 RowNamesObject = 1 << 3 110 }; 111 112 #define ROBJECT_TYPE_INTERNAL_MASK (RObject::Container | RObject::Variable | RObject::Workspace | RObject::Environment | RObject::Function) 113 /** @returns false if an object of the given old type cannot represent an object of the given new type (e.g. (new_type & RObjectType::Variable), but (old_type & RObjectType::Container)). */ isMatchingType(int old_type,int new_type)114 static bool isMatchingType (int old_type, int new_type) { return ((old_type & ROBJECT_TYPE_INTERNAL_MASK) == (new_type & ROBJECT_TYPE_INTERNAL_MASK)); }; 115 getShortName()116 QString getShortName () const { return name; }; 117 enum ObjectNameOptions { 118 DollarExpansion = 1, /**< Return list members as list$member, instead of list[["member"]] */ 119 IncludeEnvirIfNotGlobalEnv = 2, /**< Include package name for objects on the search path */ 120 IncludeEnvirForGlobalEnv = 4, /**< Include ".GlobalEnv" for objects inside globalenv */ 121 IncludeEnvirIfMasked = 8, /**< Include package name for objects that are masked (only applicable for object lists, i.e. getFullNames()) */ 122 ExplicitSlotsExpansion = 16, /**< Return slots as slot(object, member), intead of object\@member */ 123 NoIncludeEnvir = 0, /**< Label for missing include-envirs */ 124 DefaultObjectNameOptions = IncludeEnvirIfNotGlobalEnv 125 }; 126 virtual QString getFullName (int name_options = DefaultObjectNameOptions) const; 127 QString getLabel () const; 128 QString getMetaProperty (const QString &id) const; 129 QString getDescription () const; 130 131 void setLabel (const QString &value, bool sync=true); 132 void setMetaProperty (const QString &id, const QString &value, bool sync=true); 133 isContainer()134 bool isContainer () const { return (type & (Container | Environment | Workspace)); }; isDataFrame()135 bool isDataFrame () const { return (type & DataFrame); }; isVariable()136 bool isVariable () const { return (type & Variable); }; 137 /** see RObjectType */ isType(int type)138 bool isType (int type) const { return (RObject::type & type); }; isPseudoObject()139 bool isPseudoObject () const { return isType (PseudoObject); }; getPseudoObjectType()140 PseudoObjectType getPseudoObjectType () const { return pseudo_object_types.value (this, InvalidPseudoObject); }; isSlotsPseudoObject()141 bool isSlotsPseudoObject () const { return (isPseudoObject () && (getPseudoObjectType () == SlotsObject)); }; isPackageNamespace()142 bool isPackageNamespace () const { return (isPseudoObject () && (getPseudoObjectType () == NamespaceObject)); }; hasPseudoObject(const PseudoObjectType type)143 bool hasPseudoObject (const PseudoObjectType type) const { return (contained_objects & type); }; hasMetaObject()144 bool hasMetaObject () const { return (meta_map); }; 145 /** see RObjectType::Pending */ isPending()146 bool isPending () const { return type & Pending; }; 147 148 /** trigger an update of this and all descendent objects */ 149 virtual void updateFromR (RCommandChain *chain); 150 /** fetch updated data from the backend, if there are any listeners. Default implementation does nothing except clearing the dirty flag */ 151 virtual void updateDataFromR (RCommandChain *chain); 152 /** mark the data of this object and all of its children as dirty (recursively). Dirty data will be updated *after* the new structure update (if the object is opened for editing) */ 153 void markDataDirty (); 154 155 /** Returns the editor of this object, if any, or 0 */ 156 QList<RKEditor*> editors () const; 157 bool canWrite () const; 158 bool canRead () const; 159 bool canRename () const; 160 bool canRemove () const; 161 /** returns true, if this object is inside the .GlobalEnv. The .GlobalEnv is not considered to be inside itself. */ 162 bool isInGlobalEnv () const; 163 /** returns the toplevel environment that this object is in. May the same as the object. */ 164 REnvironmentObject *toplevelEnvironment () const; 165 166 void rename (const QString &new_short_name); 167 void remove (bool removed_in_workspace); 168 classNames()169 const QStringList &classNames () const { return classnames; }; 170 QString makeClassString (const QString &sep) const; 171 /** @param class_name the name of the class to check for 172 @returns true, if the object has (among others) the given class, false otherwise */ 173 bool inherits (const QString &class_name) const; 174 175 /** get vector of dimensions. For simplicity, In RKWard each object is considered to have at least one dimension (but that dimension may be 0 in length) */ getDimensions()176 const QVector<qint32> &getDimensions () const { return dimensions; }; 177 /** short hand for getDimension (0). Meaningful for one-dimensional objects */ getLength()178 int getLength () const { return dimensions[0]; }; 179 180 /** return the index of the given child, or -1 if there is no such child */ 181 int getObjectModelIndexOf (RObject *child) const; 182 int numChildrenForObjectModel () const; 183 RObject *findChildByObjectModelIndex (int) const; 184 185 /** A QList of RObjects. Internally the same as RObjectMap, but can be considered "public" */ 186 typedef QList<RObject*> ObjectList; 187 188 /** A map of values to labels. This is used both in regular objects, in which it just represents a map of named values, if any. The more important use is in factors, where it represents the factor levels. Here, the key is always a string representation of a positive integer. */ 189 typedef QMap<QString, QString> ValueLabels; 190 191 /** write the MetaData to the backend. Commands will be issued in the given chain */ 192 virtual void writeMetaData (RCommandChain *chain); 193 194 /** Returns the parent of this object. All objects have a parent except for the RObjectList (which returns 0) */ parentObject()195 RObject *parentObject () const { return (parent); }; 196 getDataType()197 RDataType getDataType () const { return (typeToDataType (type)); }; getType()198 int getType () const { return type; }; typeToDataType(int ftype)199 static RDataType typeToDataType (int ftype) { return ((RDataType) ((ftype & DataTypeMask) >> 14)); }; setDataType(RDataType new_type)200 void setDataType (RDataType new_type) { 201 int n_type = type - (type & DataTypeMask); 202 type = n_type + (new_type << 14); 203 }; 204 /** returns a textual representation of the given RDataType */ 205 static QString typeToText (RDataType); 206 /** converts the given text to a VarType. Returns Invalid on failure */ 207 static RDataType textToType (const QString &text); 208 /** Returns the given string in quotes, taking care of escaping quotation marks inside the string. */ 209 static QString rQuote (const QString &string); 210 /** Returns a pretty description of the object, and its most important properties. */ 211 virtual QString getObjectDescription () const; 212 /** Parses an object path (such as package::name[["a"]]$b@slot) into its components, returning them as a list (in this case 'package', '::' 'name', '$', 'a', '$', 'b', '@', 'slot'). */ 213 static QStringList parseObjectPath (const QString &path); 214 /** Tests whether the given name is "irregular", i.e. contains spaces, quotes, operators, or the like. @see RContainerObject::validizeName () */ 215 static bool irregularShortName (const QString &name); 216 /** try to find the object as a child object of this object. 217 @param name of the object (relative to this object) 218 @returns a pointer to the object (if found) or 0 if not found */ findObject(const QString & name)219 RObject *findObject (const QString &name) { return findObjects (parseObjectPath (name), false, "$").value (0); }; 220 /** Function for code completion: given the partial name, find all objects matching this partial name 221 @param partial_name The partial name to look up */ findObjectsMatching(const QString & partial_name)222 RObject::ObjectList findObjectsMatching (const QString &partial_name) { return findObjects (parseObjectPath (partial_name), true, "$"); }; 223 /** Get full-qualified object names for a list of objects as returned by findObjectsMatching */ 224 static QStringList getFullNames (const RObject::ObjectList &objects, int options); 225 226 /** Fetch more levels of object representation (if needed). Note: Data is fetched asynchronously. 227 @param levels levels to recurse (0 = only direct children). */ 228 void fetchMoreIfNeeded (int levels=1); 229 230 /** Representation of changes to an edited object (currently for vector data, only) */ 231 struct ChangeSet { 232 ChangeSet (int from = -1, int to = -1, bool reset = false) : from_indexChangeSet233 from_index(from), to_index(to), full_reset(reset) {}; 234 int from_index; /**< first changed index */ 235 int to_index; /**< last changed index */ 236 bool full_reset; /**< Model should do a full reset (e.g. dimensions may have changed) */ 237 }; 238 239 /** generates a (full) name for a child of this object with the given name. */ 240 virtual QString makeChildName (const QString &short_child_name, bool misplaced=false, int object_name_options=DefaultObjectNameOptions) const; 241 protected: 242 // why do I need those to compile? I thought they were derived classes! 243 friend class RContainerObject; 244 friend class RObjectList; 245 friend class REnvironmentObject; 246 /** A map of objects accessible by index. Used in RContainerObject. Defined here for technical reasons. */ 247 typedef QList<RObject*> RObjectMap; 248 249 RObject *parent; 250 QString name; 251 /** or-ed combination of RObjectType flags for this object */ 252 int type; 253 QVector<qint32> dimensions; 254 QStringList classnames; 255 /** or-ed combination of PseudoObjectType flags of pseudo objects available in this object */ 256 qint8 contained_objects; slotsPseudoObject()257 RSlotsPseudoObject *slotsPseudoObject () const { return (hasPseudoObject (SlotsObject) ? slots_objects.value (this) : 0); }; 258 /** returns the namespace environment for this object. Always returns 0 for objects which are not a package environment! */ namespaceEnvironment()259 REnvironmentObject* namespaceEnvironment () const { return (hasPseudoObject (NamespaceObject) ? namespace_objects.value (this) : 0); }; 260 void setSpecialChildObject (RObject *special, PseudoObjectType special_type); 261 262 /** Worker function for findObject() and findObjectsMatching(). 263 * @If partial true: Look for partial matches (objects starting with the given pattern), false: look for exact matches, only. */ 264 virtual ObjectList findObjects (const QStringList &path, bool partial, const QString &op); 265 266 /** Update object to reflect the structure passed in the new_data argument. If the data is mismatching (i.e. can not be accommodated by this type of object) false is returned (calls canAccommodateStructure () internally). In this case you should delete the object, and create a new one. 267 @returns true if the changes could be done, false if this */ 268 virtual bool updateStructure (RData *new_data); 269 270 typedef QMap<QString, QString> MetaMap; 271 MetaMap *meta_map; 272 273 virtual bool canAccommodateStructure (RData *new_data); 274 bool isValidName (RData *new_data); 275 bool isValidType (RData *new_data) const; 276 277 /** handles updating the object name from the given data (common functionality between RContainerObject and RKVariable. This should really never return true, as the name should never change. Hence also raises an assert. Is still useful for it's side effect of detaching and deleting the data from the RData structure after checking it. 278 @param new_data The data. Make sure it really is the classes field of an .rk.get.structure-command to update classes *before* calling this function! WARNING: the new_data object may get changed during this call. Call canAccommodateStructure () before calling this function! 279 @returns whether this caused any changes */ 280 bool updateName (RData *new_data); 281 /** update type information from the given data. 282 @param new_data The command. Make sure it really is the classification field of an .rk.get.structure-command to update classes *before* calling this function! WARNING: the new_data object may get changed during this call. Call canAccommodateStructure () before calling this function! 283 @returns whether this caused any changes */ 284 virtual bool updateType (RData *new_data); 285 /** handles updating class names from the given data (common functionality between RContainerObject and RKVariable 286 @param new_data The data. Make sure it really is the classes field of an .rk.get.structure-command to update classes *before* calling this function! WARNING: the new_data object may get changed during this call. Call canAccommodateStructure () before calling this function! 287 @returns whether this caused any changes */ 288 bool updateClasses (RData *new_data); 289 /** handles updating the meta data from the given data (common functionality between RContainerObject and RKVariable. WARNING: the new_data object may get changed during this call. Call canAccommodateStructure () before calling this function! 290 @param new_data The data. Make sure it really is the meta field of an .rk.get.structure-command to update classes *before* calling this function! 291 @returns whether this caused any changes */ 292 bool updateMeta (RData *new_data); 293 /** update dimension information from the given data. 294 @param new_data The command. Make sure it really is the dims field of an .rk.get.structure-command to update classes *before* calling this function! WARNING: the new_data object may get changed during this call. Call canAccommodateStructure () before calling this function! 295 @returns whether this caused any changes */ 296 bool updateDimensions (RData *new_data); 297 /** update information on slots of this object (if it is an S4 object) 298 @param new_data The command. Make sure it really is the slots field of an .rk.get.structure-command to update classes *before* calling this function! WARNING: the new_data object may get changed during this call. Call canAccommodateStructure () before calling this function! 299 @returns whether this caused any changes */ 300 bool updateSlots (RData *new_data); 301 302 friend class RKModificationTracker; 303 /** Notify the object that some model needs its data. The object should take care of fetching the data from the backend, unless it already has the data. The default implementation does nothing (raises an assert). */ 304 virtual void beginEdit (); 305 /** Notify the object that a model no longer needs its data. If there have been as many endEdit() as beginEdit() calls, the object should discard its data storage. The default implementation does nothing (raises an assert). */ 306 virtual void endEdit (); 307 308 void rCommandDone (RCommand *command) override; 309 310 /* Storage hashes for special objects which are held by some but not all objects, and thus should not have a pointer 311 * in the class declaration. Some apply only to specific RObject types, but moving storage to the relevant classes, would make it more 312 * difficult to maintain the generic bits. */ 313 static QHash<const RObject*, RSlotsPseudoObject*> slots_objects; 314 static QHash<const RObject*, REnvironmentObject*> namespace_objects; 315 static QHash<const RObject*, RKRowNames*> rownames_objects; 316 317 friend class RSlotsPseudoObject; 318 friend class RKPackageNamespaceObject; 319 friend class RKOrphanNamespacesObject; 320 friend class RKRowNames; 321 static QHash<const RObject*, PseudoObjectType> pseudo_object_types; 322 }; 323 324 #endif 325