Xcos MVC: use ScicosID() instead of 0
[scilab.git] / scilab / modules / scicos / src / cpp / view_scilab / DiagramAdapter.cpp
1 /*
2  *  Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  *  Copyright (C) 2014-2014 - Scilab Enterprises - Clement DAVID
4  *
5  * Copyright (C) 2012 - 2016 - Scilab Enterprises
6  *
7  * This file is hereby licensed under the terms of the GNU GPL v2.0,
8  * pursuant to article 5.3.4 of the CeCILL v.2.1.
9  * This file was originally licensed under the terms of the CeCILL v2.1,
10  * and continues to be available under such terms.
11  * For more information, see the COPYING file which you should have received
12  * along with this program.
13  *
14  */
15
16 #include <string>
17 #include <vector>
18 #include <sstream>
19 #include <algorithm>
20
21 #include "types.hxx"
22 #include "internal.hxx"
23 #include "double.hxx"
24 #include "string.hxx"
25 #include "list.hxx"
26 #include "mlist.hxx"
27 #include "tlist.hxx"
28 #include "user.hxx"
29
30 #include "utilities.hxx"
31 #include "adapters_utilities.hxx"
32 #include "Controller.hxx"
33 #include "DiagramAdapter.hxx"
34 #include "controller_helpers.hxx"
35
36 #include "view_scilab/Adapters.hxx"
37 #include "ParamsAdapter.hxx"
38 #include "BlockAdapter.hxx"
39 #include "LinkAdapter.hxx"
40 #include "TextAdapter.hxx"
41 #include "model/BaseObject.hxx"
42
43 extern "C" {
44 #include "sci_malloc.h"
45 #include "charEncoding.h"
46 #include "localization.h"
47 }
48
49 namespace org_scilab_modules_scicos
50 {
51 namespace view_scilab
52 {
53 namespace
54 {
55
56 const std::wstring Deleted (L"Deleted");
57 const std::wstring TextSharedTypeStr (L"Text");
58 const std::wstring BlockSharedTypeStr (L"Block");
59 const std::wstring LinkSharedTypeStr (L"Link");
60
61 struct props
62 {
63
64     static types::InternalType* get(const DiagramAdapter& adaptor, const Controller& controller)
65     {
66         ParamsAdapter localAdaptor(controller, controller.referenceObject(adaptor.getAdaptee()));
67         return localAdaptor.getAsTList(new types::TList(), controller);
68     }
69
70     static bool set(DiagramAdapter& adaptor, types::InternalType* v, Controller& controller)
71     {
72         ParamsAdapter localAdaptor(controller, controller.referenceObject(adaptor.getAdaptee()));
73         return localAdaptor.setAsTList(v, controller);
74     }
75 };
76
77 struct objs
78 {
79
80     static types::InternalType* get(const DiagramAdapter& adaptor, const Controller& controller)
81     {
82         model::Diagram* adaptee = adaptor.getAdaptee();
83
84         std::vector<ScicosID> children;
85         controller.getObjectProperty(adaptee->id(), DIAGRAM, CHILDREN, children);
86         types::List* ret = new types::List();
87         int link_i = 0;
88         for (int i = 0; i < static_cast<int>(children.size()); ++i)
89         {
90             if (children[i] == ScicosID())
91             {
92                 // Deleted adapter
93                 types::MList* deleted = new types::MList();
94                 deleted->set(0, new types::String(Deleted.data()));
95                 ret->append(deleted);
96                 continue;
97             }
98
99             auto o = controller.getObject(children[i]);
100             // Reference objects only if don't come from the copy constructor
101             if (adaptor.getListObjects() != nullptr)
102             {
103                 controller.referenceObject(o);
104             }
105
106             switch (o->kind())
107             {
108                 case ANNOTATION :
109                     ret->append(new TextAdapter(controller, static_cast<model::Annotation*>(o)));
110                     break;
111                 case BLOCK :
112                 {
113                     BlockAdapter* block = new BlockAdapter(controller, static_cast<model::Block*>(o));
114                     if (adaptor.getListObjects() != nullptr)
115                     {
116                         BlockAdapter* oldBlock = adaptor.getListObjects()->get(i)->getAs<BlockAdapter>();
117                         block->setFrom(oldBlock->getFrom());
118                         block->setTo(oldBlock->getTo());
119                         block->setListObjects(oldBlock->getListObjects());
120                         block->setContribContent(oldBlock->getContribContent());
121                     }
122                     else
123                     {
124                         // The diagram doesn't have the list of his children adapters. Create it through BlockAdapter::model::get()
125                         std::wstring Model = L"model";
126                         property<BlockAdapter>::props_t_it found = std::lower_bound(property<BlockAdapter>::fields.begin(), property<BlockAdapter>::fields.end(), Model);
127                         if (found != property<BlockAdapter>::fields.end())
128                         {
129                             types::InternalType* subDiagram = found->get(*block, controller);
130
131                             // Now remove the references that this getter provoked, on the sub-diagram as well as in its sub-objects
132                             types::List* subList = block->getListObjects();
133                             for (int i = 0; i < subList->getSize(); ++i)
134                             {
135                                 const Adapters::adapters_index_t adapter_index = Adapters::instance().lookup_by_typename(subList->get(i)->getShortTypeStr());
136                                 switch (adapter_index)
137                                 {
138                                     case Adapters::BLOCK_ADAPTER :
139                                     {
140                                         BlockAdapter* subBlock = subList->get(i)->getAs<BlockAdapter>();
141                                         const_cast<Controller&>(controller).deleteObject(subBlock->getAdaptee()->id());
142                                         break;
143                                     }
144                                     case Adapters::LINK_ADAPTER :
145                                     {
146                                         LinkAdapter* subLink = subList->get(i)->getAs<LinkAdapter>();
147                                         const_cast<Controller&>(controller).deleteObject(subLink->getAdaptee()->id());
148                                         break;
149                                     }
150                                     default : // TEXT_ADAPTER
151                                     {
152                                         TextAdapter* subText = subList->get(i)->getAs<TextAdapter>();
153                                         const_cast<Controller&>(controller).deleteObject(subText->getAdaptee()->id());
154                                     }
155                                 }
156                             }
157                             subDiagram->killMe();
158                         }
159                     }
160                     ret->append(block);
161                     break;
162                 }
163                 default : // LINK
164                     LinkAdapter* link = new LinkAdapter(controller, static_cast<model::Link*>(o));
165                     if (adaptor.getListObjects() != nullptr)
166                     {
167                         link->setFrom(adaptor.getFrom()[link_i]);
168                         link->setTo(adaptor.getTo()[link_i]);
169                         link_i++;
170                     }
171                     ret->append(link);
172                     break;
173             }
174         }
175
176         return ret;
177     }
178
179     /**
180      * Clone the object if it is owned by something else
181      */
182     static types::InternalType* cloneIfNeeded(types::InternalType* v)
183     {
184         if (v->getRef() == 0 || v->getRef() == 1)
185         {
186             return v;
187         }
188
189         return v->clone();
190     }
191
192     static std::vector<types::InternalType*> extractAndSort(types::List* v)
193     {
194         std::vector<types::InternalType*> ret;
195         if (v == nullptr)
196         {
197             return ret;
198         }
199
200         ret.reserve(v->getSize());
201         for (int i = 0; i < v->getSize(); ++i)
202         {
203             ret.push_back(v->get(i));
204         }
205
206         std::sort(ret.begin(), ret.end());
207         return ret;
208     }
209
210
211     static bool set(DiagramAdapter& adaptor, types::InternalType* v, Controller& controller)
212     {
213         // Decode the list and set all children of the Diagram
214         if (v->getType() != types::InternalType::ScilabList)
215         {
216             get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for field %s: list expected.\n"), "objs");
217             return false;
218         }
219
220         /*
221          * Always deep clone both the list and children as the PARENT_DIAGRAM property will be updated
222          */
223         model::Diagram* adaptee = adaptor.getAdaptee();
224
225         types::List* argumentList = v->getAs<types::List>();
226         types::List* list = new types::List();
227
228         std::vector<ScicosID> oldDiagramChildren;
229         controller.getObjectProperty(adaptee->id(), DIAGRAM, CHILDREN, oldDiagramChildren);
230
231         /*
232          * First pass on objects:
233          *  - store IDs if they exists and are valid ; ScicosID() otherwise
234          *  - store all the links to update link connections later
235          *  - store all the valid mlist content ('Text' content)
236          *  - store all the deleted mlist content ('Deleted' content)
237          */
238
239         std::vector<ScicosID> diagramChildren;
240         diagramChildren.reserve(argumentList->getSize());
241         std::vector<kind_t> diagramChildrenKind;
242         diagramChildrenKind.reserve(argumentList->getSize());
243
244         std::vector<LinkAdapter*> links;
245         std::vector<int> textAsMListIndices;
246         std::vector<int> deletedAsMListIndices;
247
248         for (int i = 0; i < argumentList->getSize(); ++i)
249         {
250             if (argumentList->get(i)->getType() == types::InternalType::ScilabUserType)
251             {
252                 const Adapters::adapters_index_t adapter_index = Adapters::instance().lookup_by_typename(argumentList->get(i)->getShortTypeStr());
253
254                 ScicosID id;
255                 kind_t kind;
256                 types::InternalType* adapter;
257                 switch (adapter_index)
258                 {
259                     case Adapters::BLOCK_ADAPTER:
260                     {
261                         BlockAdapter* modelElement = cloneIfNeeded(argumentList->get(i))->getAs<BlockAdapter>();
262                         id = modelElement->getAdaptee()->id();
263                         kind = modelElement->getAdaptee()->kind();
264                         adapter = modelElement;
265                         break;
266                     }
267                     case Adapters::LINK_ADAPTER:
268                     {
269                         LinkAdapter* modelElement = cloneIfNeeded(argumentList->get(i))->getAs<LinkAdapter>();
270                         id = modelElement->getAdaptee()->id();
271                         kind = modelElement->getAdaptee()->kind();
272                         adapter = modelElement;
273
274                         // Do the linking in the next loop, in case the Link points to a Block that has not been added yet
275                         links.push_back(modelElement);
276                         break;
277                     }
278                     case Adapters::TEXT_ADAPTER:
279                     {
280                         TextAdapter* modelElement = cloneIfNeeded(argumentList->get(i))->getAs<TextAdapter>();
281                         id = modelElement->getAdaptee()->id();
282                         kind = modelElement->getAdaptee()->kind();
283                         adapter = modelElement;
284                         break;
285                     }
286                     default:
287                         get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for element %d of field %s: unknown Scicos object.\n"), i + 1, "objs");
288                         list->killMe();
289                         return false;
290                 }
291
292                 diagramChildren.push_back(id);
293                 diagramChildrenKind.push_back(kind);
294                 list->set(i, adapter);
295             }
296             else if (argumentList->get(i)->getType() == types::InternalType::ScilabMList)
297             {
298                 // Allow to pass mlists to 'objs', representing 'Deleted' or 'Text' objects
299                 types::MList* modelElement = argumentList->get(i)->getAs<types::MList>();
300                 types::String* header = modelElement->getFieldNames();
301
302                 if (header->get(0) == Deleted)
303                 {
304                     deletedAsMListIndices.push_back(i);
305                     // will be filled later
306                     diagramChildren.push_back(ScicosID());
307                     diagramChildrenKind.push_back(ANNOTATION);
308                 }
309                 else if (header->get(0) == TextSharedTypeStr)
310                 {
311                     textAsMListIndices.push_back(i);
312                     // will be filled later
313                     diagramChildren.push_back(ScicosID());
314                     diagramChildrenKind.push_back(ANNOTATION);
315                 }
316                 else if (header->get(0) == BlockSharedTypeStr)
317                 {
318                     ScicosID localAdaptee = controller.createObject(BLOCK);
319                     BlockAdapter* localAdaptor = new BlockAdapter(controller, controller.getObject<model::Block>(localAdaptee));
320                     if (!localAdaptor->setAsTList(modelElement, controller))
321                     {
322                         list->killMe();
323                         return false;
324                     }
325
326                     diagramChildren.push_back(localAdaptee);
327                     list->set(i, localAdaptor);
328                 }
329                 else if (header->get(0) == LinkSharedTypeStr)
330                 {
331                     ScicosID localAdaptee = controller.createObject(LINK);
332                     LinkAdapter* localAdaptor = new LinkAdapter(controller, controller.getObject<model::Link>(localAdaptee));
333                     if (!localAdaptor->setAsTList(modelElement, controller))
334                     {
335                         list->killMe();
336                         return false;
337                     }
338
339                     diagramChildren.push_back(localAdaptee);
340                     list->set(i, localAdaptor);
341                 }
342                 else
343                 {
344                     get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for element %d of field %s: unknown Scicos object.\n"), i + 1, "objs");
345                     list->killMe();
346                     return false;
347                 }
348             }
349             else if (argumentList->get(i)->getType() == types::InternalType::ScilabList)
350             {
351                 // Allow to pass empty lists to 'objs', representing deleted Blocks
352                 types::List* modelElement = argumentList->get(i)->getAs<types::List>();
353                 if (modelElement->getSize() != 0)
354                 {
355                     get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for element %d of field %s: unknown Scicos object.\n"), i + 1, "objs");
356                     list->killMe();
357                     return false;
358                 }
359
360                 deletedAsMListIndices.push_back(i);
361                 // Mark deleted objects with value '0'
362                 diagramChildren.push_back(ScicosID());
363                 diagramChildrenKind.push_back(ANNOTATION);
364             }
365             else
366             {
367                 get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for element %d of field %s: unknown Scicos object.\n"), i + 1, "objs");
368                 list->killMe();
369                 return false;
370             }
371         }
372
373         /*
374          * Create all Annotation, decoding mlist content
375          */
376         for (const auto index : textAsMListIndices)
377         {
378             ScicosID localAdaptee = controller.createObject(ANNOTATION);
379             TextAdapter* localAdaptor = new TextAdapter(controller, controller.getObject<model::Annotation>(localAdaptee));
380             if (!localAdaptor->setAsTList(argumentList->get(index), controller))
381             {
382                 // Do not return there ; the annotation will be empty
383             }
384
385             diagramChildren[index] = localAdaptee;
386         }
387
388         /*
389          * Recreate 'Deleted' mlist if needed
390          */
391         for (const auto index : deletedAsMListIndices)
392         {
393             types::MList* deleted = new types::MList();
394             deleted->set(0, new types::String(Deleted.data()));
395
396             list->set(index, deleted);
397         }
398
399         /*
400          * Set the parent diagram of all the blocks
401          */
402         controller.setObjectProperty(adaptee->id(), DIAGRAM, CHILDREN, diagramChildren);
403         {
404             std::sort(oldDiagramChildren.begin(), oldDiagramChildren.end());
405             for (const ScicosID id : diagramChildren)
406             {
407                 if (id != ScicosID() && !std::binary_search(oldDiagramChildren.begin(), oldDiagramChildren.end(), id))
408                 {
409                     auto o = controller.getObject(id);
410                     controller.setObjectProperty(o->id(), o->kind(), PARENT_DIAGRAM, adaptee->id());
411
412                     controller.referenceObject(id);
413                 }
414             }
415
416             std::sort(diagramChildren.begin(), diagramChildren.end());
417             for (const ScicosID id : oldDiagramChildren)
418             {
419                 if (id != ScicosID() && !std::binary_search(diagramChildren.begin(), diagramChildren.end(), id))
420                 {
421                     auto o = controller.getObject(id);
422                     controller.setObjectProperty(o->id(), o->kind(), PARENT_DIAGRAM, ScicosID());
423
424                     controller.deleteObject(id);
425                 }
426             }
427         }
428
429         /*
430          * Store the children in the local adaptor, avoiding the deletion of argument
431          */
432         v->IncreaseRef();
433         adaptor.setListObjects(list);
434
435         /*
436          * Re-sync the partial link information
437          */
438         std::vector<link_t> from_content (links.size());
439         std::vector<link_t> to_content (links.size());
440         // Do the linking at model-level
441         for (int i = 0; i < static_cast<int>(links.size()); ++i)
442         {
443             // Trigger 'from' and 'to' properties
444             from_content[i] = links[i]->getFrom();
445             links[i]->setFromInModel(from_content[i], controller);
446
447             to_content[i] = links[i]->getTo();
448             links[i]->setToInModel(to_content[i], controller);
449         }
450         adaptor.setFrom(from_content);
451         adaptor.setTo(to_content);
452
453         v->DecreaseRef();
454         return true;
455     }
456 };
457
458 struct version
459 {
460
461     static types::InternalType* get(const DiagramAdapter& adaptor, const Controller& controller)
462     {
463         model::Diagram* adaptee = adaptor.getAdaptee();
464
465         std::string version;
466         controller.getObjectProperty(adaptee->id(), DIAGRAM, VERSION_NUMBER, version);
467
468         return new types::String(version.data());
469     }
470
471     static bool set(DiagramAdapter& adaptor, types::InternalType* v, Controller& controller)
472     {
473         if (v->getType() == types::InternalType::ScilabString)
474         {
475             types::String* current = v->getAs<types::String>();
476             if (current->getSize() != 1)
477             {
478                 get_or_allocate_logger()->log(LOG_ERROR, _("Wrong dimension for field %s: %d-by-%d expected.\n"), "version", 1, 1);
479                 return false;
480             }
481
482             model::Diagram* adaptee = adaptor.getAdaptee();
483
484             char* c_str = wide_string_to_UTF8(current->get(0));
485             std::string version (c_str);
486             FREE(c_str);
487
488             controller.setObjectProperty(adaptee->id(), DIAGRAM, VERSION_NUMBER, version);
489             return true;
490         }
491         else if (v->getType() == types::InternalType::ScilabDouble)
492         {
493             types::Double* current = v->getAs<types::Double>();
494             if (current->getSize() != 0)
495             {
496                 get_or_allocate_logger()->log(LOG_ERROR, _("Wrong size for field %s: at least %d-by-%d expected.\n"), "version", 1, 1);
497                 return false;
498             }
499
500             model::Diagram* adaptee = adaptor.getAdaptee();
501
502             std::string version;
503             controller.setObjectProperty(adaptee->id(), DIAGRAM, VERSION_NUMBER, version);
504             return true;
505         }
506
507         get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for field %s.%s: Real matrix expected.\n"), "graphics", "orig");
508         return false;
509     }
510 };
511
512 struct contrib
513 {
514
515     static types::InternalType* get(const DiagramAdapter& adaptor, const Controller& /*controller*/)
516     {
517         return adaptor.getContribContent();
518     }
519
520     static bool set(DiagramAdapter& adaptor, types::InternalType* v, Controller& /*controller*/)
521     {
522         adaptor.setContribContent(v->clone());
523         return true;
524     }
525 };
526
527 } /* namespace */
528
529 template<> property<DiagramAdapter>::props_t property<DiagramAdapter>::fields = property<DiagramAdapter>::props_t();
530
531 DiagramAdapter::DiagramAdapter(const Controller& c, org_scilab_modules_scicos::model::Diagram* adaptee) :
532     BaseAdapter<DiagramAdapter, org_scilab_modules_scicos::model::Diagram>(c, adaptee),
533     from_vec(),
534     to_vec(),
535     list_objects(nullptr),
536     contrib_content(nullptr)
537 {
538     if (property<DiagramAdapter>::properties_have_not_been_set())
539     {
540         property<DiagramAdapter>::fields.reserve(4);
541         property<DiagramAdapter>::add_property(L"props", &props::get, &props::set);
542         property<DiagramAdapter>::add_property(L"objs", &objs::get, &objs::set);
543         property<DiagramAdapter>::add_property(L"version", &version::get, &version::set);
544         property<DiagramAdapter>::add_property(L"contrib", &contrib::get, &contrib::set);
545     }
546
547     // objs::set will set the adapter's content (listObjects, from_vec & to_vec) if needed
548     Controller controller;
549     types::List* listObjects = objs::get(*this, controller)->getAs<types::List>();
550     if (listObjects->getSize() > 0)
551     {
552         objs::set(*this, listObjects, controller);
553     }
554
555     setContribContent(new types::List());
556 }
557
558 DiagramAdapter::DiagramAdapter(const DiagramAdapter& adapter) :
559     BaseAdapter<DiagramAdapter, org_scilab_modules_scicos::model::Diagram>(adapter, true),
560     from_vec(),
561     to_vec(),
562     list_objects(nullptr),
563     contrib_content(nullptr)
564 {
565     Controller controller;
566
567     // set the list and perform from / to links update
568     objs::set(*this, objs::get(*this, controller)->getAs<types::List>(), controller);
569
570     setFrom(adapter.getFrom());
571     setTo(adapter.getTo());
572     setContribContent(adapter.getContribContent());
573 }
574
575 DiagramAdapter::~DiagramAdapter()
576 {
577     // CHILDREN will be unreferenced on Controller::deleteObject
578
579     if (list_objects != nullptr)
580     {
581         list_objects->DecreaseRef();
582         list_objects->killMe();
583     }
584
585     contrib_content->DecreaseRef();
586     contrib_content->killMe();
587 }
588
589 std::wstring DiagramAdapter::getTypeStr()
590 {
591     return getSharedTypeStr();
592 }
593 std::wstring DiagramAdapter::getShortTypeStr()
594 {
595     return getSharedTypeStr();
596 }
597
598 types::List* DiagramAdapter::getListObjects() const
599 {
600     return list_objects;
601 }
602
603 void DiagramAdapter::setListObjects(types::List* v)
604 {
605     types::InternalType* temp = list_objects;
606
607     // Do not check if v is nullptr on purpose ; it *should* not
608     v->IncreaseRef();
609     list_objects = v;
610
611     if (temp != nullptr)
612     {
613         temp->DecreaseRef();
614         temp->killMe();
615     }
616 }
617
618 std::vector<link_t> DiagramAdapter::getFrom() const
619 {
620     return from_vec;
621 }
622
623 void DiagramAdapter::setFrom(const std::vector<link_t>& from)
624 {
625     from_vec = from;
626 }
627
628 std::vector<link_t> DiagramAdapter::getTo() const
629 {
630     return to_vec;
631 }
632
633 void DiagramAdapter::setTo(const std::vector<link_t>& to)
634 {
635     to_vec = to;
636 }
637
638 types::InternalType* DiagramAdapter::getContribContent() const
639 {
640     return contrib_content;
641 }
642
643 void DiagramAdapter::setContribContent(types::InternalType* v)
644 {
645     types::InternalType* temp = contrib_content;
646
647     // do not check if v is nullptr on purpose ; it *should* not
648     v->IncreaseRef();
649     contrib_content = v;
650
651     if (temp != nullptr)
652     {
653         temp->DecreaseRef();
654         temp->killMe();
655     }
656 }
657
658 } /* namespace view_scilab */
659 } /* namespace org_scilab_modules_scicos */