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