resourceskill.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * *
3  * Copyright (C) 2007-2013 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 != NOHEAD && m != NOHEADTAIL);
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  // Write the tail
132  if (m != NOHEADTAIL && m != NOTAIL) o->EndObject(tag);
133 }
134 
135 
137 {
138  if (pAttr.isA (Tags::tag_resource))
140  else if (pAttr.isA (Tags::tag_skill))
142 }
143 
144 
145 DECLARE_EXPORT void ResourceSkill::endElement (XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
146 {
147  if (pAttr.isA (Tags::tag_resource))
148  {
149  Resource *r = dynamic_cast<Resource*>(pIn.getPreviousObject());
150  if (r) setResource(r);
151  else throw LogicException("Incorrect object type during read operation");
152  }
153  else if (pAttr.isA (Tags::tag_skill))
154  {
155  Skill *s = dynamic_cast<Skill*>(pIn.getPreviousObject());
156  if (s) setSkill(s);
157  else throw LogicException("Incorrect object type during read operation");
158  }
159  else if (pAttr.isA(Tags::tag_priority))
160  setPriority(pElement.getInt());
161  else if (pAttr.isA(Tags::tag_effective_end))
162  setEffectiveEnd(pElement.getDate());
163  else if (pAttr.isA(Tags::tag_effective_start))
164  setEffectiveStart(pElement.getDate());
165  else if (pAttr.isA(Tags::tag_action))
166  {
167  delete static_cast<Action*>(pIn.getUserArea());
168  pIn.setUserArea(
169  new Action(MetaClass::decodeAction(pElement.getString().c_str()))
170  );
171  }
172  else if (pIn.isObjectEnd())
173  {
174  // The resourceskill data is now all read in. See if it makes sense now...
175  Action a = pIn.getUserArea() ?
176  *static_cast<Action*>(pIn.getUserArea()) :
177  ADD_CHANGE;
178  delete static_cast<Action*>(pIn.getUserArea());
179  try { validate(a); }
180  catch (...)
181  {
182  delete this;
183  throw;
184  }
185  }
186 }
187 
188 
190 {
191  if (attr.isA(Tags::tag_resource))
192  return PythonObject(getResource());
193  if (attr.isA(Tags::tag_skill))
194  return PythonObject(getSkill());
195  if (attr.isA(Tags::tag_priority))
196  return PythonObject(getPriority());
197  if (attr.isA(Tags::tag_effective_end))
198  return PythonObject(getEffective().getEnd());
199  if (attr.isA(Tags::tag_effective_start))
200  return PythonObject(getEffective().getStart());
201  return NULL;
202 }
203 
204 
206 {
207  if (attr.isA(Tags::tag_resource))
208  {
209  if (!field.check(Resource::metadata))
210  {
211  PyErr_SetString(PythonDataException, "resourceskill resource must be of type resource");
212  return -1;
213  }
214  Resource* y = static_cast<Resource*>(static_cast<PyObject*>(field));
215  setResource(y);
216  }
217  else if (attr.isA(Tags::tag_skill))
218  {
219  if (!field.check(Skill::metadata))
220  {
221  PyErr_SetString(PythonDataException, "resourceskill skill must be of type skill");
222  return -1;
223  }
224  Skill* y = static_cast<Skill*>(static_cast<PyObject*>(field));
225  setSkill(y);
226  }
227  else if (attr.isA(Tags::tag_priority))
228  setPriority(field.getInt());
229  else if (attr.isA(Tags::tag_effective_end))
230  setEffectiveEnd(field.getDate());
231  else if (attr.isA(Tags::tag_effective_start))
232  setEffectiveStart(field.getDate());
233  else
234  return -1;
235  return 0;
236 }
237 
238 
239 /** @todo this method implementation is not generic enough and not extendible by subclasses. */
240 PyObject* ResourceSkill::create(PyTypeObject* pytype, PyObject* args, PyObject* kwds)
241 {
242  try
243  {
244  // Pick up the skill
245  PyObject* skill = PyDict_GetItemString(kwds,"skill");
246  if (!PyObject_TypeCheck(skill, Skill::metadata->pythonClass))
247  throw DataException("resourceskill skill must be of type skill");
248 
249  // Pick up the resource
250  PyObject* res = PyDict_GetItemString(kwds,"resource");
251  if (!PyObject_TypeCheck(res, Resource::metadata->pythonClass))
252  throw DataException("resourceskill resource must be of type resource");
253 
254  // Pick up the priority
255  PyObject* q1 = PyDict_GetItemString(kwds,"priority");
256  int q2 = q1 ? PythonObject(q1).getInt() : 1;
257 
258  // Pick up the effective dates
259  DateRange eff;
260  PyObject* eff_start = PyDict_GetItemString(kwds,"effective_start");
261  if (eff_start)
262  {
263  PythonObject d(eff_start);
264  eff.setStart(d.getDate());
265  }
266  PyObject* eff_end = PyDict_GetItemString(kwds,"effective_end");
267  if (eff_end)
268  {
269  PythonObject d(eff_end);
270  eff.setEnd(d.getDate());
271  }
272 
273  // Create the load
274  ResourceSkill *l = new ResourceSkill(
275  static_cast<Skill*>(skill),
276  static_cast<Resource*>(res),
277  q2, eff
278  );
279 
280  // Return the object
281  Py_INCREF(l);
282  return static_cast<PyObject*>(l);
283  }
284  catch (...)
285  {
286  PythonType::evalException();
287  return NULL;
288  }
289 }
290 
291 
292 DECLARE_EXPORT void ResourceSkill::validate(Action action)
293 {
294  // Catch null operation and resource pointers
295  Skill *skill = getSkill();
296  Resource *res = getResource();
297  if (!skill || !res)
298  {
299  // Invalid load model
300  if (!skill && !res)
301  throw DataException("Missing resource and kill on a resourceskill");
302  else if (!skill)
303  throw DataException("Missing skill on a resourceskill on resource '"
304  + res->getName() + "'");
305  else if (!res)
306  throw DataException("Missing resource on a resourceskill on skill '"
307  + skill->getName() + "'");
308  }
309 
310  // Check if a resourceskill with 1) identical resource, 2) identical skill and
311  // 3) overlapping effectivity dates already exists
312  Skill::resourcelist::const_iterator i = skill->getResources().begin();
313  for (; i != skill->getResources().end(); ++i)
314  if (i->getResource() == res
315  && i->getEffective().overlap(getEffective())
316  && &*i != this)
317  break;
318 
319  // Apply the appropriate action
320  switch (action)
321  {
322  case ADD:
323  if (i != skill->getResources().end())
324  {
325  throw DataException("Resourceskill of '" + res->getName() + "' and '"
326  + skill->getName() + "' already exists");
327  }
328  break;
329  case CHANGE:
330  throw DataException("Can't update a resourceskill");
331  case ADD_CHANGE:
332  // ADD is handled in the code after the switch statement
333  if (i == skill->getResources().end()) break;
334  throw DataException("Can't update a resourceskill");
335  case REMOVE:
336  // This resourceskill was only used temporarily during the reading process
337  delete this;
338  if (i == skill->getResources().end())
339  // Nothing to delete
340  throw DataException("Can't remove nonexistent resourceskill of '"
341  + res->getName() + "' and '" + skill->getName() + "'");
342  delete &*i;
343  return;
344  }
345 }
346 
347 
349 {
350  // Initialize the type
352  x.setName("resourceSkillIterator");
353  x.setDoc("frePPLe iterator for resource skills");
354  x.supportiter();
355  return x.typeReady();
356 }
357 
358 
359 PyObject* ResourceSkillIterator::iternext()
360 {
361  PyObject* result;
362  if (res)
363  {
364  // Iterate over skills on a resource
365  if (ir == res->getSkills().end()) return NULL;
366  result = const_cast<ResourceSkill*>(&*ir);
367  ++ir;
368  }
369  else
370  {
371  // Iterate over resources having a skill
372  if (is == skill->getResources().end()) return NULL;
373  result = const_cast<ResourceSkill*>(&*is);
374  ++is;
375  }
376  Py_INCREF(result);
377  return result;
378 }
379 
380 }