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