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 //== INCLUDES =================================================================
45 #include <QBuffer>
46 #include <QXmlQuery>
47 #include <QXmlResultItems>
48 
49 #include "context.hh"
50 #include "input.hh"
51 #include "output.hh"
52 #include "type.hh"
53 #include "function.hh"
54 
55 #include "types/number.hh"
56 #include "types/string.hh"
57 #include "types/bool.hh"
58 #include "types/filename.hh"
59 #include "types/selection.hh"
60 #include "types/vec3d.hh"
61 #include "types/vec4d.hh"
62 #include "types/matrix4x4.hh"
63 #include "types/objectId/objectId.hh"
64 #include "types/any.hh"
65 
66 #define DATA_NAME "DataFlow"
67 
68 //== NAMESPACES ===============================================================
69 namespace VSI {
70 
71 //=============================================================================
72 //
73 // CLASS VSI::Context - IMPLEMENTATION
74 //
75 //=============================================================================
76 
78 Context::Context (QScriptEngine *_engine) :
79  scriptEngine_ (_engine)
80 {
81  // add start element
82  Element *e = new Element (this, "start");
83  elements_.append (e);
84 
85  e->category_ = "Undefined";
86 
87  e->shortDesc_ = "Start";
88  e->longDesc_ = "Start";
89 
90  e->dataOut_ = new Output (e);
91  e->dataOut_->name_ = "data";
92  e->dataOut_->type_ = "data";
93 
94  e->dataOut_->shortDesc_ = DATA_NAME;
95  e->dataOut_->longDesc_ = DATA_NAME;
96 
97  e->flags_ = ELEMENT_FLAG_NO_DELETE | ELEMENT_FLAG_SKIP_TOOLBOX;
98 
99  // add end element
100  e = new Element (this, "end");
101  elements_.append (e);
102 
103  e->category_ = "Undefined";
104 
105  e->shortDesc_ = "End";
106  e->longDesc_ = "End";
107 
108  e->dataIn_ = new Input (e);
109  e->dataIn_->name_ = "data";
110  e->dataIn_->type_ = "data";
111 
112  e->dataIn_->shortDesc_ = DATA_NAME;
113  e->dataIn_->longDesc_ = DATA_NAME;
114 
115  e->flags_ = ELEMENT_FLAG_NO_DELETE | ELEMENT_FLAG_SKIP_TOOLBOX;
116 
117 
118  // Register default types
119  registerType (new TypeNumber ());
120  registerType (new TypeBool ());
121  registerType (new TypeString ());
122  registerType (new TypeFilename ());
123  registerType (new TypeSelection ());
124  registerType (new TypeVec3D ());
125  registerType (new TypeVec4D ());
126  registerType (new TypeMatrix4x4 ());
127  registerType (new TypeObjectId ());
128  registerType (new TypeAny ());
129 }
130 
131 //------------------------------------------------------------------------------
132 
135 {
136  foreach (Element *e, elements_)
137  delete e;
138  foreach (Type *t, types_)
139  delete t;
140 }
141 
142 //------------------------------------------------------------------------------
143 
145 QVector <Element *> Context::elements (QString _category)
146 {
147  QVector <Element *> rv;
148  foreach (Element *e, elements_)
149  if (e->category () == _category)
150  rv.append (e);
151  return rv;
152 }
153 
154 //------------------------------------------------------------------------------
155 
157 Element * Context::element (QString _name)
158 {
159  foreach (Element *e, elements_)
160  if (e->name () == _name)
161  return e;
162  return NULL;
163 }
164 
165 //------------------------------------------------------------------------------
166 
168 QStringList Context::categories ()
169 {
170  QStringList sl;
171 
172  foreach (Element *e, elements_)
173  if (!sl.contains (e->category ()) && !(e->flags () & ELEMENT_FLAG_SKIP_TOOLBOX))
174  sl << e->category ();
175 
176  return sl;
177 }
178 
179 //------------------------------------------------------------------------------
180 
182 void Context::parse (QByteArray _xml)
183 {
184  QBuffer device (&_xml);
185  device.open(QIODevice::ReadOnly);
186 
187  QXmlQuery query;
188  query.setFocus (&device);
189 
190  // for each OpenFlipper/element
191  query.setQuery ("OpenFlipper//element");
192 
193  QXmlResultItems elements;
194 
195  if (query.isValid ())
196  {
197  query.evaluateTo (&elements);
198  QXmlItem item (elements.next ());
199  while (!item.isNull ())
200  {
201  QXmlQuery q (query);
202  q.setFocus (item);
203  parseElement (q);
204  item = elements.next ();
205  }
206 
207  }
208 
209 }
210 
211 //------------------------------------------------------------------------------
212 
213 // parse element from xml
214 void Context::parseElement (QXmlQuery &_xml)
215 {
216  if (getXmlString (_xml, "string(@name)").isEmpty ())
217  return;
218 
219  Element *e = new Element (this, getXmlString (_xml, "string(@name)"));
220 
221  elements_.append (e);
222 
223  e->category_ = getXmlString (_xml, "category/string(text())", "Undefined");
224 
225  e->shortDesc_ = getXmlString (_xml, "short/string(text())", e->name ());
226  e->longDesc_ = getXmlString (_xml, "long/string(text())", e->shortDesc_);
227 
228  // scene graph in/output for scenegraph elements
229  if (strToBool (getXmlString (_xml, "dataflow/string(text())")))
230  {
231 
232  e->dataIn_ = new Input (e);
233  e->dataIn_->name_ = "data";
234  e->dataIn_->type_ = QVariant::Invalid;
235 
236  e->dataIn_->shortDesc_ = DATA_NAME;
237  e->dataIn_->longDesc_ = DATA_NAME;
238  e->dataIn_->setState (Input::NoRuntimeUserInput | Input::NoUserInput);
239 
240  e->dataOut_ = new Output (e);
241  e->dataOut_->name_ = "data";
242  e->dataOut_->type_ = QVariant::Invalid;
243 
244  e->dataOut_->shortDesc_ = DATA_NAME;
245  e->dataOut_->longDesc_ = DATA_NAME;
246  }
247 
248  // parse all inputs
249  _xml.setQuery ("inputs/input");
250 
251  QXmlResultItems inouts;
252 
253  if (_xml.isValid ())
254  {
255  _xml.evaluateTo (&inouts);
256  QXmlItem item (inouts.next ());
257  while (!item.isNull ())
258  {
259  QXmlQuery q (_xml);
260  q.setFocus (item);
261  Input *i = parseInput (q, e);
262  if (i)
263  {
264  e->inputs_.append (i);
265  }
266  item = inouts.next ();
267  }
268 
269  }
270 
271  // parse all outputs
272  _xml.setQuery ("outputs/output");
273 
274  if (_xml.isValid ())
275  {
276  _xml.evaluateTo (&inouts);
277  QXmlItem item (inouts.next ());
278  while (!item.isNull ())
279  {
280  QXmlQuery q (_xml);
281  q.setFocus (item);
282  Output *o = parseOutput (q, e);
283  if (o)
284  {
285  o->element_ = e;
286  e->outputs_.append (o);
287  }
288  item = inouts.next ();
289  }
290 
291  }
292 
293  // parse all functions
294  _xml.setQuery ("functions/function");
295 
296  if (_xml.isValid ())
297  {
298  _xml.evaluateTo (&inouts);
299  QXmlItem item (inouts.next ());
300  while (!item.isNull ())
301  {
302  QXmlQuery q (_xml);
303  q.setFocus (item);
304  Function *f = parseFunction (q, e);
305  if (f)
306  {
307  e->functions_.append (f);
308  }
309  item = inouts.next ();
310  }
311 
312  }
313 
314  // get code & precode segment
315  e->precode_ = getXmlString (_xml, "precode/string(text())", "");
316  e->code_ = getXmlString (_xml, "code/string(text())", "");
317 
318  // remove spaces at line begin
319  e->precode_ = e->precode_.replace (QRegExp ("\\n\\s*"),"\n");
320  e->code_ = e->code_.replace (QRegExp ("\\n\\s*"),"\n");
321 
322 }
323 
324 //------------------------------------------------------------------------------
325 
326 // parse element input from xml
327 Input* Context::parseInput (QXmlQuery &_xml, Element *_e)
328 {
329  Input *i = new Input (_e);
330 
331  // common part
332  if (!parseInOutBase (_xml, i))
333  {
334  delete i;
335  return NULL;
336  }
337 
338  // input states
339  QString stateStr = getXmlString (_xml, "string(@external)");
340  unsigned int state = 0;
341 
342  if (!stateStr.isEmpty () && !strToBool (stateStr))
343  state |= Input::NoExternalInput;
344 
345  stateStr = getXmlString (_xml, "string(@user)");
346 
347  if (!stateStr.isEmpty () && !strToBool (stateStr))
348  state |= Input::NoUserInput;
349 
350  stateStr = getXmlString (_xml, "string(@runtime)");
351 
352  if (!stateStr.isEmpty () && !strToBool (stateStr))
353  state |= Input::NoRuntimeUserInput;
354 
355  stateStr = getXmlString (_xml, "string(@optional)");
356 
357  if (!stateStr.isEmpty () && strToBool (stateStr))
358  state |= Input::Optional;
359 
360  i->state_ = state;
361 
362  return i;
363 }
364 
365 //------------------------------------------------------------------------------
366 
367 // parse element output from xml
368 Output* Context::parseOutput (QXmlQuery &_xml, Element *_e)
369 {
370  Output *o = new Output (_e);
371 
372  // common part
373  if (!parseInOutBase (_xml, o))
374  {
375  delete o;
376  return NULL;
377  }
378 
379  return o;
380 }
381 
382 //------------------------------------------------------------------------------
383 
384 // parse common input and output parts from xml
385 bool Context::parseInOutBase (QXmlQuery &_xml, InOut *_io)
386 {
387  QString type = getXmlString (_xml, "string(@type)");
388 
389  if (getXmlString (_xml, "string(@name)").isEmpty () ||
390  type.isEmpty ())
391  return false;
392 
393  _io->name_ = getXmlString (_xml, "string(@name)");
394  _io->type_ = type;
395 
396  _io->shortDesc_ = getXmlString (_xml, "short/string(text())", _io->name ());
397  _io->longDesc_ = getXmlString (_xml, "long/string(text())", _io->shortDesc_);
398 
399  // get type hints for supported types
400  if (typeSupported (type))
401  {
402  foreach (QString hint, supportedTypes_[type]->supportedHints ())
403  {
404  QString value = getXmlString (_xml, hint + "/string(text())", "");
405  if (!value.isEmpty ())
406  _io->hints_[hint] = value;
407  }
408  }
409 
410  return true;
411 }
412 
413 //------------------------------------------------------------------------------
414 
415 // parse element function from xml
416 Function* Context::parseFunction (QXmlQuery &_xml, Element *_e)
417 {
418  QString name = getXmlString (_xml, "string(@name)");
419  if (name.isEmpty ())
420  return NULL;
421 
422  Function *f = new Function (_e, name);
423 
424  f->shortDesc_ = getXmlString (_xml, "short/string(text())", f->name ());
425  f->longDesc_ = getXmlString (_xml, "long/string(text())", f->shortDesc_);
426 
427  // add start element
428  f->start_ = new Element (_e->context (), "start_" + _e->name () + "_" + name);
429 
430  f->start_->category_ = "Undefined";
431 
432  f->start_->shortDesc_ = "Start";
433  f->start_->longDesc_ = "Start";
434 
435  f->start_->dataOut_ = new Output (f->start_);
436  f->start_->dataOut_->name_ = "data";
437  f->start_->dataOut_->type_ = "data";
438 
439  f->start_->dataOut_->shortDesc_ = DATA_NAME;
440  f->start_->dataOut_->longDesc_ = DATA_NAME;
441 
442  f->start_->flags_ = ELEMENT_FLAG_NO_DELETE | ELEMENT_FLAG_SKIP_TOOLBOX;
443 
444  elements_.append (f->start_);
445 
446  // add end element
447  f->end_ = new Element (_e->context (), "end_" + _e->name () + "_" + name);
448 
449  f->end_->category_ = "Undefined";
450 
451  f->end_->shortDesc_ = "End";
452  f->end_->longDesc_ = "End";
453 
454  f->end_->dataIn_ = new Input (f->end_);
455  f->end_->dataIn_->name_ = "data";
456  f->end_->dataIn_->type_ = "data";
457 
458  f->end_->dataIn_->shortDesc_ = DATA_NAME;
459  f->end_->dataIn_->longDesc_ = DATA_NAME;
460 
461  f->end_->flags_ = ELEMENT_FLAG_NO_DELETE | ELEMENT_FLAG_SKIP_TOOLBOX;
462 
463  elements_.append (f->end_);
464 
465  // inputs
466  _xml.setQuery ("inputs/input");
467 
468  QXmlResultItems inouts;
469 
470  if (_xml.isValid ())
471  {
472  _xml.evaluateTo (&inouts);
473  QXmlItem item (inouts.next ());
474  while (!item.isNull ())
475  {
476  QXmlQuery q (_xml);
477  q.setFocus (item);
478  Output *o = new Output (f->start_);
479  if (parseInOutBase(q, o))
480  {
481  f->start_->outputs_.append (o);
482  }
483  else
484  delete o;
485  item = inouts.next ();
486  }
487 
488  }
489 
490  // outputs
491  _xml.setQuery ("outputs/output");
492 
493  if (_xml.isValid ())
494  {
495  _xml.evaluateTo (&inouts);
496  QXmlItem item (inouts.next ());
497  while (!item.isNull ())
498  {
499  QXmlQuery q (_xml);
500  q.setFocus (item);
501  Input *i = new Input (f->end_);
502  if (parseInOutBase(q, i))
503  {
504  i->state_ = Input::NoUserInput | Input::NoRuntimeUserInput;
505  f->end_->inputs_.append (i);
506  }
507  else
508  delete i;
509  item = inouts.next ();
510  }
511 
512  }
513 
514  // add end node only if we have outputs
515  if (!f->end_->inputs ().isEmpty ())
516  {
517  // Generate end node return code
518 
519  QString endCode = "return { ";
520  foreach (Input *i, f->end_->inputs ())
521  endCode += i->name () + " : [input=\"" + i->name () + "\"], ";
522  endCode.remove (endCode.length () - 2, 2);
523  endCode += " };\n";
524 
525  f->end_->code_ = endCode;
526  }
527 
528  return f;
529 }
530 
531 //------------------------------------------------------------------------------
532 
534 QString Context::getXmlString (QXmlQuery &_xml, QString _expr, QString _default)
535 {
536  QStringList sl;
537 
538  _xml.setQuery (_expr);
539 
540  if (_xml.isValid () && _xml.evaluateTo (&sl) && sl.length () == 1)
541  return sl[0];
542 
543  return _default;
544 }
545 
546 //------------------------------------------------------------------------------
547 
549 bool Context::strToBool (QString _str)
550 {
551  if (_str == "1" || _str == "true" || _str == "True" || _str == "TRUE" ||
552  _str == "yes" || _str == "Yes" || _str == "YES")
553  return true;
554  return false;
555 }
556 
557 //------------------------------------------------------------------------------
558 
561 {
562  types_.append (_type);
563 
564  foreach (const QString &s, _type->supportedTypes())
565  supportedTypes_.insert (s, _type);
566 }
567 
568 //------------------------------------------------------------------------------
569 
571 bool Context::typeSupported(QString _type)
572 {
573  return supportedTypes_.contains (_type);
574 }
575 
576 //------------------------------------------------------------------------------
577 
579 bool Context::canConvert(QString _type1, QString _type2)
580 {
581  if (!typeSupported (_type1) || !typeSupported (_type2))
582  return false;
583  return supportedTypes_[_type1]->canConvertTo (_type2) ||
584  supportedTypes_[_type2]->canConvertTo (_type1);
585 }
586 
587 //------------------------------------------------------------------------------
588 
590 Type * Context::getType(QString _type)
591 {
592  if (typeSupported (_type))
593  return supportedTypes_[_type];
594  return NULL;
595 }
596 
597 //------------------------------------------------------------------------------
598 
599 }
unsigned int flags() const
Flags.
Definition: element.hh:109
const QString & name() const
Name.
Definition: inout.hh:70
void registerType(Type *_type)
Registers a supported datatype.
Definition: context.cc:560
static QString getXmlString(QXmlQuery &_xml, QString _expr, QString _default="")
Gets the string of a xml query.
Definition: context.cc:534
bool typeSupported(QString _type)
Is the given type supported.
Definition: context.cc:571
QString name() const
Name.
Definition: function.hh:81
bool canConvert(QString _type1, QString _type2)
Can the given types be converted to each other.
Definition: context.cc:579
Type * getType(QString _type)
Get type object for given type name.
Definition: context.cc:590
const QVector< Input * > & inputs() const
Inputs.
Definition: element.hh:94
static bool strToBool(QString _str)
Converts the given string to bool.
Definition: context.cc:549
void setState(unsigned int _state)
Sets the state.
Definition: input.hh:79
virtual QStringList supportedTypes()
Names of Types.
Definition: type.cc:71
const QVector< Element * > & elements() const
Returns all available elements.
Definition: context.hh:83
void parse(QByteArray _xml)
Parse xml content.
Definition: context.cc:182
Context * context() const
Context of element.
Definition: element.hh:79
Element * element(QString _name)
Returns the element with a given name.
Definition: context.cc:157
QString name() const
Element name.
Definition: element.hh:82
Context(QScriptEngine *_engine)
Constructor.
Definition: context.cc:78
~Context()
Destructor.
Definition: context.cc:134
const QString & category() const
Element category.
Definition: element.hh:85
QStringList categories()
List of categories.
Definition: context.cc:168