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