VPTissue Reference Manual
Workspace_def.h
Go to the documentation of this file.
1 #ifndef WS_WORKSPACE_DEF_H_INCLUDED
2 #define WS_WORKSPACE_DEF_H_INCLUDED
3 /*
4  * Copyright 2011-2016 Universiteit Antwerpen
5  *
6  * Licensed under the EUPL, Version 1.1 or as soon they will be approved by
7  * the European Commission - subsequent versions of the EUPL (the "Licence");
8  * You may not use this work except in compliance with the Licence.
9  * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl5
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the Licence is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the Licence for the specific language governing
15  * permissions and limitations under the Licence.
16  */
22 #include "IProject.h"
23 #include "Workspace.h"
24 #include "ptree/PTreeUtils.h"
25 #include "util/misc/Exception.h"
27 
28 #include <boost/property_tree/xml_parser.hpp>
29 #include <QDir>
30 
31 namespace SimShell {
32 namespace Ws {
33 
34 using namespace std;
35 using namespace boost::property_tree;
36 using namespace boost::property_tree::xml_parser;
37 using namespace SimPT_Sim::Util;
38 
39 template <class ProjectType, const std::string& index_file>
41  const std::string& prefs_file)
42  : Preferences(path + '/' + prefs_file),
43  m_path((path.length() > 0 && path.rfind('/') == path.length() - 1) ? path.substr(0, path.length()-1) : path),
44  m_prefs_file(prefs_file),
45  m_filesystem_watcher((m_path == "") ? index_file : m_path + '/' + index_file, bind(&Workspace<ProjectType, index_file>::Refresh, this))
46 {
47  const string workspace_file = (m_path == "") ? index_file : m_path + '/' + index_file;
48 
49  ptree p;
50  try {
51  read_xml(workspace_file, p, trim_whitespace);
52  } catch (xml_parser_error& e) {
53  throw Exception("Could not open \"" + workspace_file + "\": Exception xml_parser_error: " + e.what());
54  }
55 
56  try {
57  m_user_data = p.get_child("workspace.user_data");
58  } catch (ptree_bad_path &) {
59  // No user_data subtree? Not a problem.
60  }
61 
62  try {
63  auto& projects = p.get_child("workspace.projects"); // may throw
64 
65  for (auto& value : projects) {
66  auto& project_type = value.first;
67  auto& project_name = value.second.data();
68  auto project_path = m_path + '/' + project_name;
69  auto project = ProjectType::Constructor(project_type, project_path,
70  m_prefs_file, shared_ptr<IWorkspace>(this, [](IWorkspace *){}));
71 
72  if (project) {
73  m_projects.insert({project_name, ProjectMapEntry(project, project_type)});
74  } else {
75  throw Exception("Could not create project of type \"" + project_type + "\".");
76  }
77  }
78 
79  } catch (ptree_bad_path& e) {
80  throw Exception("File \"" + workspace_file + "\": Exception ptree_bad_path: " + e.what());
81  }
82 }
83 
84 template <class ProjectType, const std::string& index_file>
86  : Preferences(move(other)),
87  m_path(move(other.m_path)),
88  m_projects(move(other.m_projects)),
89  m_filesystem_watcher(move(other.m_filesystem_watcher))
90 {
91 }
92 
93 template <class ProjectType, const std::string& index_file>
95 {
96  return index_file;
97 }
98 
99 template <class ProjectType, const std::string& index_file>
101 {
102  return m_path;
103 }
104 
105 template <class ProjectType, const std::string& index_file>
106 const ptree& Workspace<ProjectType, index_file>::GetUserData(const string& user) const
107 {
108  auto result_optional = m_user_data.get_child_optional(user);
109  if (result_optional) {
110  return result_optional.get();
111  } else {
112  auto& result = m_user_data.put_child(user, ptree());
113  return result;
114  }
115 }
116 
117 template <class ProjectType, const std::string& index_file>
118 void Workspace<ProjectType, index_file>::SetUserData(const string& user, const ptree& p)
119 {
120  m_user_data.put_child(user, p);
121  Store();
122 }
123 
124 template <class ProjectType, const std::string& index_file>
125 IWorkspace::ProjectIterator
127 {
128  return m_projects.begin();
129 }
130 
131 template <class ProjectType, const std::string& index_file>
132 IWorkspace::ConstProjectIterator
134 {
135  return m_projects.begin();
136 }
137 
138 template <class ProjectType, const std::string& index_file>
139 IWorkspace::ProjectIterator
141 {
142  return m_projects.end();
143 }
144 
145 template <class ProjectType, const std::string& index_file>
146 IWorkspace::ConstProjectIterator
148 {
149  return m_projects.end();
150 }
151 
152 template <class ProjectType, const std::string& index_file>
154 {
155  return begin()->second.Project();
156 }
157 
158 template <class ProjectType, const std::string& index_file>
159 shared_ptr<const IProject> Workspace<ProjectType, index_file>::Front() const
160 {
161  return begin()->second.Project();
162 }
163 
164 template <class ProjectType, const std::string& index_file>
166 {
167  return (--end())->second.Project();
168 }
169 
170 template <class ProjectType, const std::string& index_file>
171 shared_ptr<const IProject> Workspace<ProjectType, index_file>::Back() const
172 {
173  return (--end())->second.Project();
174 }
175 
176 template <class ProjectType, const std::string& index_file>
177 IWorkspace::ProjectIterator
179 {
180  return m_projects.find(name);
181 }
182 
183 template <class ProjectType, const std::string& index_file>
184 IWorkspace::ConstProjectIterator
186 {
187  return m_projects.find(name);
188 }
189 
190 
191 template <class ProjectType, const std::string& index_file>
192 bool
194 {
195  return m_projects.find(name) != end();
196 }
197 
198 template <class ProjectType, const std::string& index_file>
199 shared_ptr<const IProject> Workspace<ProjectType, index_file>::Get(const string& name) const
200 {
201  auto it = m_projects.find(name);
202  if (it == m_projects.end())
203  throw Exception("No such project \"" + name + "\" in workspace.");
204  return it->second.Project();
205 }
206 
207 template <class ProjectType, const std::string& index_file>
208 shared_ptr<IProject> Workspace<ProjectType, index_file>::Get(const string& name)
209 {
210  auto it = m_projects.find(name);
211  if (it == m_projects.end())
212  throw Exception("No such project \"" + name + "\" in workspace.");
213  return it->second.Project();
214 }
215 
216 template <class ProjectType, const std::string& index_file>
217 IWorkspace::ProjectIterator
218 Workspace<ProjectType, index_file>::New(const std::string& type, const string& name)
219 {
220  if (m_projects.find(name) != m_projects.end()) {
221  throw Exception("Cannot create project \"" + name
222  + "\": Another project with the same name already exists.");
223  }
224 
225  // create directory for project
226  QDir d(QString::fromStdString(m_path));
227  if (!d.mkdir(QString::fromStdString(name))) {
228  //throw Exception("Could not create directory \"" + m_path + '/' + name + "\".");
229  }
230 
231  return Add(type, name);
232 }
233 
234 template <class ProjectType, const std::string& index_file>
235 IWorkspace::ProjectIterator
236 Workspace<ProjectType, index_file>::Add(const std::string& project_type, const string& project_name)
237 {
238  auto it = m_projects.find(project_name);
239  if (it != m_projects.end()) {
240  return it;
241  }
242  string project_path = m_path + '/' + project_name;
243  auto result = m_projects.end();
244  auto project = ProjectType::Constructor(project_type, project_path,
245  m_prefs_file, shared_ptr<IWorkspace>(this, [](IWorkspace*){}));
246  if (project) {
247  result = m_projects.insert({project_name, ProjectMapEntry(project, project_type)}).first;
248  } else {
249  throw Exception("Could not create project of type \"" + project_type + "\".");
250  }
251 
252  Store();
254  {Event::WorkspaceChanged::ProjectAdded, project_name});
255  return result;
256 }
257 
258 template <class ProjectType, const std::string& index_file>
259 void
261 {
262  // TODO: refresh preferences?
263 
264  const string workspace_file = (m_path == "") ? index_file : m_path + '/' + index_file;
265 
266  ptree p;
267  try {
268  read_xml(workspace_file, p, trim_whitespace);
269  } catch (xml_parser_error& e) {
270  throw Exception("Could not open \"" + workspace_file + "\": Exception xml_parser_error: " + e.what());
271  }
272 
273  try {
274  auto& projects = p.get_child("workspace.projects"); // may throw
275 
276  // Remove old projects.
277  vector<string> to_delete(m_projects.size());
278  for (auto& value : m_projects) {
279  bool found = false;
280  for (auto& compare : projects) {
281  if (compare.second.data() == value.first) {
282  found = true;
283  break;
284  }
285  }
286  if (!found) {
287  to_delete.push_back(value.first);
288  }
289  }
290  for (auto& filename : to_delete) {
291  m_projects.erase(filename);
292  Subject<Event::WorkspaceChanged, std::weak_ptr<const void>>::Notify({Event::WorkspaceChanged::Type::ProjectRemoved, filename});
293  }
294 
295  // Add new projects.
296  for (auto& value : projects) {
297  const string project_type = value.first;
298  const string project_name = value.second.data();
299  const string project_path = m_path + '/' + project_name;
300 
301  // Check if project already exists.
302  auto existing_project_it = m_projects.find(project_name);
303  if (existing_project_it == m_projects.end()) {
304  // New project.
305  auto project = ProjectType::Constructor(project_type, project_path,
306  m_prefs_file, shared_ptr<IWorkspace>(this, [](IWorkspace*){}));
307  if (project) {
308  m_projects.insert({project_name, ProjectMapEntry(project, project_type)});
309  Subject<Event::WorkspaceChanged, std::weak_ptr<const void>>::Notify({Event::WorkspaceChanged::Type::ProjectAdded, project_name});
310  } else {
311  throw Exception("Could not create project of type \"" + project_type + "\".");
312  }
313  } else {
314  // Refresh Existing project.
315  existing_project_it->second->Refresh();
316  }
317  }
318  } catch (ptree_bad_path& e) {
319  throw Exception("File \"" + workspace_file + "\": Exception ptree_bad_path: " + e.what());
320  }
321 }
322 
323 template <class ProjectType, const std::string& index_file>
324 IWorkspace::ProjectIterator
325 Workspace<ProjectType, index_file>::Rename(const string& old_name, const string& new_name)
326 {
327  auto it = m_projects.find(old_name);
328  if (it == m_projects.end())
329  throw Exception("No such project \"" + old_name + "\".");
330  return Rename(it, new_name);
331 }
332 
333 template <class ProjectType, const std::string& index_file>
334 IWorkspace::ProjectIterator
335 Workspace<ProjectType, index_file>::Rename(ProjectIterator it, const string& new_name)
336 {
337  // Iterator will be invalidated after rename, store name.
338  const string old_name = it->first;
339  const string new_path = m_path + '/' + new_name;
340  QDir dir;
341  if (!dir.rename(QString::fromStdString(it->second->GetPath()), QString::fromStdString(new_path))) {
342  throw Exception("Could not move directory \"" + it->second->GetPath() + "\" to \"" + new_path + "\".");
343  }
344  auto project_type = it->second.Type();
345  m_projects.erase(it);
346  ProjectMap::value_type v(new_name, ProjectMapEntry((ProjectType::Constructor)(project_type,
347  new_path, m_prefs_file, shared_ptr<IWorkspace>(this, [](IWorkspace *){})), project_type));
348  auto result = m_projects.insert(v);
349  Store();
350  Subject<Event::WorkspaceChanged, std::weak_ptr<const void>>::Notify({old_name, new_name});
351  return result.first;
352 }
353 
354 template <class ProjectType, const std::string& index_file>
356 {
357  auto it = m_projects.find(name);
358  if (it == m_projects.end())
359  throw Exception("No such project \"" + name + "\".");
360  Remove(it);
361 }
362 
363 template <class ProjectType, const std::string& index_file>
365 {
366  // Iterator will be invalidated after remove, store name.
367  string name = it->first;
368 
369  QString dirname(QString::fromStdString(m_path + '/' + it->first));
370 
371  // Disable filesystem watcher for project.
372  // If we don't do this, all files being removed in the following
373  // step will cause unnecessary updates for the project, potentially
374  // even leading to a segfault (because the filesystem watcher runs
375  // in a separate thread and may cause updates when the project is
376  // already removed)
377  it->second.Project()->SetWatchingDirectory(false);
378 
379  // Next, remove all other files from project directory
380  // TODO: Use Qt5 QDir::removeRecursively()
381  function<bool(QString&)> remove;
382  remove = [&remove](const QString& dirname)->bool {
383  bool result = true;
384  QDir d(dirname);
385  if (d.exists(dirname)) {
386  for (QFileInfo info : d.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs | QDir::Files, QDir::DirsFirst)) {
387  if (info.isDir()) {
388  QString dirname = info.absoluteFilePath();
389  result = remove(dirname);
390  }
391  else {
392  result = QFile::remove(info.absoluteFilePath());
393  }
394  if (!result) {
395  return result;
396  }
397  }
398  result = d.rmdir(dirname);
399  }
400  return result;
401  };
402  if (!remove(dirname))
403  throw Exception("Could not remove directory \"" + m_path + '/' + it->first + "\".");
404  m_projects.erase(it);
405  Store();
406  Subject<Event::WorkspaceChanged, std::weak_ptr<const void>>::Notify({Event::WorkspaceChanged::ProjectRemoved, name});
407 }
408 
409 template <class ProjectType, const std::string& index_file>
411 {
412  ptree pt_workspace;
413  pt_workspace.put("workspace.projects", "");
414  for (auto& project : *this) {
415  pt_workspace.add("workspace.projects.project", project.first);
416  }
417  pt_workspace.put_child("workspace.user_data", m_user_data);
418  write_xml(m_path + '/' + index_file, pt_workspace, std::locale(), XmlWriterSettings::GetTab());
419 }
420 
421 } // namespace
422 } // namespace
423 
424 #endif // end_of_inclde_guard
virtual ProjectIterator begin()
Implementation of IPreferences.
Definition: Preferences.h:35
virtual bool IsProject(const std::string &name) const
STL namespace.
ProjectMap m_projects
Mapping from project name to project.
Namespace for miscellaneous utilities.
Definition: PTreeFile.cpp:44
virtual std::shared_ptr< IProject > Get(const std::string &name)
Workspace(const std::string &path, const std::string &prefs_file)
Constructor.
Definition: Workspace_def.h:40
Interface for Workspace.
std::string m_path
Path workspace was initialized with.
virtual ProjectIterator Rename(ProjectIterator, const std::string &new_name)
boost::property_tree::ptree m_user_data
GetUserData() with non-existing argument creates an empty user data.
Interface for workspace-like behavior.
Definition: IWorkspace.h:48
Extremely simple Exception root class.
Definition: Exception.h:28
PTreeUtils interface.
Interface for IProject.
std::string m_prefs_file
Name of preferences file.
virtual std::shared_ptr< IProject > Back()
virtual const boost::property_tree::ptree & GetUserData(const std::string &user) const
virtual void Remove(ProjectIterator)
virtual void SetUserData(const std::string &user, const boost::property_tree::ptree &)
virtual ProjectIterator Add(const std::string &type, const std::string &name)
virtual void Refresh()
Abstraction of workspace on file system.
virtual const std::string & GetPath() const
virtual std::shared_ptr< IProject > Front()
Subject in Observer pattern.
Definition: Subject.h:34
virtual ProjectIterator end()
virtual const std::string & GetIndexFile() const
Definition: Workspace_def.h:94
Header file for Exception class.
Xml writer settings class.
virtual ProjectIterator Find(const std::string &name)
Namespace for generic graphical shell for simulators.
Definition: SimSession.h:32
virtual ProjectIterator New(const std::string &type, const std::string &name)