resourceskill.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * *
3  * Copyright (C) 2007-2012 by Johan De Taeye, frePPLe bvba *
4  * *
5  * This library is free software; you can redistribute it and/or modify it *
6  * under the terms of the GNU Affero General Public License as published *
7  * by the Free Software Foundation; either version 3 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This library is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU Affero General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU Affero General Public *
16  * License along with this program. *
17  * If not, see <http://www.gnu.org/licenses/>. *
18  * *
19  ***************************************************************************/
20 
21 #define FREPPLE_CORE
22 #include "frepple/model.h"
23 
24 namespace frepple
25 {
26 
28 
29 
31 {
32  // Initialize the metadata
33  metadata = new MetaCategory("resourceskill", "resourceskills", MetaCategory::ControllerDefault, writer);
34  const_cast<MetaCategory*>(metadata)->registerClass(
35  "resourceskill","resourceskill",true,Object::createDefault<ResourceSkill>
36  );
37 
38  // Initialize the Python class
40  x.setName("resourceskill");
41  x.setDoc("frePPLe resourceskill");
42  x.supportgetattro();
43  x.supportsetattro();
44  x.supportcreate(create);
45  x.addMethod("toXML", toXML, METH_VARARGS, "return a XML representation");
46  const_cast<MetaCategory*>(ResourceSkill::metadata)->pythonClass = x.type_object();
47  return x.typeReady();
48 }
49 
50 
52 {
53  setSkill(s);
54  setResource(r);
55  setPriority(u);
57  try { validate(ADD); }
58  catch (...)
59  {
60  if (getSkill()) getSkill()->resources.erase(this);
61  if (getResource()) getResource()->skills.erase(this);
63  throw;
64  }
65 }
66 
67 
69 {
70  setSkill(s);
71  setResource(r);
72  setPriority(u);
73  setEffective(e);
75  try { validate(ADD); }
76  catch (...)
77  {
78  if (getSkill()) getSkill()->resources.erase(this);
79  if (getResource()) getResource()->skills.erase(this);
81  throw;
82  }
83 }
84 
85 
87 {
88  bool first = true;
89  for (Resource::iterator i = Resource::begin(); i != Resource::end(); ++i)
90  for (Resource::skilllist::const_iterator j = i->getSkills().begin(); j != i->getSkills().end(); ++j)
91  {
92  if (first)
93  {
95  first = false;
96  }
97  // We use the FULL mode, to force the flows being written regardless
98  // of the depth in the XML tree.
100  }
101  if (!first) o->EndObject(Tags::tag_resourceskills);
102 }
103 
104 
106 {
107  // If the resourceskill has already been saved, no need to repeat it again
108  // A 'reference' to a load is not useful to be saved.
109  if (m == REFERENCE) return;
110  assert(m != NOHEADER);
111 
112  o->BeginObject(tag);
113 
114  // If the resourceskill is defined inside of a resource tag, we don't need to save
115  // the resource. Otherwise we do save it...
116  if (!dynamic_cast<Resource*>(o->getPreviousObject()))
118 
119  // If the resourceskill is defined inside of a skill tag, we don't need to save
120  // the skill. Otherwise we do save it...
121  if (!dynamic_cast<Skill*>(o->getPreviousObject()))
123 
124  // Write the priority and effective daterange
126  if (getEffective().getStart() != Date::infinitePast)
128  if (getEffective().getEnd() != Date::infiniteFuture)
130 
131  o->EndObject(tag);
132 }
133 
134 
136 {
137  if (pAttr.isA (Tags::tag_resource))
139  else if (pAttr.isA (Tags::tag_skill))
141 }
142 
143 
144 DECLARE_EXPORT void ResourceSkill::endElement (XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
145 {
146  if (pAttr.isA (Tags::tag_resource))
147  {
148  Resource *r = dynamic_cast<Resource*>(pIn.getPreviousObject());
149  if (r) setResource(r);
150  else throw LogicException("Incorrect object type during read operation");
151  }
152  else if (pAttr.isA (Tags::tag_skill))
153  {
154  Skill *s = dynamic_cast<Skill*>(pIn.getPreviousObject());
155  if (s) setSkill(s);
156  else throw LogicException("Incorrect object type during read operation");
157  }
158  else if (pAttr.isA(Tags::tag_priority))
159  setPriority(pElement.getInt());
160  else if (pAttr.isA(Tags::tag_effective_end))
161  setEffectiveEnd(pElement.getDate());
162  else if (pAttr.isA(Tags::tag_effective_start))
163  setEffectiveStart(pElement.getDate());
164  else if (pAttr.isA(Tags::tag_action))
165  {
166  delete static_cast<Action*>(pIn.getUserArea());
167  pIn.setUserArea(
168  new Action(MetaClass::decodeAction(pElement.getString().c_str()))
169  );
170  }
171  else if (pIn.isObjectEnd())
172  {
173  // The resourceskill data is now all read in. See if it makes sense now...
174  Action a = pIn.getUserArea() ?
175  *static_cast<Action*>(pIn.getUserArea()) :
176  ADD_CHANGE;
177  delete static_cast<Action*>(pIn.getUserArea());
178  try { validate(a); }
179  catch (...)
180  {
181  delete this;
182  throw;
183  }
184  }
185 }
186 
187 
189 {
190  if (attr.isA(Tags::tag_resource))
191  return PythonObject(getResource());
192  if (attr.isA(Tags::tag_skill))
193  return PythonObject(getSkill());
194  if (attr.isA(Tags::tag_priority))
195  return PythonObject(getPriority());
196  if (attr.isA(Tags::tag_effective_end))
197  return PythonObject(getEffective().getEnd());
198  if (attr.isA(Tags::tag_effective_start))
199  return PythonObject(getEffective().getStart());
200  return NULL;
201 }
202 
203 
205 {
206  if (attr.isA(Tags::tag_resource))
207  {
208  if (!field.check(Resource::metadata))
209  {
210  PyErr_SetString(PythonDataException, "resourceskill resource must be of type resource");
211  return -1;
212  }
213  Resource* y = static_cast<Resource*>(static_cast<PyObject*>(field));
214  setResource(y);
215  }
216  else if (attr.isA(Tags::tag_skill))
217  {
218  if (!field.check(Skill::metadata))
219  {
220  PyErr_SetString(PythonDataException, "resourceskill skill must be of type skill");
221  return -1;
222  }
223  Skill* y = static_cast<Skill*>(static_cast<PyObject*>(field));
224  setSkill(y);
225  }
226  else if (attr.isA(Tags::tag_priority))
227  setPriority(field.getInt());
228  else if (attr.isA(Tags::tag_effective_end))
229  setEffectiveEnd(field.getDate());
230  else if (attr.isA(Tags::tag_effective_start))
231  setEffectiveStart(field.getDate());
232  else
233  return -1;
234  return 0;
235 }
236 
237 
238 /** @todo this method implementation is not generic enough and not extendible by subclasses. */
239 PyObject* ResourceSkill::create(PyTypeObject* pytype, PyObject* args, PyObject* kwds)
240 {
241  try
242  {
243  // Pick up the skill
244  PyObject* skill = PyDict_GetItemString(kwds,"skill");
245  if (!PyObject_TypeCheck(skill, Skill::metadata->pythonClass))
246  throw DataException("resourceskill skill must be of type skill");
247 
248  // Pick up the resource
249  PyObject* res = PyDict_GetItemString(kwds,"resource");
250  if (!PyObject_TypeCheck(res, Resource::metadata->pythonClass))
251  throw DataException("resourceskill resource must be of type resource");
252 
253  // Pick up the priority
254  PyObject* q1 = PyDict_GetItemString(kwds,"priority");
255  int q2 = q1 ? PythonObject(q1).getInt() : 1;
256 
257  // Pick up the effective dates
258  DateRange eff;
259  PyObject* eff_start = PyDict_GetItemString(kwds,"effective_start");
260  if (eff_start)
261  {
262  PythonObject d(eff_start);
263  eff.setStart(d.getDate());
264  }
265  PyObject* eff_end = PyDict_GetItemString(kwds,"effective_end");
266  if (eff_end)
267  {
268  PythonObject d(eff_end);
269  eff.setEnd(d.getDate());
270  }
271 
272  // Create the load
273  ResourceSkill *l = new ResourceSkill(
274  static_cast<Skill*>(skill),
275  static_cast<Resource*>(res),
276  q2, eff
277  );
278 
279  // Return the object
280  Py_INCREF(l);
281  return static_cast<PyObject*>(l);
282  }
283  catch (...)
284  {
285  PythonType::evalException();
286  return NULL;
287  }
288 }
289 
290 
291 DECLARE_EXPORT void ResourceSkill::validate(Action action)
292 {
293  // Catch null operation and resource pointers
294  Skill *skill = getSkill();
295  Resource *res = getResource();
296  if (!skill || !res)
297  {
298  // Invalid load model
299  if (!skill && !res)
300  throw DataException("Missing resource and kill on a resourceskill");
301  else if (!skill)
302  throw DataException("Missing skill on a resourceskill on resource '"
303  + res->getName() + "'");
304  else if (!res)
305  throw DataException("Missing resource on a resourceskill on skill '"
306  + skill->getName() + "'");
307  }
308 
309  // Check if a resourceskill with 1) identical resource, 2) identical skill and
310  // 3) overlapping effectivity dates already exists
311  Skill::resourcelist::const_iterator i = skill->getResources().begin();
312  for (; i != skill->getResources().end(); ++i)
313  if (i->getResource() == res
314  && i->getEffective().overlap(getEffective())
315  && &*i != this)
316  break;
317 
318  // Apply the appropriate action
319  switch (action)
320  {
321  case ADD:
322  if (i != skill->getResources().end())
323  {
324  throw DataException("Resourceskill of '" + res->getName() + "' and '"
325  + skill->getName() + "' already exists");
326  }
327  break;
328  case CHANGE:
329  throw DataException("Can't update a resourceskill");
330  case ADD_CHANGE:
331  // ADD is handled in the code after the switch statement
332  if (i == skill->getResources().end()) break;
333  throw DataException("Can't update a resourceskill");
334  case REMOVE:
335  // This resourceskill was only used temporarily during the reading process
336  delete this;
337  if (i == skill->getResources().end())
338  // Nothing to delete
339  throw DataException("Can't remove nonexistent resourceskill of '"
340  + res->getName() + "' and '" + skill->getName() + "'");
341  delete &*i;
342  return;
343  }
344 }
345 
346 
348 {
349  // Initialize the type
351  x.setName("resourceSkillIterator");
352  x.setDoc("frePPLe iterator for resource skills");
353  x.supportiter();
354  return x.typeReady();
355 }
356 
357 
358 PyObject* ResourceSkillIterator::iternext()
359 {
360  PyObject* result;
361  if (res)
362  {
363  // Iterate over skills on a resource
364  if (ir == res->getSkills().end()) return NULL;
365  result = const_cast<ResourceSkill*>(&*ir);
366  ++ir;
367  }
368  else
369  {
370  // Iterate over resources having a skill
371  if (is == skill->getResources().end()) return NULL;
372  result = const_cast<ResourceSkill*>(&*is);
373  ++is;
374  }
375  Py_INCREF(result);
376  return result;
377 }
378 
379 }