Scicos: speedup model transformation
[scilab.git] / scilab / modules / scicos / src / cpp / view_scilab / LinkAdapter.cpp
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2014-2016 - Scilab Enterprises - Clement DAVID
4  * Copyright (C) 2017 - ESI Group - Clement DAVID
5  *
6  * Copyright (C) 2012 - 2016 - Scilab Enterprises
7  *
8  * This file is hereby licensed under the terms of the GNU GPL v2.0,
9  * pursuant to article 5.3.4 of the CeCILL v.2.1.
10  * This file was originally licensed under the terms of the CeCILL v2.1,
11  * and continues to be available under such terms.
12  * For more information, see the COPYING file which you should have received
13  * along with this program.
14  *
15  */
16
17 #include <string>
18 #include <vector>
19 #include <map>
20 #include <iterator>
21 #include <algorithm>
22
23 #include "internal.hxx"
24 #include "list.hxx"
25 #include "types.hxx"
26 #include "user.hxx"
27 #include "double.hxx"
28
29 #include "utilities.hxx"
30 #include "adapters_utilities.hxx"
31 #include "Controller.hxx"
32 #include "LinkAdapter.hxx"
33 #include "controller_helpers.hxx"
34 #include "model/Link.hxx"
35 #include "model/Port.hxx"
36 #include "model/Block.hxx"
37
38 extern "C" {
39 #include "sci_malloc.h"
40 #include "charEncoding.h"
41 #include "localization.h"
42 }
43
44 namespace org_scilab_modules_scicos
45 {
46 namespace view_scilab
47 {
48 namespace
49 {
50
51 const std::string split ("split");
52 const std::string lsplit ("lsplit");
53 const std::string limpsplit ("limpsplit");
54
55 // shared information for relinking across adapters hierarchy
56 std::map<ScicosID, partial_link_t> partial_links;
57
58 struct xx
59 {
60
61     static types::InternalType* get(const LinkAdapter& adaptor, const Controller& controller)
62     {
63         model::Link* adaptee = adaptor.getAdaptee();
64
65         std::vector<double> controlPoints;
66         controller.getObjectProperty(adaptee, CONTROL_POINTS, controlPoints);
67
68         double* data;
69         int size = (int)controlPoints.size() / 2;
70         types::Double* o = new types::Double(size, 1, &data);
71
72         for (int i = 0; i < size; ++i)
73         {
74             data[i] = controlPoints[2 * i];
75         }
76         return o;
77     }
78
79     static bool set(LinkAdapter& adaptor, types::InternalType* v, Controller& controller)
80     {
81         model::Link* adaptee = adaptor.getAdaptee();
82
83         if (v->getType() != types::InternalType::ScilabDouble)
84         {
85             get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for field %s: Real matrix object.\n"), "xx");
86             return false;
87         }
88
89         types::Double* current = v->getAs<types::Double>();
90
91         std::vector<double> controlPoints;
92         controller.getObjectProperty(adaptee, CONTROL_POINTS, controlPoints);
93
94         int newXSize = current->getSize();
95         int oldXSize = static_cast<int>(controlPoints.size() / 2);
96         std::vector<double> newControlPoints (controlPoints);
97
98         if (newXSize == oldXSize)
99         {
100             for (int i = 0; i < newXSize; ++i)
101             {
102                 newControlPoints[2 * i] = current->getReal()[i];
103             }
104         }
105         else
106         {
107             newControlPoints.resize(2 * current->getSize(), 0);
108
109             for (int i = 0; i < newXSize; ++i)
110             {
111                 newControlPoints[2 * i] = current->getReal()[i];
112             }
113         }
114
115         controller.setObjectProperty(adaptee, CONTROL_POINTS, newControlPoints);
116         return true;
117     }
118 };
119
120 struct yy
121 {
122
123     static types::InternalType* get(const LinkAdapter& adaptor, const Controller& controller)
124     {
125         model::Link* adaptee = adaptor.getAdaptee();
126
127         std::vector<double> controlPoints;
128         controller.getObjectProperty(adaptee, CONTROL_POINTS, controlPoints);
129
130         double* data;
131         int size = (int)controlPoints.size() / 2;
132         types::Double* o = new types::Double(size, 1, &data);
133
134         for (int i = 0; i < size; ++i)
135         {
136             data[i] = controlPoints[2 * i + 1];
137         }
138         return o;
139     }
140
141     static bool set(LinkAdapter& adaptor, types::InternalType* v, Controller& controller)
142     {
143         model::Link* adaptee = adaptor.getAdaptee();
144
145         if (v->getType() != types::InternalType::ScilabDouble)
146         {
147             get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for field %s: Real matrix object.\n"), "yy");
148             return false;
149         }
150
151         types::Double* current = v->getAs<types::Double>();
152
153         std::vector<double> controlPoints;
154         controller.getObjectProperty(adaptee, CONTROL_POINTS, controlPoints);
155
156         int newYSize = current->getSize();
157         int oldYSize = static_cast<int>(controlPoints.size() / 2);
158         std::vector<double> newControlPoints (controlPoints);
159
160         if (newYSize == oldYSize)
161         {
162             for (int i = 0; i < newYSize; ++i)
163             {
164                 newControlPoints[2 * i + 1] = current->getReal()[i];
165             }
166         }
167         else
168         {
169             newControlPoints.resize(2 * current->getSize());
170
171             for (int i = 0; i < newYSize; ++i)
172             {
173                 newControlPoints[2 * i + 1] = current->getReal()[i];
174             }
175         }
176
177         controller.setObjectProperty(adaptee, CONTROL_POINTS, newControlPoints);
178         return true;
179     }
180 };
181
182 struct id
183 {
184
185     static types::InternalType* get(const LinkAdapter& adaptor, const Controller& controller)
186     {
187         model::Link* adaptee = adaptor.getAdaptee();
188
189         std::string id;
190         controller.getObjectProperty(adaptee, DESCRIPTION, id);
191
192         types::String* o = new types::String(id.data());
193         return o;
194     }
195
196     static bool set(LinkAdapter& adaptor, types::InternalType* v, Controller& controller)
197     {
198         if (v->getType() != types::InternalType::ScilabString)
199         {
200             get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for field %s: String matrix expected.\n"), "id");
201             return false;
202         }
203
204         types::String* current = v->getAs<types::String>();
205         if (current->getSize() != 1)
206         {
207             get_or_allocate_logger()->log(LOG_ERROR, _("Wrong dimension for field %s: %d-by-%d expected.\n"), "id", 1, 1);
208             return false;
209         }
210
211         model::Link* adaptee = adaptor.getAdaptee();
212
213         char* c_str = wide_string_to_UTF8(current->get(0));
214         std::string description(c_str);
215         FREE(c_str);
216
217         controller.setObjectProperty(adaptee, DESCRIPTION, description);
218         return true;
219     }
220 };
221
222 struct thick
223 {
224
225     static types::InternalType* get(const LinkAdapter& adaptor, const Controller& controller)
226     {
227         model::Link* adaptee = adaptor.getAdaptee();
228
229         std::vector<double> thick;
230         controller.getObjectProperty(adaptee, THICK, thick);
231
232         double* data;
233         types::Double* o = new types::Double(1, 2, &data);
234
235         data[0] = thick[0];
236         data[1] = thick[1];
237         return o;
238     }
239
240     static bool set(LinkAdapter& adaptor, types::InternalType* v, Controller& controller)
241     {
242         model::Link* adaptee = adaptor.getAdaptee();
243
244         if (v->getType() != types::InternalType::ScilabDouble)
245         {
246             get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for field %s: Real matrix expected.\n"), "thick");
247             return false;
248         }
249
250         types::Double* current = v->getAs<types::Double>();
251         if (current->getSize() != 2)
252         {
253             get_or_allocate_logger()->log(LOG_ERROR, _("Wrong dimension for field %s: %d-by-%d expected.\n"), "thick", 2, 1);
254             return false;
255         }
256
257         std::vector<double> thick (2);
258         thick[0] = current->get(0);
259         thick[1] = current->get(1);
260
261         controller.setObjectProperty(adaptee, THICK, thick);
262         return true;
263     }
264 };
265
266 struct ct
267 {
268
269     static types::InternalType* get(const LinkAdapter& adaptor, const Controller& controller)
270     {
271         model::Link* adaptee = adaptor.getAdaptee();
272
273         int color;
274         int kind;
275         controller.getObjectProperty(adaptee, COLOR, color);
276         controller.getObjectProperty(adaptee, KIND, kind);
277
278         double* data;
279         types::Double* o = new types::Double(1, 2, &data);
280
281         data[0] = color;
282         data[1] = kind;
283         return o;
284     }
285
286     static bool set(LinkAdapter& adaptor, types::InternalType* v, Controller& controller)
287     {
288         model::Link* adaptee = adaptor.getAdaptee();
289
290         if (v->getType() != types::InternalType::ScilabDouble)
291         {
292             return false;
293         }
294
295         types::Double* current = v->getAs<types::Double>();
296         if (current->getSize() != 2)
297         {
298             return false;
299         }
300         if (floor(current->get(0)) != current->get(0) || floor(current->get(1)) != current->get(1))
301         {
302             return false;
303         }
304
305         int color = static_cast<int>(current->get(0));
306         int kind  = static_cast<int>(current->get(1));
307
308         controller.setObjectProperty(adaptee, COLOR, color);
309         controller.setObjectProperty(adaptee, KIND, kind);
310         return true;
311     }
312 };
313
314 link_t getLinkEnd(model::Link* adaptee, const Controller& controller, const object_properties_t end)
315 {
316     link_t ret {0, 0, Start};
317     if (end == DESTINATION_PORT)
318     {
319         ret.kind = End;
320     }
321
322     ScicosID endID;
323     controller.getObjectProperty(adaptee, end, endID);
324     if (endID != ScicosID())
325     {
326         ScicosID sourceBlock;
327         controller.getObjectProperty(endID, PORT, SOURCE_BLOCK, sourceBlock);
328         model::Block* sourceBlockObject = controller.getBaseObject<model::Block>(sourceBlock);
329
330         // Looking for the block number among the block IDs
331         ScicosID parent;
332         kind_t parentKind = BLOCK;
333         controller.getObjectProperty(adaptee, PARENT_BLOCK, parent);
334         std::vector<ScicosID> children;
335         // Added to a superblock
336         if (parent == ScicosID())
337         {
338             // Added to a diagram
339             controller.getObjectProperty(adaptee, PARENT_DIAGRAM, parent);
340             parentKind = DIAGRAM;
341             if (parent == ScicosID())
342             {
343                 return ret;
344             }
345         }
346         controller.getObjectProperty(parent, parentKind, CHILDREN, children);
347
348         ret.block = static_cast<int>(std::distance(children.begin(), std::find(children.begin(), children.end(), sourceBlock)) + 1);
349
350         // To find the port index from its 'endID' ID, search through all the block's ports lists
351         std::vector<ScicosID> sourceBlockPorts;
352         controller.getObjectProperty(sourceBlockObject, INPUTS, sourceBlockPorts);
353
354         std::vector<ScicosID>::iterator found = std::find(sourceBlockPorts.begin(), sourceBlockPorts.end(), endID);
355         if (found == sourceBlockPorts.end()) // Not found in the data inputs
356         {
357             sourceBlockPorts.clear();
358             controller.getObjectProperty(sourceBlockObject, OUTPUTS, sourceBlockPorts);
359             found = std::find(sourceBlockPorts.begin(), sourceBlockPorts.end(), endID);
360             if (found == sourceBlockPorts.end()) // Not found in the data outputs
361             {
362                 sourceBlockPorts.clear();
363                 controller.getObjectProperty(sourceBlockObject, EVENT_INPUTS, sourceBlockPorts);
364                 found = std::find(sourceBlockPorts.begin(), sourceBlockPorts.end(), endID);
365                 if (found == sourceBlockPorts.end()) // Not found in the event inputs
366                 {
367                     sourceBlockPorts.clear();
368                     controller.getObjectProperty(sourceBlockObject, EVENT_OUTPUTS, sourceBlockPorts);
369                     found = std::find(sourceBlockPorts.begin(), sourceBlockPorts.end(), endID);
370                     if (found == sourceBlockPorts.end()) // Not found in the event outputs
371                     {
372                         return ret;
373                     }
374                 }
375             }
376         }
377         ret.port = static_cast<int>(std::distance(sourceBlockPorts.begin(), found) + 1);
378
379         int kind;
380         controller.getObjectProperty(endID, PORT, PORT_KIND, kind);
381         if (kind == PORT_IN || kind == PORT_EIN)
382         {
383             ret.kind = End;
384         }
385         else
386         {
387             ret.kind = Start;
388         }
389     }
390     // Default case, the property was initialized at [].
391     return ret;
392 }
393
394 /*
395  * Connectivity is ensured if 'port' is of the desired type or if either of the concerned blocks is a split block,
396  * because they are connectable to anything
397  */
398 bool checkConnectivity(const int neededType, const ScicosID port, model::Block* blk1, Controller& controller)
399 {
400     int portKind;
401     controller.getObjectProperty(port, PORT, PORT_KIND, portKind);
402
403     if (portKind != neededType)
404     {
405         // Last chance if one of the connecting blocks is just a split block
406         std::string name1;
407         controller.getObjectProperty(blk1, SIM_FUNCTION_NAME, name1);
408         if (name1 != split && name1 != lsplit && name1 != limpsplit)
409         {
410             ScicosID blk2;
411             controller.getObjectProperty(port, PORT, SOURCE_BLOCK, blk2);
412             std::string name2;
413             controller.getObjectProperty(blk2, BLOCK, SIM_FUNCTION_NAME, name2);
414             if (name2 != split && name2 != lsplit && name2 != limpsplit)
415             {
416                 return false;
417             }
418         }
419     }
420     return true;
421 }
422
423 void setLinkEnd(model::Link* linkObject, Controller& controller, const object_properties_t end, const link_t& v, const std::vector<ScicosID>& children)
424 {
425     ScicosID from;
426     controller.getObjectProperty(linkObject, SOURCE_PORT, from);
427     ScicosID to;
428     controller.getObjectProperty(linkObject, DESTINATION_PORT, to);
429     ScicosID concernedPort;
430     ScicosID otherPort;
431     switch (end)
432     {
433         case SOURCE_PORT:
434             concernedPort = from;
435             otherPort = to;
436             break;
437         case DESTINATION_PORT:
438             concernedPort = to;
439             otherPort = from;
440             break;
441         default:
442             return;
443     }
444
445     if (v.block == 0 || v.port == 0)
446     {
447         // We want to set an empty link
448         if (concernedPort == ScicosID())
449         {
450             // In this case, the link was already empty, do a dummy call to display the console status.
451             controller.setObjectProperty(linkObject, end, concernedPort);
452         }
453         else
454         {
455             // Untie the old link on the concerned end and set its port as unconnected
456             controller.setObjectProperty(concernedPort, PORT, CONNECTED_SIGNALS, ScicosID());
457             controller.setObjectProperty(linkObject, end, ScicosID());
458         }
459         return;
460     }
461
462     // Connect the new one
463
464     if (v.kind != Start && v.kind != End)
465     {
466         return;
467     }
468     // kind == 0: trying to set the start of the link (output port)
469     // kind == 1: trying to set the end of the link (input port)
470
471     if (v.block < 0 || v.block > static_cast<int>(children.size()))
472     {
473         return; // Trying to link to a non-existing block
474     }
475     ScicosID blkID = children[v.block - 1];
476
477     if (blkID == ScicosID())
478     {
479         // Deleted Block
480         return;
481     }
482
483     // Check that the ID designates a BLOCK (and not an ANNOTATION)
484     model::Block* blkObject = controller.getBaseObject<model::Block>(blkID);
485     if (blkObject->kind() != BLOCK)
486     {
487         return;
488     }
489
490     // v.port may be decremented locally to square with the port indexes
491     int portIndex = v.port;
492
493     std::vector<ScicosID> sourceBlockPorts;
494     bool newPortIsImplicit = false;
495     enum portKind newPortKind = PORT_UNDEF;
496     int linkType;
497     controller.getObjectProperty(linkObject, KIND, linkType);
498     if (linkType == model::activation)
499     {
500         std::vector<ScicosID> evtin;
501         std::vector<ScicosID> evtout;
502         controller.getObjectProperty(blkObject, EVENT_INPUTS, evtin);
503         controller.getObjectProperty(blkObject, EVENT_OUTPUTS, evtout);
504
505         if (v.kind == Start)
506         {
507             if (otherPort != ScicosID())
508             {
509                 if (!checkConnectivity(PORT_EIN, otherPort, blkObject, controller))
510                 {
511                     return;
512                 }
513             }
514             newPortKind = PORT_EOUT;
515             sourceBlockPorts = evtout;
516         }
517         else
518         {
519             if (otherPort != ScicosID())
520             {
521                 if (!checkConnectivity(PORT_EOUT, otherPort, blkObject, controller))
522                 {
523                     return;
524                 }
525             }
526             newPortKind = PORT_EIN;
527             sourceBlockPorts = evtin;
528         }
529
530     }
531     else if (linkType == model::regular || linkType == model::implicit)
532     {
533         std::vector<ScicosID> in;
534         std::vector<ScicosID> out;
535         controller.getObjectProperty(blkObject, INPUTS, in);
536         controller.getObjectProperty(blkObject, OUTPUTS, out);
537
538         if (linkType == model::regular)
539         {
540             if (v.kind == Start)
541             {
542                 if (otherPort != ScicosID())
543                 {
544                     if (!checkConnectivity(PORT_IN, otherPort, blkObject, controller))
545                     {
546                         return;
547                     }
548                 }
549                 newPortKind = PORT_OUT;
550                 sourceBlockPorts = out;
551             }
552             else
553             {
554                 if (otherPort != ScicosID())
555                 {
556                     if (!checkConnectivity(PORT_OUT, otherPort, blkObject, controller))
557                     {
558                         return;
559                     }
560                 }
561                 newPortKind = PORT_IN;
562                 sourceBlockPorts = in;
563             }
564
565             // Rule out the implicit ports
566             for (int i = 0; i < static_cast<int>(sourceBlockPorts.size()); ++i)
567             {
568                 bool isImplicit;
569                 controller.getObjectProperty(sourceBlockPorts[i], PORT, IMPLICIT, isImplicit);
570                 if (isImplicit == true)
571                 {
572                     sourceBlockPorts.erase(sourceBlockPorts.begin() + i);
573                     if (portIndex > i + 1)
574                     {
575                         portIndex--; // Keep portIndex consistent with the port indexes
576                     }
577                 }
578             }
579         }
580         else // model::implicit
581         {
582             newPortIsImplicit = true;
583             if (v.kind == Start)
584             {
585                 sourceBlockPorts = out;
586             }
587             else // End
588             {
589                 sourceBlockPorts = in;
590             }
591
592             // Rule out the explicit ports
593             for (size_t i = 0; i < sourceBlockPorts.size(); ++i)
594             {
595                 bool isImplicit;
596                 controller.getObjectProperty(sourceBlockPorts[i], PORT, IMPLICIT, isImplicit);
597                 if (isImplicit == false)
598                 {
599                     sourceBlockPorts.erase(sourceBlockPorts.begin() + i);
600                     if (portIndex > static_cast<int>(i + 1))
601                     {
602                         portIndex--; // Keep portIndex consistent with the port indexes
603                     }
604                 }
605             }
606         }
607     }
608
609     // Disconnect the old port if it was connected. After that, concernedPort will be reused to designate the new port
610     if (concernedPort != ScicosID())
611     {
612         controller.setObjectProperty(concernedPort, PORT, CONNECTED_SIGNALS, ScicosID());
613     }
614
615     model::Port* concernedPortObject;
616     int nBlockPorts = static_cast<int>(sourceBlockPorts.size());
617     if (nBlockPorts >= portIndex)
618     {
619         concernedPort = sourceBlockPorts[portIndex - 1];
620         concernedPortObject = controller.getBaseObject<model::Port>(concernedPort);
621     }
622     else
623     {
624         while (nBlockPorts < portIndex) // Create as many ports as necessary
625         {
626             concernedPortObject = controller.createBaseObject<model::Port>(PORT);
627             concernedPort = concernedPortObject->id();
628             controller.setObjectProperty(concernedPortObject, IMPLICIT, newPortIsImplicit);
629             controller.setObjectProperty(concernedPortObject, PORT_KIND, static_cast<int>(newPortKind));
630             controller.setObjectProperty(concernedPortObject, SOURCE_BLOCK, blkID);
631             controller.setObjectProperty(concernedPortObject, CONNECTED_SIGNALS, ScicosID());
632             // Set the default dataType so it is saved in the model
633             std::vector<int> dataType;
634             controller.getObjectProperty(concernedPortObject, DATATYPE, dataType);
635             controller.setObjectProperty(concernedPortObject, DATATYPE, dataType);
636
637             std::vector<ScicosID> concernedPorts;
638             if (linkType == model::activation)
639             {
640                 if (v.kind == Start)
641                 {
642                     controller.getObjectProperty(blkObject, EVENT_OUTPUTS, concernedPorts);
643                     concernedPorts.push_back(concernedPort);
644                     controller.setObjectProperty(blkObject, EVENT_OUTPUTS, concernedPorts);
645                 }
646                 else
647                 {
648                     controller.getObjectProperty(blkObject, EVENT_INPUTS, concernedPorts);
649                     concernedPorts.push_back(concernedPort);
650                     controller.setObjectProperty(blkObject, EVENT_INPUTS, concernedPorts);
651                 }
652             }
653             else // model::regular || model::implicit
654             {
655                 if (v.kind == Start)
656                 {
657                     controller.getObjectProperty(blkObject, OUTPUTS, concernedPorts);
658                     concernedPorts.push_back(concernedPort);
659                     controller.setObjectProperty(blkObject, OUTPUTS, concernedPorts);
660                 }
661                 else
662                 {
663                     controller.getObjectProperty(blkObject, INPUTS, concernedPorts);
664                     concernedPorts.push_back(concernedPort);
665                     controller.setObjectProperty(blkObject, INPUTS, concernedPorts);
666                 }
667             }
668
669             nBlockPorts++;
670         }
671     }
672     ScicosID oldLink;
673     controller.getObjectProperty(concernedPortObject, CONNECTED_SIGNALS, oldLink);
674     if (oldLink != ScicosID())
675     {
676         // Disconnect the old link if it was indeed connected to the concerned port
677         ScicosID oldPort;
678         controller.getObjectProperty(oldLink, LINK, end, oldPort);
679         if (concernedPort == oldPort)
680         {
681             controller.setObjectProperty(oldLink, LINK, end, ScicosID());
682         }
683     }
684
685     // Connect the new source and destination ports together
686     controller.setObjectProperty(concernedPortObject, CONNECTED_SIGNALS, linkObject->id());
687     controller.setObjectProperty(linkObject, end, concernedPort);
688 }
689
690 // Check if the Link is valid
691 bool is_valid(types::Double* o)
692 {
693     if (o->getSize() == 0)
694     {
695         return true;
696     }
697
698     if (o->getSize() == 2 || o->getSize() == 3)
699     {
700         if (floor(o->get(0)) != o->get(0) || floor(o->get(1)) != o->get(1))
701         {
702             return false; // Block and Port numbers must be integer values
703         }
704         if (o->get(1) < 0)
705         {
706             return false; // Port number must be positive
707         }
708
709         if (o->getSize() == 3)
710         {
711             if (floor(o->get(2)) != o->get(2))
712             {
713                 return false; // Kind must be an integer value
714             }
715             if (o->get(2) < 0)
716             {
717                 return false; // Kind must be positive
718             }
719         }
720
721         return true;
722     }
723
724     return false;
725 }
726
727
728 struct from
729 {
730
731     static types::InternalType* get(const LinkAdapter& adaptor, const Controller& controller)
732     {
733         link_t from_content;
734         auto it = partial_links.find(adaptor.getAdaptee()->id());
735         if (it == partial_links.end())
736         {
737             // if not found use the connected value
738             from_content = getLinkEnd(adaptor.getAdaptee(), controller, SOURCE_PORT);
739         }
740         else
741         {
742             // if found, use the partial value
743             from_content = it->second.from;
744         }
745
746         double* data;
747         types::Double* o = new types::Double(1, 3, &data);
748
749         data[0] = from_content.block;
750         data[1] = from_content.port;
751         data[2] = from_content.kind;
752         return o;
753     }
754
755     static bool set(LinkAdapter& adaptor, types::InternalType* v, Controller& controller)
756     {
757         if (v->getType() != types::InternalType::ScilabDouble)
758         {
759             return false;
760         }
761
762         types::Double* current = v->getAs<types::Double>();
763
764         if (!is_valid(current))
765         {
766             return false;
767         }
768
769         link_t from_content {0, 0, Start};
770         if (current->getSize() >= 2)
771         {
772             from_content.block = static_cast<int>(current->get(0));
773             from_content.port = static_cast<int>(current->get(1));
774             // By default, 'kind' designates an output (set to 0)
775
776             if (current->getSize() == 3)
777             {
778                 from_content.kind = (current->get(2) == 0.) ? Start : End;
779             }
780         }
781
782         // store the new data on the adapter, the linking will be performed later on the diagram update
783         auto it = partial_links.find(adaptor.getAdaptee()->id());
784         if (it == partial_links.end())
785         {
786             partial_link_t l;
787             l.from = from_content;
788             l.to = getLinkEnd(adaptor.getAdaptee(), controller, DESTINATION_PORT);
789             partial_links.insert({adaptor.getAdaptee()->id(), l});
790         }
791         else
792         {
793             it->second.from = from_content;
794         }
795         return true;
796     }
797 };
798
799 struct to
800 {
801
802     static types::InternalType* get(const LinkAdapter& adaptor, const Controller& controller)
803     {
804         link_t to_content;
805         auto it = partial_links.find(adaptor.getAdaptee()->id());
806
807         if (it == partial_links.end())
808         {
809             // if not found use the connected value
810             to_content = getLinkEnd(adaptor.getAdaptee(), controller, DESTINATION_PORT);
811         }
812         else
813         {
814             // if found, use the partial value
815             to_content = it->second.to;
816         }
817
818         double* data;
819         types::Double* o = new types::Double(1, 3, &data);
820
821         data[0] = to_content.block;
822         data[1] = to_content.port;
823         data[2] = to_content.kind;
824         return o;
825     }
826
827     static bool set(LinkAdapter& adaptor, types::InternalType* v, Controller& controller)
828     {
829         if (v->getType() != types::InternalType::ScilabDouble)
830         {
831             return false;
832         }
833
834         types::Double* current = v->getAs<types::Double>();
835
836         if (current->getSize() != 0 && current->getSize() != 2 && current->getSize() != 3)
837         {
838             return false;
839         }
840
841         if (!is_valid(current))
842         {
843             return false;
844         }
845
846         link_t to_content {0, 0, End};
847         if (current->getSize() >= 2)
848         {
849             to_content.block = static_cast<int>(current->get(0));
850             to_content.port = static_cast<int>(current->get(1));
851             // By default, 'kind' designates an input (set to 1)
852
853             if (current->getSize() == 3)
854             {
855                 to_content.kind = (current->get(2) == 0.) ? Start : End;
856             }
857         }
858
859         // store the new data on the adapter, the linking will be performed later on the diagram update
860         auto it = partial_links.find(adaptor.getAdaptee()->id());
861         if (it == partial_links.end())
862         {
863             partial_link_t l;
864             l.from = getLinkEnd(adaptor.getAdaptee(), controller, SOURCE_PORT);
865             l.to = to_content;
866             partial_links.insert({adaptor.getAdaptee()->id(), l});
867         }
868         else
869         {
870             it->second.to = to_content;
871         }
872         return true;
873     }
874 };
875
876 } /* namespace */
877
878 template<> property<LinkAdapter>::props_t property<LinkAdapter>::fields = property<LinkAdapter>::props_t();
879
880 LinkAdapter::LinkAdapter(const Controller& c, org_scilab_modules_scicos::model::Link* adaptee) :
881     BaseAdapter<LinkAdapter, org_scilab_modules_scicos::model::Link>(c, adaptee)
882 {
883     if (property<LinkAdapter>::properties_have_not_been_set())
884     {
885         property<LinkAdapter>::reserve_properties(7);
886         property<LinkAdapter>::add_property(L"xx", &xx::get, &xx::set);
887         property<LinkAdapter>::add_property(L"yy", &yy::get, &yy::set);
888         property<LinkAdapter>::add_property(L"id", &id::get, &id::set);
889         property<LinkAdapter>::add_property(L"thick", &thick::get, &thick::set);
890         property<LinkAdapter>::add_property(L"ct", &ct::get, &ct::set);
891         property<LinkAdapter>::add_property(L"from", &from::get, &from::set);
892         property<LinkAdapter>::add_property(L"to", &to::get, &to::set);
893         property<LinkAdapter>::shrink_to_fit();
894     }
895 }
896
897 LinkAdapter::LinkAdapter(const LinkAdapter& adapter) :
898     BaseAdapter<LinkAdapter, org_scilab_modules_scicos::model::Link>(adapter)
899 {
900     Controller controller;
901     add_partial_links_information(controller, adapter.getAdaptee(), getAdaptee());
902 }
903
904 LinkAdapter::~LinkAdapter()
905 {
906     if (getAdaptee()->refCount() == 0)
907     {
908         partial_links.erase(getAdaptee()->id());
909     }
910 }
911
912 std::wstring LinkAdapter::getTypeStr() const
913 {
914     return getSharedTypeStr();
915 }
916 std::wstring LinkAdapter::getShortTypeStr() const
917 {
918     return getSharedTypeStr();
919 }
920
921 void LinkAdapter::relink(Controller& controller, model::Link* adaptee, const std::vector<ScicosID>& children)
922 {
923     auto it = partial_links.find(adaptee->id());
924     if (it == partial_links.end())
925     {
926         // unable to relink as there is no information to do so
927         return;
928     }
929     partial_link_t l = it->second;
930
931     setLinkEnd(adaptee, controller, SOURCE_PORT, l.from, children);
932     setLinkEnd(adaptee, controller, DESTINATION_PORT, l.to, children);
933
934     // refresh the shared values
935     ScicosID from;
936     controller.getObjectProperty(adaptee, SOURCE_PORT, from);
937     ScicosID to;
938     controller.getObjectProperty(adaptee, DESTINATION_PORT, to);
939
940     bool isConnected = from != ScicosID() && to != ScicosID();
941     if (isConnected)
942     {
943         partial_links.erase(it);
944     }
945 }
946
947 void LinkAdapter::add_partial_links_information(Controller& controller, model::BaseObject* original, model::BaseObject* cloned)
948 {
949     // precondition
950     if (cloned == nullptr)
951     {
952         return;
953     }
954
955     switch (original->kind())
956     {
957         // add the from / to information if applicable
958         case LINK:
959         {
960             auto it = partial_links.find(original->id());
961             if (it != partial_links.end())
962             {
963                 partial_links.insert({cloned->id(), it->second});
964             }
965             else
966             {
967                 partial_link_t l;
968                 l.from = getLinkEnd(static_cast<model::Link*>(original), controller, SOURCE_PORT);
969                 l.to = getLinkEnd(static_cast<model::Link*>(original), controller, DESTINATION_PORT);
970                 partial_links.insert({cloned->id(), l});
971             }
972             break;
973         }
974
975         // handle recursion
976         case DIAGRAM:
977         case BLOCK:
978         {
979             std::vector<ScicosID> originalChildren;
980             controller.getObjectProperty(original->id(), original->kind(), CHILDREN, originalChildren);
981             std::vector<ScicosID> clonedChildren;
982             controller.getObjectProperty(cloned->id(), cloned->kind(), CHILDREN, clonedChildren);
983
984             for (size_t i = 0; i < originalChildren.size(); ++i)
985             {
986                 add_partial_links_information(controller, controller.getBaseObject(originalChildren[i]), controller.getBaseObject(clonedChildren[i]));
987             }
988             break;
989         }
990
991         default:
992             break;
993     }
994
995 }
996
997 } /* namespace view_scilab */
998 } /* namespace org_scilab_modules_scicos */