Developer Documentation
context.cc
1 /*===========================================================================*\
2 * *
3 * OpenFlipper *
4  * Copyright (c) 2001-2015, RWTH-Aachen University *
5  * Department of Computer Graphics and Multimedia *
6  * All rights reserved. *
7  * www.openflipper.org *
8  * *
9  *---------------------------------------------------------------------------*
10  * This file is part of OpenFlipper. *
11  *---------------------------------------------------------------------------*
12  * *
13  * Redistribution and use in source and binary forms, with or without *
14  * modification, are permitted provided that the following conditions *
15  * are met: *
16  * *
17  * 1. Redistributions of source code must retain the above copyright notice, *
18  * this list of conditions and the following disclaimer. *
19  * *
20  * 2. Redistributions in binary form must reproduce the above copyright *
21  * notice, this list of conditions and the following disclaimer in the *
22  * documentation and/or other materials provided with the distribution. *
23  * *
24  * 3. Neither the name of the copyright holder nor the names of its *
25  * contributors may be used to endorse or promote products derived from *
26  * this software without specific prior written permission. *
27  * *
28  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39 * *
40 \*===========================================================================*/
41 
42 /*===========================================================================*\
43 * *
44 * $Revision$ *
45 * $LastChangedBy$ *
46 * $Date$ *
47 * *
48 \*===========================================================================*/
49 
50 //== INCLUDES =================================================================
51 #include <QBuffer>
52 #include <QXmlQuery>
53 #include <QXmlResultItems>
54 
55 #include "context.hh"
56 #include "input.hh"
57 #include "output.hh"
58 #include "type.hh"
59 #include "function.hh"
60 
61 #include "types/number.hh"
62 #include "types/string.hh"
63 #include "types/bool.hh"
64 #include "types/filename.hh"
65 #include "types/selection.hh"
66 #include "types/vec3d.hh"
67 #include "types/vec4d.hh"
68 #include "types/matrix4x4.hh"
69 #include "types/objectId/objectId.hh"
70 #include "types/any.hh"
71 
72 #define DATA_NAME "DataFlow"
73 
74 //== NAMESPACES ===============================================================
75 namespace VSI {
76 
77 //=============================================================================
78 //
79 // CLASS VSI::Context - IMPLEMENTATION
80 //
81 //=============================================================================
82 
84 Context::Context (QScriptEngine *_engine) :
85  scriptEngine_ (_engine)
86 {
87  // add start element
88  Element *e = new Element (this, "start");
89  elements_.append (e);
90 
91  e->category_ = "Undefined";
92 
93  e->shortDesc_ = "Start";
94  e->longDesc_ = "Start";
95 
96  e->dataOut_ = new Output (e);
97  e->dataOut_->name_ = "data";
98  e->dataOut_->type_ = "data";
99 
100  e->dataOut_->shortDesc_ = DATA_NAME;
101  e->dataOut_->longDesc_ = DATA_NAME;
102 
103  e->flags_ = ELEMENT_FLAG_NO_DELETE | ELEMENT_FLAG_SKIP_TOOLBOX;
104 
105  // add end element
106  e = new Element (this, "end");
107  elements_.append (e);
108 
109  e->category_ = "Undefined";
110 
111  e->shortDesc_ = "End";
112  e->longDesc_ = "End";
113 
114  e->dataIn_ = new Input (e);
115  e->dataIn_->name_ = "data";
116  e->dataIn_->type_ = "data";
117 
118  e->dataIn_->shortDesc_ = DATA_NAME;
119  e->dataIn_->longDesc_ = DATA_NAME;
120 
121  e->flags_ = ELEMENT_FLAG_NO_DELETE | ELEMENT_FLAG_SKIP_TOOLBOX;
122 
123 
124  // Register default types
125  registerType (new TypeNumber ());
126  registerType (new TypeBool ());
127  registerType (new TypeString ());
128  registerType (new TypeFilename ());
129  registerType (new TypeSelection ());
130  registerType (new TypeVec3D ());
131  registerType (new TypeVec4D ());
132  registerType (new TypeMatrix4x4 ());
133  registerType (new TypeObjectId ());
134  registerType (new TypeAny ());
135 }
136 
137 //------------------------------------------------------------------------------
138 
141 {
142  foreach (Element *e, elements_)
143  delete e;
144  foreach (Type *t, types_)
145  delete t;
146 }
147 
148 //------------------------------------------------------------------------------
149 
151 QVector <Element *> Context::elements (QString _category)
152 {
153  QVector <Element *> rv;
154  foreach (Element *e, elements_)
155  if (e->category () == _category)
156  rv.append (e);
157  return rv;
158 }
159 
160 //------------------------------------------------------------------------------
161 
163 Element * Context::element (QString _name)
164 {
165  foreach (Element *e, elements_)
166  if (e->name () == _name)
167  return e;
168  return NULL;
169 }
170 
171 //------------------------------------------------------------------------------
172 
174 QStringList Context::categories ()
175 {
176  QStringList sl;
177 
178  foreach (Element *e, elements_)
179  if (!sl.contains (e->category ()) && !(e->flags () & ELEMENT_FLAG_SKIP_TOOLBOX))
180  sl << e->category ();
181 
182  return sl;
183 }
184 
185 //------------------------------------------------------------------------------
186 
188 void Context::parse (QByteArray _xml)
189 {
190  QBuffer device (&_xml);
191  device.open(QIODevice::ReadOnly);
192 
193  QXmlQuery query;
194  query.setFocus (&device);
195 
196  // for each OpenFlipper/element
197  query.setQuery ("OpenFlipper//element");
198 
199  QXmlResultItems elements;
200 
201  if (query.isValid ())
202  {
203  query.evaluateTo (&elements);
204  QXmlItem item (elements.next ());
205  while (!item.isNull ())
206  {
207  QXmlQuery q (query);
208  q.setFocus (item);
209  parseElement (q);
210  item = elements.next ();
211  }
212 
213  }
214 
215 }
216 
217 //------------------------------------------------------------------------------
218 
219 // parse element from xml
220 void Context::parseElement (QXmlQuery &_xml)
221 {
222  if (getXmlString (_xml, "string(@name)").isEmpty ())
223  return;
224 
225  Element *e = new Element (this, getXmlString (_xml, "string(@name)"));
226 
227  elements_.append (e);
228 
229  e->category_ = getXmlString (_xml, "category/string(text())", "Undefined");
230 
231  e->shortDesc_ = getXmlString (_xml, "short/string(text())", e->name ());
232  e->longDesc_ = getXmlString (_xml, "long/string(text())", e->shortDesc_);
233 
234  // scene graph in/output for scenegraph elements
235  if (strToBool (getXmlString (_xml, "dataflow/string(text())")))
236  {
237 
238  e->dataIn_ = new Input (e);
239  e->dataIn_->name_ = "data";
240  e->dataIn_->type_ = QVariant::Invalid;
241 
242  e->dataIn_->shortDesc_ = DATA_NAME;
243  e->dataIn_->longDesc_ = DATA_NAME;
244  e->dataIn_->setState (Input::NoRuntimeUserInput | Input::NoUserInput);
245 
246  e->dataOut_ = new Output (e);
247  e->dataOut_->name_ = "data";
248  e->dataOut_->type_ = QVariant::Invalid;
249 
250  e->dataOut_->shortDesc_ = DATA_NAME;
251  e->dataOut_->longDesc_ = DATA_NAME;
252  }
253 
254  // parse all inputs
255  _xml.setQuery ("inputs/input");
256 
257  QXmlResultItems inouts;
258 
259  if (_xml.isValid ())
260  {
261  _xml.evaluateTo (&inouts);
262  QXmlItem item (inouts.next ());
263  while (!item.isNull ())
264  {
265  QXmlQuery q (_xml);
266  q.setFocus (item);
267  Input *i = parseInput (q, e);
268  if (i)
269  {
270  e->inputs_.append (i);
271  }
272  item = inouts.next ();
273  }
274 
275  }
276 
277  // parse all outputs
278  _xml.setQuery ("outputs/output");
279 
280  if (_xml.isValid ())
281  {
282  _xml.evaluateTo (&inouts);
283  QXmlItem item (inouts.next ());
284  while (!item.isNull ())
285  {
286  QXmlQuery q (_xml);
287  q.setFocus (item);
288  Output *o = parseOutput (q, e);
289  if (o)
290  {
291  o->element_ = e;
292  e->outputs_.append (o);
293  }
294  item = inouts.next ();
295  }
296 
297  }
298 
299  // parse all functions
300  _xml.setQuery ("functions/function");
301 
302  if (_xml.isValid ())
303  {
304  _xml.evaluateTo (&inouts);
305  QXmlItem item (inouts.next ());
306  while (!item.isNull ())
307  {
308  QXmlQuery q (_xml);
309  q.setFocus (item);
310  Function *f = parseFunction (q, e);
311  if (f)
312  {
313  e->functions_.append (f);
314  }
315  item = inouts.next ();
316  }
317 
318  }
319 
320  // get code & precode segment
321  e->precode_ = getXmlString (_xml, "precode/string(text())", "");
322  e->code_ = getXmlString (_xml, "code/string(text())", "");
323 
324  // remove spaces at line begin
325  e->precode_ = e->precode_.replace (QRegExp ("\\n\\s*"),"\n");
326  e->code_ = e->code_.replace (QRegExp ("\\n\\s*"),"\n");
327 
328 }
329 
330 //------------------------------------------------------------------------------
331 
332 // parse element input from xml
333 Input* Context::parseInput (QXmlQuery &_xml, Element *_e)
334 {
335  Input *i = new Input (_e);
336 
337  // common part
338  if (!parseInOutBase (_xml, i))
339  {
340  delete i;
341  return NULL;
342  }
343 
344  // input states
345  QString stateStr = getXmlString (_xml, "string(@external)");
346  unsigned int state = 0;
347 
348  if (!stateStr.isEmpty () && !strToBool (stateStr))
349  state |= Input::NoExternalInput;
350 
351  stateStr = getXmlString (_xml, "string(@user)");
352 
353  if (!stateStr.isEmpty () && !strToBool (stateStr))
354  state |= Input::NoUserInput;
355 
356  stateStr = getXmlString (_xml, "string(@runtime)");
357 
358  if (!stateStr.isEmpty () && !strToBool (stateStr))
359  state |= Input::NoRuntimeUserInput;
360 
361  stateStr = getXmlString (_xml, "string(@optional)");
362 
363  if (!stateStr.isEmpty () && strToBool (stateStr))
364  state |= Input::Optional;
365 
366  i->state_ = state;
367 
368  return i;
369 }
370 
371 //------------------------------------------------------------------------------
372 
373 // parse element output from xml
374 Output* Context::parseOutput (QXmlQuery &_xml, Element *_e)
375 {
376  Output *o = new Output (_e);
377 
378  // common part
379  if (!parseInOutBase (_xml, o))
380  {
381  delete o;
382  return NULL;
383  }
384 
385  return o;
386 }
387 
388 //------------------------------------------------------------------------------
389 
390 // parse common input and output parts from xml
391 bool Context::parseInOutBase (QXmlQuery &_xml, InOut *_io)
392 {
393  QString type = getXmlString (_xml, "string(@type)");
394 
395  if (getXmlString (_xml, "string(@name)").isEmpty () ||
396  type.isEmpty ())
397  return false;
398 
399  _io->name_ = getXmlString (_xml, "string(@name)");
400  _io->type_ = type;
401 
402  _io->shortDesc_ = getXmlString (_xml, "short/string(text())", _io->name ());
403  _io->longDesc_ = getXmlString (_xml, "long/string(text())", _io->shortDesc_);
404 
405  // get type hints for supported types
406  if (typeSupported (type))
407  {
408  foreach (QString hint, supportedTypes_[type]->supportedHints ())
409  {
410  QString value = getXmlString (_xml, hint + "/string(text())", "");
411  if (!value.isEmpty ())
412  _io->hints_[hint] = value;
413  }
414  }
415 
416  return true;
417 }
418 
419 //------------------------------------------------------------------------------
420 
421 // parse element function from xml
422 Function* Context::parseFunction (QXmlQuery &_xml, Element *_e)
423 {
424  QString name = getXmlString (_xml, "string(@name)");
425  if (name.isEmpty ())
426  return NULL;
427 
428  Function *f = new Function (_e, name);
429 
430  f->shortDesc_ = getXmlString (_xml, "short/string(text())", f->name ());
431  f->longDesc_ = getXmlString (_xml, "long/string(text())", f->shortDesc_);
432 
433  // add start element
434  f->start_ = new Element (_e->context (), "start_" + _e->name () + "_" + name);
435 
436  f->start_->category_ = "Undefined";
437 
438  f->start_->shortDesc_ = "Start";
439  f->start_->longDesc_ = "Start";
440 
441  f->start_->dataOut_ = new Output (f->start_);
442  f->start_->dataOut_->name_ = "data";
443  f->start_->dataOut_->type_ = "data";
444 
445  f->start_->dataOut_->shortDesc_ = DATA_NAME;
446  f->start_->dataOut_->longDesc_ = DATA_NAME;
447 
448  f->start_->flags_ = ELEMENT_FLAG_NO_DELETE | ELEMENT_FLAG_SKIP_TOOLBOX;
449 
450  elements_.append (f->start_);
451 
452  // add end element
453  f->end_ = new Element (_e->context (), "end_" + _e->name () + "_" + name);
454 
455  f->end_->category_ = "Undefined";
456 
457  f->end_->shortDesc_ = "End";
458  f->end_->longDesc_ = "End";
459 
460  f->end_->dataIn_ = new Input (f->end_);
461  f->end_->dataIn_->name_ = "data";
462  f->end_->dataIn_->type_ = "data";
463 
464  f->end_->dataIn_->shortDesc_ = DATA_NAME;
465  f->end_->dataIn_->longDesc_ = DATA_NAME;
466 
467  f->end_->flags_ = ELEMENT_FLAG_NO_DELETE | ELEMENT_FLAG_SKIP_TOOLBOX;
468 
469  elements_.append (f->end_);
470 
471  // inputs
472  _xml.setQuery ("inputs/input");
473 
474  QXmlResultItems inouts;
475 
476  if (_xml.isValid ())
477  {
478  _xml.evaluateTo (&inouts);
479  QXmlItem item (inouts.next ());
480  while (!item.isNull ())
481  {
482  QXmlQuery q (_xml);
483  q.setFocus (item);
484  Output *o = new Output (f->start_);
485  if (parseInOutBase(q, o))
486  {
487  f->start_->outputs_.append (o);
488  }
489  else
490  delete o;
491  item = inouts.next ();
492  }
493 
494  }
495 
496  // outputs
497  _xml.setQuery ("outputs/output");
498 
499  if (_xml.isValid ())
500  {
501  _xml.evaluateTo (&inouts);
502  QXmlItem item (inouts.next ());
503  while (!item.isNull ())
504  {
505  QXmlQuery q (_xml);
506  q.setFocus (item);
507  Input *i = new Input (f->end_);
508  if (parseInOutBase(q, i))
509  {
510  i->state_ = Input::NoUserInput | Input::NoRuntimeUserInput;
511  f->end_->inputs_.append (i);
512  }
513  else
514  delete i;
515  item = inouts.next ();
516  }
517 
518  }
519 
520  // add end node only if we have outputs
521  if (!f->end_->inputs ().isEmpty ())
522  {
523  // Generate end node return code
524 
525  QString endCode = "return { ";
526  foreach (Input *i, f->end_->inputs ())
527  endCode += i->name () + " : [input=\"" + i->name () + "\"], ";
528  endCode.remove (endCode.length () - 2, 2);
529  endCode += " };\n";
530 
531  f->end_->code_ = endCode;
532  }
533 
534  return f;
535 }
536 
537 //------------------------------------------------------------------------------
538 
540 QString Context::getXmlString (QXmlQuery &_xml, QString _expr, QString _default)
541 {
542  QStringList sl;
543 
544  _xml.setQuery (_expr);
545 
546  if (_xml.isValid () && _xml.evaluateTo (&sl) && sl.length () == 1)
547  return sl[0];
548 
549  return _default;
550 }
551 
552 //------------------------------------------------------------------------------
553 
555 bool Context::strToBool (QString _str)
556 {
557  if (_str == "1" || _str == "true" || _str == "True" || _str == "TRUE" ||
558  _str == "yes" || _str == "Yes" || _str == "YES")
559  return true;
560  return false;
561 }
562 
563 //------------------------------------------------------------------------------
564 
567 {
568  types_.append (_type);
569 
570  foreach (const QString &s, _type->supportedTypes())
571  supportedTypes_.insert (s, _type);
572 }
573 
574 //------------------------------------------------------------------------------
575 
577 bool Context::typeSupported(QString _type)
578 {
579  return supportedTypes_.contains (_type);
580 }
581 
582 //------------------------------------------------------------------------------
583 
585 bool Context::canConvert(QString _type1, QString _type2)
586 {
587  if (!typeSupported (_type1) || !typeSupported (_type2))
588  return false;
589  return supportedTypes_[_type1]->canConvertTo (_type2) ||
590  supportedTypes_[_type2]->canConvertTo (_type1);
591 }
592 
593 //------------------------------------------------------------------------------
594 
596 Type * Context::getType(QString _type)
597 {
598  if (typeSupported (_type))
599  return supportedTypes_[_type];
600  return NULL;
601 }
602 
603 //------------------------------------------------------------------------------
604 
605 }
unsigned int flags() const
Flags.
Definition: element.hh:115
const QVector< Input * > & inputs() const
Inputs.
Definition: element.hh:100
const QString & category() const
Element category.
Definition: element.hh:91
QString name() const
Name.
Definition: function.hh:87
const QVector< Element * > & elements() const
Returns all available elements.
Definition: context.hh:89
bool typeSupported(QString _type)
Is the given type supported.
Definition: context.cc:577
QStringList categories()
List of categories.
Definition: context.cc:174
void parse(QByteArray _xml)
Parse xml content.
Definition: context.cc:188
Context * context() const
Context of element.
Definition: element.hh:85
Element * element(QString _name)
Returns the element with a given name.
Definition: context.cc:163
static bool strToBool(QString _str)
Converts the given string to bool.
Definition: context.cc:555
const QString & name() const
Name.
Definition: inout.hh:76
Context(QScriptEngine *_engine)
Constructor.
Definition: context.cc:84
bool canConvert(QString _type1, QString _type2)
Can the given types be converted to each other.
Definition: context.cc:585
QString name() const
Element name.
Definition: element.hh:88
void setState(unsigned int _state)
Sets the state.
Definition: input.hh:85
static QString getXmlString(QXmlQuery &_xml, QString _expr, QString _default="")
Gets the string of a xml query.
Definition: context.cc:540
virtual QStringList supportedTypes()
Names of Types.
Definition: type.cc:77
void registerType(Type *_type)
Registers a supported datatype.
Definition: context.cc:566
Type * getType(QString _type)
Get type object for given type name.
Definition: context.cc:596
~Context()
Destructor.
Definition: context.cc:140