Xcos MVC: slight improvements in Adapters
[scilab.git] / scilab / modules / scicos / src / cpp / view_scilab / ports_management.hxx
1 /*
2  *  Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  *  Copyright (C) 2014 - Scilab Enterprises - Paul Bignier
4  *  Copyright (C) 2014 - Scilab Enterprises - Clement DAVID
5  *
6  *  This file must be used under the terms of the CeCILL.
7  *  This source file is licensed as described in the file COPYING, which
8  *  you should have received as part of this distribution.  The terms
9  *  are also available at
10  *  http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
11  *
12  */
13
14 #ifndef PORTS_MANAGEMENT_HXX_
15 #define PORTS_MANAGEMENT_HXX_
16
17 #include <string>
18 #include <vector>
19 #include <algorithm>
20
21 #include "internal.hxx"
22 #include "bool.hxx"
23 #include "double.hxx"
24 #include "string.hxx"
25
26 #include "Controller.hxx"
27 #include "model/Port.hxx"
28
29 extern "C" {
30 #include "sci_malloc.h"
31 #include "charEncoding.h"
32 }
33
34 namespace org_scilab_modules_scicos
35 {
36 namespace view_scilab
37 {
38
39 /*
40  * Return a Scilab encoded value for a property.
41  */
42 template<typename Adaptor, object_properties_t p>
43 types::InternalType* get_ports_property(const Adaptor& adaptor, const object_properties_t port_kind, const Controller& controller)
44 {
45     ScicosID adaptee = adaptor.getAdaptee()->id();
46
47     // Retrieve the identifiers
48     std::vector<ScicosID> ids;
49     controller.getObjectProperty(adaptee, BLOCK, port_kind, ids);
50
51     // Translate identifiers: shared variables
52     int i = 0;
53     size_t datatypeIndex = -1;
54     // Translate identifiers to return values
55     switch (p)
56     {
57         case STYLE:
58         case LABEL:
59         {
60             types::String* o = new types::String((int)ids.size(), 1);
61             for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
62             {
63                 std::string s;
64                 controller.getObjectProperty(*it, PORT, p, s);
65                 o->set(i, s.data());
66             }
67             return o;
68         }
69         case DATATYPE_TYPE:
70             // The type defaults to [1] if no port has been defined
71             if (ids.empty())
72             {
73                 return new types::Double(1);
74             }
75             datatypeIndex++;
76             // no break
77         case DATATYPE_COLS:
78             datatypeIndex++;
79             // no break
80         case DATATYPE_ROWS:
81         {
82             datatypeIndex++;
83             double* data;
84             types::Double* o = new types::Double((int)ids.size(), 1, &data);
85             for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
86             {
87                 std::vector<int> v;
88                 controller.getObjectProperty(*it, PORT, DATATYPE, v);
89                 data[i] = v[datatypeIndex];
90             }
91             return o;
92         }
93         case FIRING:
94         {
95             double* data;
96             types::Double* o = new types::Double((int)ids.size(), 1, &data);
97             for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
98             {
99                 controller.getObjectProperty(*it, PORT, p, data[i]);
100             }
101             return o;
102         }
103         case IMPLICIT:
104         {
105             if (ids.size() == 0)
106             {
107                 // When no port is present, return an empty matrix
108                 return types::Double::Empty();
109             }
110
111             types::String* o = new types::String((int)ids.size(), 1);
112             for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
113             {
114                 bool v;
115                 controller.getObjectProperty(*it, PORT, p, v);
116                 o->set(i, (v == false) ? L"E" : L"I");
117             }
118             return o;
119         }
120         case CONNECTED_SIGNALS:
121         {
122             double* v;
123             types::Double* o = new types::Double((int)ids.size(), 1, &v);
124
125             ScicosID diagram;
126             controller.getObjectProperty(adaptee, BLOCK, PARENT_DIAGRAM, diagram);
127
128             std::vector<ScicosID> children;
129             if (diagram != 0)
130             {
131                 controller.getObjectProperty(diagram, DIAGRAM, CHILDREN, children);
132             }
133
134             for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
135             {
136                 ScicosID id;
137                 controller.getObjectProperty(*it, PORT, p, id);
138
139                 std::vector<ScicosID>::iterator found = std::find(children.begin(), children.end(), id);
140
141                 if (found != children.end())
142                 {
143                     v[i] = (double)std::distance(children.begin(), found) + 1;
144                 }
145                 else
146                 {
147                     v[i] = 0;
148                 }
149             }
150             return o;
151         }
152         default:
153             return 0;
154     }
155 }
156
157 /*
158  * Set a Scilab encoded values as a property.
159  *
160  * \note this method will ignore or return false if one of the ports does not exist, depending on the property setted.
161  */
162 template<typename Adaptor, object_properties_t p>
163 bool set_ports_property(const Adaptor& adaptor, const object_properties_t port_kind, Controller& controller, types::InternalType* v)
164 {
165     ScicosID adaptee = adaptor.getAdaptee()->id();
166
167     // Retrieve the ports identifiers
168     std::vector<ScicosID> ids;
169     controller.getObjectProperty(adaptee, BLOCK, port_kind, ids);
170
171     if (v->getType() == types::InternalType::ScilabString)
172     {
173         types::String* current = v->getAs<types::String>();
174
175         // Translate identifiers: shared variables
176         int i = 0;
177         // Translate identifiers from values
178         switch (p)
179         {
180             case STYLE:
181             case LABEL:
182             {
183                 for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
184                 {
185                     char* c_str = NULL;
186                     if (i >= current->getSize())
187                     {
188                         // If the input isn't large enough, fill each port with empty strings
189                         c_str = wide_string_to_UTF8(L"");
190                     }
191                     else
192                     {
193                         c_str = wide_string_to_UTF8(current->get(i));
194                     }
195                     controller.setObjectProperty(*it, PORT, p, std::string(c_str));
196                     FREE(c_str);
197                 }
198                 return true;
199             }
200             case IMPLICIT:
201             {
202                 if (current->getSize() < static_cast<int>(ids.size()))
203                 {
204                     return false;
205                 }
206
207                 std::wstring E = L"E";
208                 std::wstring I = L"I";
209                 for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
210                 {
211                     if (current->get(i) == I)
212                     {
213                         controller.setObjectProperty(*it, PORT, p, true);
214                     }
215                     else if (current->get(i) == E)
216                     {
217                         controller.setObjectProperty(*it, PORT, p, false);
218                     }
219                     else
220                     {
221                         return false;
222                     }
223                 }
224                 return true;
225             }
226             default:
227                 return false;
228         }
229     }
230     else if (v->getType() == types::InternalType::ScilabDouble)
231     {
232         types::Double* current = v->getAs<types::Double>();
233
234         // Translate identifiers: shared variables
235         int i = 0;
236         size_t datatypeIndex = -1;
237         // Translate identifiers from values
238         switch (p)
239         {
240             case FIRING:
241                 if (current->isEmpty())
242                 {
243                     return true;
244                 }
245
246                 if (current->getSize() < static_cast<int>(ids.size()))
247                 {
248                     return false;
249                 }
250
251                 for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
252                 {
253                     double firing = current->get(i);
254
255                     controller.setObjectProperty(*it, PORT, p, firing);
256                 }
257                 return true;
258             case STYLE:
259             case LABEL:
260                 // Do nothing, because if the sizes match, then there are already zero concerned ports, so no ports to update
261                 return true;
262
263             case DATATYPE_TYPE:
264                 datatypeIndex++;
265                 // no break
266             case DATATYPE_COLS:
267                 datatypeIndex++;
268                 // no break
269             case DATATYPE_ROWS:
270             {
271                 datatypeIndex++;
272
273                 // ignore the set without error
274                 if (current->getSize() != static_cast<int>(ids.size()))
275                 {
276                     return true;
277                 }
278
279                 for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it, ++i)
280                 {
281                     std::vector<int> newDataType;
282                     controller.getObjectProperty(*it, PORT, DATATYPE, newDataType);
283
284                     double data = current->get(i);
285                     if (std::floor(data) != data)
286                     {
287                         return false;
288                     }
289
290                     newDataType[datatypeIndex] = static_cast<int>(data);
291                     controller.setObjectProperty(*it, PORT, DATATYPE, newDataType);
292                 }
293                 return true;
294             }
295
296             case IMPLICIT:
297                 // Do nothing, because if the sizes match, then there are already zero concerned ports, so no ports to update
298                 return true;
299         }
300
301     }
302     else if (v->getType() == types::InternalType::ScilabBool)
303     {
304         switch (p)
305         {
306             case FIRING:
307                 // firing=%f is interpreted as "no initial event on the corresponding port", so set a negative value.
308                 for (std::vector<ScicosID>::iterator it = ids.begin(); it != ids.end(); ++it)
309                 {
310                     controller.setObjectProperty(*it, PORT, p, -1);
311                 }
312                 return true;
313             default:
314                 return false;
315         }
316     }
317     return false;
318 }
319
320 /**
321  * Fill \a newPorts with \a d values checking content if possible.
322  *
323  * \param newPorts new ports children's index or value to be filled
324  * \param children all object in the current layer (diagram or superblock)
325  * \param d the C-array values to set
326  * \return true on success, false otherwise
327  */
328 template<typename Adaptor, object_properties_t p>
329 inline bool fillNewPorts(std::vector<int>& newPorts, const std::vector<ScicosID>& children, const double* d)
330 {
331     for (std::vector<int>::iterator it = newPorts.begin(); it != newPorts.end(); ++it, ++d)
332     {
333
334         if (p == CONNECTED_SIGNALS)   // the associated link must exist
335         {
336             if (0 > *d && *d >= children.size())
337             {
338                 return false;
339             }
340             *it = static_cast<int>(*d - 1); // 'd' contains indexes
341         } // no check is performed for other properties as newPorts will contains value not index
342         else
343         {
344             *it = static_cast<int>(*d);
345         }
346     }
347     return true;
348 }
349
350 /**
351  * Set the port value
352  *
353  * \param oldPort the old port object ID
354  * \param newPort new port children's index or value
355  * \param controller current transaction instance
356  * \param children all object in the current layer (diagram or superblock)
357  * \param deletedObjects trash used to delete objects
358  */
359 template<typename Adaptor, object_properties_t p>
360 inline bool updateNewPort(const ScicosID oldPort, int newPort, Controller& controller,
361                           std::vector<ScicosID>& children, std::vector<ScicosID>& deletedObjects)
362 {
363     if (p == CONNECTED_SIGNALS)
364     {
365         // update signal and manage deconnection, using newPort as a children index
366         ScicosID oldSignal;
367         controller.getObjectProperty(oldPort, PORT, CONNECTED_SIGNALS, oldSignal);
368
369         ScicosID newSignal;
370         if (children.size() > 0 && newPort >= 0)
371         {
372             newSignal = children[newPort];
373         }
374         else
375         {
376             newSignal = 0;
377         }
378
379         if (oldSignal != newSignal)
380         {
381             if (oldSignal == 0)
382             {
383                 // FIXME: The port was not linked, check if Link #newSignal has an unconnected end that is connectable to the port (Link kind)
384                 return false;
385             }
386             // disconnect the old link
387             ScicosID oldSignalSrc;
388             controller.getObjectProperty(oldSignal, LINK, SOURCE_PORT, oldSignalSrc);
389             ScicosID oldSignalDst;
390             controller.getObjectProperty(oldSignal, LINK, DESTINATION_PORT, oldSignalDst);
391             ScicosID unconnected = 0;
392             if (oldSignalSrc == oldPort)
393             {
394                 controller.setObjectProperty(oldSignalDst, PORT, CONNECTED_SIGNALS, unconnected);
395             }
396             else   // oldSignalDst == oldPort
397             {
398                 controller.setObjectProperty(oldSignalSrc, PORT, CONNECTED_SIGNALS, unconnected);
399             }
400             // Link de-association is not performed as the link will be removed
401             // connect the new link if there is one
402             if (newSignal != 0)
403             {
404                 controller.setObjectProperty(newSignal, LINK, SOURCE_PORT, 0);
405                 controller.setObjectProperty(oldPort, PORT, CONNECTED_SIGNALS, newSignal);
406                 children.erase(std::find(children.begin(), children.end(), oldSignal));
407                 deletedObjects.push_back(oldSignal);
408             }
409         }
410     }
411     else
412     {
413         // update the p property, using newPort as a value
414         int datatypeIndex = -1;
415         switch (p)
416         {
417             case DATATYPE_TYPE:
418                 datatypeIndex++;
419                 // no break
420             case DATATYPE_COLS:
421                 datatypeIndex++;
422                 // no break
423             case DATATYPE_ROWS:
424             {
425                 datatypeIndex++;
426                 std::vector<int> datatype;
427                 controller.getObjectProperty(oldPort, PORT, DATATYPE, datatype);
428                 datatype[datatypeIndex] = newPort;
429                 controller.setObjectProperty(oldPort, PORT, DATATYPE, datatype);
430                 return true;
431             }
432             default:
433                 controller.setObjectProperty(oldPort, PORT, p, newPort);
434         }
435     }
436     return true;
437 }
438
439 /**
440  * Add a new port
441  *
442  * \param newPortID the old port object ID
443  * \param newPort new port children's index or value
444  * \param children all object in the current layer (diagram or superblock)
445  * \param controller current transaction instance
446  * \return true on success, false otherwise
447  */
448 template<typename Adaptor, object_properties_t p>
449 inline bool addNewPort(const ScicosID newPortID, int newPort, const std::vector<ScicosID>& children, Controller& controller)
450 {
451     bool status = true;
452     if (p == CONNECTED_SIGNALS)
453     {
454         // set the connected signal if applicable, using newPort as a children index
455         if (children.size() > 0)
456         {
457             ScicosID signal = children[newPort];
458             status = controller.setObjectProperty(newPortID, PORT, CONNECTED_SIGNALS, signal) != FAIL;
459         }
460     }
461     else
462     {
463         // set the requested property, using newPort as a value
464         int datatypeIndex = -1;
465         switch (p)
466         {
467             case DATATYPE_TYPE:
468                 datatypeIndex++;
469                 // no break
470             case DATATYPE_COLS:
471                 datatypeIndex++;
472                 // no break
473             case DATATYPE_ROWS:
474             {
475                 datatypeIndex++;
476                 std::vector<int> datatype;
477                 controller.getObjectProperty(newPortID, PORT, DATATYPE, datatype);
478                 datatype[datatypeIndex] = newPort;
479                 return controller.setObjectProperty(newPortID, PORT, DATATYPE, datatype) != FAIL;
480             }
481             default:
482                 return controller.setObjectProperty(newPortID, PORT, p, newPort) != FAIL;
483         }
484     }
485
486     return status;
487 }
488
489 /**
490  * Update the ports with a specific property.
491  *
492  * Create ports if needed, remove ports if needed and set a default property on each port.
493  */
494 template<typename Adaptor, object_properties_t p>
495 bool update_ports_property(const Adaptor& adaptor, const object_properties_t port_kind, Controller& controller, types::InternalType* v)
496 {
497     ScicosID adaptee = adaptor.getAdaptee()->id();
498
499     if (v->getType() != types::InternalType::ScilabDouble)
500     {
501         return false;
502     }
503     types::Double* value = v->getAs<types::Double>();
504
505     ScicosID parentDiagram;
506     controller.getObjectProperty(adaptee, BLOCK, PARENT_DIAGRAM, parentDiagram);
507
508     std::vector<ScicosID> children;
509     if (parentDiagram != 0)
510     {
511         controller.getObjectProperty(parentDiagram, DIAGRAM, CHILDREN, children);
512     }
513
514     std::vector<int> newPorts (value->getSize());
515
516     // retrieve old data
517     std::vector<ScicosID> oldPorts;
518     controller.getObjectProperty(adaptee, BLOCK, port_kind, oldPorts);
519     std::vector<ScicosID> previousPorts = oldPorts;
520
521     double* d = value->getReal();
522     if (!fillNewPorts<Adaptor, p>(newPorts, children, d))
523     {
524         return false;
525     }
526
527     std::vector<ScicosID> deletedObjects;
528
529     // updated ports
530     while (!oldPorts.empty() && !newPorts.empty())
531     {
532         ScicosID oldPort = oldPorts.back();
533         oldPorts.pop_back();
534         int newPort = newPorts.back();
535         newPorts.pop_back();
536
537         if (!updateNewPort<Adaptor, p>(oldPort, newPort, controller, children, deletedObjects))
538         {
539             return false;
540         }
541     }
542
543     // removed ports
544     if (!oldPorts.empty())
545     {
546         previousPorts.erase(previousPorts.begin() + oldPorts.size(), previousPorts.end());
547
548         while (!oldPorts.empty())
549         {
550             ScicosID oldPort = oldPorts.back();
551             oldPorts.pop_back();
552
553             ScicosID signal;
554             controller.getObjectProperty(oldPort, PORT, CONNECTED_SIGNALS, signal);
555             if (signal != 0)
556             {
557                 // the link is connected, disconnect the other side
558                 ScicosID oldSignalSrc;
559                 controller.getObjectProperty(signal, LINK, SOURCE_PORT, oldSignalSrc);
560                 ScicosID oldSignalDst;
561                 controller.getObjectProperty(signal, LINK, DESTINATION_PORT, oldSignalDst);
562
563                 ScicosID unconnected = 0;
564                 if (oldSignalSrc == oldPort)
565                 {
566                     controller.setObjectProperty(oldSignalDst, PORT, CONNECTED_SIGNALS, unconnected);
567                 }
568                 else     // oldSignalDst == oldPort
569                 {
570                     controller.setObjectProperty(oldSignalSrc, PORT, CONNECTED_SIGNALS, unconnected);
571                 }
572
573                 children.erase(std::find(children.begin(), children.end(), signal));
574                 deletedObjects.push_back(signal);
575             }
576
577             deletedObjects.push_back(oldPort);
578         }
579
580         controller.setObjectProperty(adaptee, BLOCK, port_kind, previousPorts);
581     }
582
583     // added ports
584     if (!newPorts.empty())
585     {
586         while (!newPorts.empty())
587         {
588             int newPort = newPorts.back();
589             newPorts.pop_back();
590
591             ScicosID id = controller.createObject(PORT);
592             controller.setObjectProperty(id, PORT, SOURCE_BLOCK, adaptee);
593             switch (port_kind)
594             {
595                 case INPUTS:
596                     controller.setObjectProperty(id, PORT, PORT_KIND, model::PORT_IN);
597                     break;
598                 case OUTPUTS:
599                     controller.setObjectProperty(id, PORT, PORT_KIND, model::PORT_OUT);
600                     break;
601                 case EVENT_INPUTS:
602                     controller.setObjectProperty(id, PORT, PORT_KIND, model::PORT_EIN);
603                     break;
604                 case EVENT_OUTPUTS:
605                     controller.setObjectProperty(id, PORT, PORT_KIND, model::PORT_EOUT);
606                     break;
607                 default:
608                     return false;
609             }
610             addNewPort<Adaptor, p>(id, newPort, children, controller);
611             previousPorts.push_back(id);
612         }
613
614         controller.setObjectProperty(adaptee, BLOCK, port_kind, previousPorts);
615     }
616
617     // remove objects from the model after de-association
618     if (parentDiagram != 0)
619     {
620         controller.setObjectProperty(parentDiagram, DIAGRAM, CHILDREN, children);
621     }
622     for (std::vector<ScicosID>::iterator it = deletedObjects.begin(); it != deletedObjects.end(); ++it)
623     {
624         controller.deleteObject(*it);
625     }
626
627     return true;
628 }
629
630 } /* namespace view_scilab */
631 } /* namespace org_scilab_modules_scicos */
632
633 #endif /* PORTS_MANAGEMENT_HXX_ */