1 /***************************************************************************
2 robjectlist - description
3 -------------------
4 begin : Wed Aug 18 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 #include "robjectlist.h"
18
19 #define UPDATE_DELAY_INTERVAL 500
20
21 #define ROBJECTLIST_UPDATE_ENVIRONMENTS_COMMAND 1
22 #define ROBJECTLIST_UPDATE_COMPLETE_COMMAND 2
23
24 #include <qtimer.h>
25 #include <qstringlist.h>
26
27 #include <KLocalizedString>
28
29 #include "renvironmentobject.h"
30 #include "../rbackend/rkrinterface.h"
31 #include "rkmodificationtracker.h"
32 #include "../misc/rkprogresscontrol.h"
33 #include "../settings/rksettingsmoduler.h"
34 #include "rkpseudoobjects.h"
35
36 #include "../rkglobals.h"
37
38 #include "../debug.h"
39
40 // static
41 RObjectList *RObjectList::object_list = 0;
42
RObjectList()43 RObjectList::RObjectList () : RContainerObject (0, QString ()) {
44 RK_TRACE (OBJECTS);
45 object_list = this;
46
47 update_timer = new QTimer (this);
48 update_timer->setSingleShot (true);
49 connect (update_timer, &QTimer::timeout, this, &RObjectList::timeout);
50
51 //update_timer->start (AUTO_UPDATE_INTERVAL, true);
52
53 type = RObject::Workspace;
54 name = "search()";
55
56 update_chain = 0;
57
58 globalenv = new REnvironmentObject (0, ".GlobalEnv");
59 globalenv->updateFromR (update_chain);
60
61 // TODO: Do we really need tracker notification at this stage?
62 RKOrphanNamespacesObject *obj = new RKOrphanNamespacesObject (this);
63 RKGlobals::tracker ()->beginAddObject (obj, this, 0); // first child after GlobalEnv
64 orphan_namespaces = obj;
65 RKGlobals::tracker ()->endAddObject (obj, this, 0);
66 }
67
~RObjectList()68 RObjectList::~RObjectList () {
69 RK_TRACE (OBJECTS);
70 delete orphan_namespaces;
71 delete globalenv;
72 }
73
getObjectDescription() const74 QString RObjectList::getObjectDescription () const {
75 return i18n ("This section contains environments that are not part of <i>.GlobalEnv</i> / your \"workspace\". Most importantly, this includes loaded packages, but also objects added to R's <i>search()<i>-path using <i>attach()</i>.");
76 }
77
detachPackages(const QStringList & packages,RCommandChain * chain,RKProgressControl * control)78 QStringList RObjectList::detachPackages (const QStringList &packages, RCommandChain *chain, RKProgressControl* control) {
79 RK_TRACE (OBJECTS);
80
81 QStringList remove;
82 QStringList reject;
83 for (int i = 0; i < packages.size(); ++i) {
84 QString shortname = packages[i];
85 shortname.remove ("package:");
86 if (RKSettingsModuleRPackages::essentialPackages ().contains (shortname)) {
87 reject.append (i18n ("Did not unload package %1. It is required in RKWard. If you really want to do this, do so on the R Console.", shortname));
88 } else if (!findChildByName (packages[i])) {
89 RK_ASSERT (false);
90 reject.append (i18n ("Package %1 appears not to have been loaded", shortname));
91 } else {
92 remove.append (packages[i]);
93 }
94 }
95 for (int i = 0; i < remove.size (); ++i) {
96 RCommand *command = new RCommand ("detach (" + rQuote (remove[i]) + ')', RCommand::App | RCommand::ObjectListUpdate);
97
98 if (control) control->addRCommand (command);
99 RKGlobals::rInterface()->issueCommand (command, chain);
100 }
101
102 return reject;
103 }
104
updateFromR(RCommandChain * chain)105 void RObjectList::updateFromR (RCommandChain *chain) {
106 RK_TRACE (OBJECTS);
107
108 if (update_chain) {
109 // gee, looks like another update is still on the way. lets schedule one for later:
110 update_timer->start (UPDATE_DELAY_INTERVAL);
111 RK_DEBUG (OBJECTS, DL_DEBUG, "another object-list update is already running. Rescheduling a further update for later");
112 return;
113 }
114
115 emit (updateStarted ());
116 update_chain = RKGlobals::rInterface ()->startChain (chain);
117
118 RCommand *command = new RCommand ("list (search (), loadedNamespaces ())", RCommand::App | RCommand::Sync | RCommand::GetStructuredData, QString (), this, ROBJECTLIST_UPDATE_ENVIRONMENTS_COMMAND);
119 RKGlobals::rInterface ()->issueCommand (command, update_chain);
120 }
121
updateFromR(RCommandChain * chain,const QStringList & current_searchpath,const QStringList & current_namespaces)122 void RObjectList::updateFromR (RCommandChain *chain, const QStringList ¤t_searchpath, const QStringList ¤t_namespaces) {
123 RK_TRACE (OBJECTS);
124
125 // TODO: can this happen? when?
126 if (update_chain) {
127 // gee, looks like another update is still on the way. lets schedule one for later:
128 update_timer->start (UPDATE_DELAY_INTERVAL);
129 RK_DEBUG (OBJECTS, DL_DEBUG, "another object-list update is already running. Rescheduling a further update for later");
130 return;
131 }
132
133 emit (updateStarted ());
134 update_chain = RKGlobals::rInterface ()->startChain (chain);
135
136 updateEnvironments (current_searchpath, false);
137 updateNamespaces (current_namespaces);
138
139 RKGlobals::rInterface ()->issueCommand (QString (), RCommand::App | RCommand::Sync | RCommand::EmptyCommand, QString (), this, ROBJECTLIST_UPDATE_COMPLETE_COMMAND, update_chain);
140 }
141
rCommandDone(RCommand * command)142 void RObjectList::rCommandDone (RCommand *command) {
143 RK_TRACE (OBJECTS);
144
145 if (command->getFlags () == ROBJECTLIST_UPDATE_ENVIRONMENTS_COMMAND) {
146 RK_ASSERT (command->getDataType () == RData::StructureVector);
147 const RData::RDataStorage & data = command->structureVector ();
148 RK_ASSERT (data.size () == 2);
149
150 QStringList new_environments = data[0]->stringVector ();
151 RK_ASSERT (new_environments.size () >= 2);
152
153 updateEnvironments (new_environments, true);
154 updateNamespaces (data[1]->stringVector ());
155
156 RKGlobals::rInterface ()->issueCommand (QString (), RCommand::App | RCommand::Sync | RCommand::EmptyCommand, QString (), this, ROBJECTLIST_UPDATE_COMPLETE_COMMAND, update_chain);
157 } else if (command->getFlags () == ROBJECTLIST_UPDATE_COMPLETE_COMMAND) {
158 RK_ASSERT (update_chain);
159 RKGlobals::rInterface ()->closeChain (update_chain);
160 update_chain = 0;
161
162 RK_DEBUG (OBJECTS, DL_DEBUG, "object list update complete");
163 emit (updateComplete ());
164 } else {
165 RK_ASSERT (false);
166 }
167 }
168
updateEnvironments(const QStringList & _env_names,bool force_globalenv_update)169 void RObjectList::updateEnvironments (const QStringList &_env_names, bool force_globalenv_update) {
170 RK_TRACE (OBJECTS);
171
172 RObjectMap newchildmap;
173 QStringList env_names = _env_names;
174 if (!env_names.isEmpty ()) {
175 QString dummy = env_names.takeFirst ();
176 RK_ASSERT (dummy == ".GlobalEnv");
177 if (force_globalenv_update) {
178 // for now, we only update the .GlobalEnv. All others we assume to be static
179 getGlobalEnv ()->updateFromR (update_chain);
180 }
181 } else {
182 RK_ASSERT (!env_names.isEmpty ());
183 }
184
185 // find which items are new, and copy the old ones
186 for (int i = 0; i < env_names.count (); ++i) {
187 QString name = env_names[i];
188
189 RObject *obj = findChildByName (name);
190 if (obj && (i > 0) && (env_names.lastIndexOf (name, i-1) > -1)) { // duplicate environment names can happen (e.g. if a data.frame is attached multiple times)
191 obj = 0; // only copy the old item once
192 }
193 if (!obj) {
194 obj = createTopLevelEnvironment (name);
195 RKGlobals::tracker ()->beginAddObject (obj, this, i);
196 childmap.insert (i, obj);
197 RKGlobals::tracker ()->endAddObject (obj, this, i);
198 }
199 newchildmap.insert (i, obj);
200 }
201
202 // check which envs have been removed or changed position
203 for (int i = 0; i < childmap.size (); ++i) { // do *not* cache the childmap.size ()! We may change it in the loop.
204 RObject *obj = childmap[i];
205 int new_pos = newchildmap.indexOf (obj);
206
207 if (new_pos < 0) { // environment is gone
208 RK_DEBUG (OBJECTS, DL_INFO, "removing toplevel environment %s from list", obj->getShortName ().toLatin1 ().data ());
209 if (RKGlobals::tracker ()->removeObject (obj, 0, true)) --i;
210 else (newchildmap.insert (i, obj));
211 } else if (new_pos != i) {
212 // this call is rather expensive, all in all, but fortunately called very rarely
213 moveChild (obj, i, new_pos);
214 }
215 }
216
217 RK_DO (RK_ASSERT (childmap == newchildmap), OBJECTS, DL_DEBUG); // this is an expensive assert, hence wrapping it inside RK_DO
218 }
219
updateNamespaces(const QStringList namespace_names)220 void RObjectList::updateNamespaces (const QStringList namespace_names) {
221 RK_TRACE (OBJECTS);
222
223 QStringList orphan_namespace_names;
224 for (int i = 0; i < namespace_names.size (); ++i) {
225 if (!findPackage (namespace_names[i])) orphan_namespace_names.append (namespace_names[i]);
226 }
227 orphan_namespaces->updateFromR (update_chain, orphan_namespace_names);
228 }
229
createTopLevelEnvironment(const QString & name)230 REnvironmentObject *RObjectList::createTopLevelEnvironment (const QString &name) {
231 RK_TRACE (OBJECTS);
232
233 REnvironmentObject *envobj = new REnvironmentObject (this, name);
234 envobj->updateFromR (update_chain);
235 return envobj;
236 }
237
findObjects(const QStringList & path,bool partial,const QString & op)238 RObject::ObjectList RObjectList::findObjects (const QStringList &path, bool partial, const QString &op) {
239 RK_TRACE (OBJECTS);
240 RK_ASSERT (op == "$");
241
242 RObject::ObjectList ret;
243 if (path.value (1) == "::") {
244 RObject *environment = findPackage (path[0]);
245 if (environment) return (environment->findObjects (path.mid (2), partial, "$"));
246 return ret;
247 } else if (path.value (1) == ":::") {
248 RObject *environment = findPackage (path[0]);
249 if (environment) environment = static_cast<REnvironmentObject*> (environment)->namespaceEnvironment ();
250 if (!environment) environment = orphan_namespaces->findOrphanNamespace (path[0]);
251 if (environment) return (environment->findObjects (path.mid (2), partial, "$"));
252 return ret;
253 } else if (path.value (0) == ".GlobalEnv") {
254 if (path.length () > 1) return getGlobalEnv ()->findObjects (path.mid (2), partial, "$");
255 // else we'll find base::.GlobalEnv, below
256 }
257
258 // no namespace given. Search all environments for matches, .GlobalEnv, first
259 ret = getGlobalEnv ()->findObjects (path, partial, "$");
260 for (int i = 0; i < childmap.size (); ++i) {
261 if (!(partial || ret.isEmpty ())) return ret;
262
263 ret.append (childmap[i]->findObjects (path, partial, "$"));
264 }
265 return ret;
266 }
267
getFullNames(const RObject::ObjectList & matches,int options)268 QStringList RObject::getFullNames (const RObject::ObjectList &matches, int options) {
269 RK_TRACE (OBJECTS);
270
271 QStringList ret;
272 QSet<QString> unique_names;
273 for (int i = 0; i < matches.count (); ++i) {
274 if (options & IncludeEnvirIfMasked) {
275 // - If the name is *not* masked (yet), return the plain name.
276 // - If the name *is* masked, return the full qualitfied name.
277 // NOTE: This assumes objects are given in search order!
278 QString base_name = matches[i]->getFullName (options);
279 if (unique_names.contains (base_name)) {
280 base_name = matches[i]->getFullName (options | IncludeEnvirIfNotGlobalEnv | IncludeEnvirForGlobalEnv);
281 }
282 RK_ASSERT (!unique_names.contains (base_name));
283 unique_names.insert (base_name);
284 ret.append (base_name);
285 } else {
286 ret.append (matches[i]->getFullName (options));
287 }
288 }
289 return ret;
290 }
291
findPackage(const QString & namespacename) const292 REnvironmentObject* RObjectList::findPackage (const QString &namespacename) const {
293 RK_TRACE (OBJECTS);
294
295 for (int i = childmap.size () - 1; i >= 0; --i) {
296 RObject* child = childmap[i];
297 if (!child->isType (PackageEnv)) continue; // Skip Autoloads
298 REnvironmentObject* env = static_cast<REnvironmentObject *> (child);
299 if (env->packageName () == namespacename) {
300 return env;
301 }
302 }
303 return 0;
304 }
305
updateStructure(RData *)306 bool RObjectList::updateStructure (RData *) {
307 RK_TRACE (OBJECTS);
308
309 RK_ASSERT (false);
310
311 return true;
312 }
313
timeout()314 void RObjectList::timeout () {
315 RK_TRACE (OBJECTS);
316
317 updateFromR (0);
318 }
319
renameChildCommand(RObject * object,const QString & new_name) const320 QString RObjectList::renameChildCommand (RObject *object, const QString &new_name) const {
321 RK_TRACE (OBJECTS);
322
323 return (makeChildName (new_name, false, IncludeEnvirIfNotGlobalEnv) + " <- " + object->getFullName () + '\n' + removeChildCommand (object));
324 }
325
removeChildCommand(RObject * object) const326 QString RObjectList::removeChildCommand (RObject *object) const {
327 RK_TRACE (OBJECTS);
328
329 return ("remove (" + object->getFullName () + ')');
330 }
331
332
333