c33f43acc282dd94c326e853b618bce96ad69d0f
[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-2016 - Scilab Enterprises - Clement DAVID
4  *  Copyright (C) 2017-2018 - 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 <bitset>
18 #include <string>
19 #include <vector>
20 #include <sstream>
21 #include <algorithm>
22 #include <tuple>
23
24 #include "types.hxx"
25 #include "internal.hxx"
26 #include "double.hxx"
27 #include "string.hxx"
28 #include "list.hxx"
29 #include "mlist.hxx"
30 #include "tlist.hxx"
31 #include "user.hxx"
32
33 #include "utilities.hxx"
34 #include "adapters_utilities.hxx"
35 #include "Controller.hxx"
36 #include "controller_helpers.hxx"
37
38 #include "view_scilab/Adapters.hxx"
39 #include "view_scilab/AdapterView.hxx"
40 #include "DiagramAdapter.hxx"
41 #include "ParamsAdapter.hxx"
42 #include "BlockAdapter.hxx"
43 #include "GraphicsAdapter.hxx"
44 #include "LinkAdapter.hxx"
45 #include "TextAdapter.hxx"
46 #include "model/BaseObject.hxx"
47 #include "model/Diagram.hxx"
48 #include "model/Block.hxx"
49
50 extern "C" {
51 #include "sci_malloc.h"
52 #include "charEncoding.h"
53 #include "localization.h"
54 }
55
56 namespace org_scilab_modules_scicos
57 {
58 namespace view_scilab
59 {
60 namespace
61 {
62
63 const std::wstring Deleted (L"Deleted");
64 const std::wstring Text (L"Text");
65 const std::wstring Block (L"Block");
66 const std::wstring Link (L"Link");
67
68 struct props
69 {
70
71     static types::InternalType* get(const DiagramAdapter& adaptor, const Controller& controller)
72     {
73         ParamsAdapter localAdaptor(controller, controller.referenceBaseObject<model::BaseObject>(adaptor.getAdaptee()));
74         return localAdaptor.getAsTList(new types::TList(), controller);
75     }
76
77     static bool set(DiagramAdapter& adaptor, types::InternalType* v, Controller& controller)
78     {
79         ParamsAdapter localAdaptor(controller, controller.referenceBaseObject<model::BaseObject>(adaptor.getAdaptee()));
80         return localAdaptor.setAsTList(v, controller);
81     }
82 };
83
84 struct objs
85 {
86     static types::InternalType* get(const DiagramAdapter& adaptor, const Controller& controller)
87     {
88         model::BaseObject* adaptee = adaptor.getAdaptee();
89
90         std::vector<ScicosID> children;
91         controller.getObjectProperty(adaptee, CHILDREN, children);
92         types::List* ret = new types::List();
93         // TODO: ret.reserve(children.size());
94         for (int i = 0; i < static_cast<int>(children.size()); ++i)
95         {
96             if (children[i] == ScicosID())
97             {
98                 // Deleted adapter
99                 types::MList* deleted = new types::MList();
100                 deleted->set(0, new types::String(Deleted.data()));
101                 ret->append(deleted);
102                 continue;
103             }
104
105             model::BaseObject* o = controller.getBaseObject(children[i]);
106             switch (o->kind())
107             {
108                 case ANNOTATION :
109                     ret->append(new TextAdapter(controller, controller.referenceBaseObject<model::Annotation>(o)));
110                     break;
111                 case BLOCK :
112                     ret->append(new BlockAdapter(controller, controller.referenceBaseObject<model::Block>(o)));
113                     break;
114                 case LINK:
115                 {
116                     ret->append(new LinkAdapter(controller, controller.referenceBaseObject<model::Link>(o)));
117                     break;
118                 }
119                 default:
120                 {
121                     types::MList* deleted = new types::MList();
122                     deleted->set(0, new types::String(Deleted.data()));
123                     ret->append(deleted);
124                     break;
125                 }
126             }
127         }
128
129         return ret;
130     }
131
132     template <kind_t kind, typename Adapter, typename Adaptee>
133     static Adapter* allocAndSet(types::MList* modelElement, Controller& controller)
134     {
135         ScicosID id = controller.createObject(kind);
136         Adaptee* localAdaptee = controller.getBaseObject<Adaptee>(id);
137
138         Adapter* localAdaptor = new Adapter(controller, localAdaptee);
139         localAdaptor->setAsTList(modelElement, controller);
140
141         return localAdaptor;
142     };
143
144     /**
145      * Allocate a model element from its mlist() representation
146      *
147      * \param modelElement the mlist representation
148      * \param controller the shared controller
149      * \param o the model object
150      * \param a any adapter compatible with \o
151      */
152     static void allocateAsMList(types::MList* modelElement, Controller& controller, model::BaseObject*& o, types::UserType*& a)
153     {
154         types::String* header = modelElement->getFieldNames();
155
156         if (header->get(0) == Deleted)
157         {
158             a = nullptr;
159             o = nullptr;
160         }
161         else if (header->get(0) == Text)
162         {
163             TextAdapter* adapter = allocAndSet<ANNOTATION, TextAdapter, model::Annotation>(modelElement, controller);
164             a = adapter;
165             o = adapter->getAdaptee();
166         }
167         else if (header->get(0) == Block)
168         {
169             BlockAdapter* adapter = allocAndSet<BLOCK, BlockAdapter, model::Block>(modelElement, controller);
170             a = adapter;
171             o = adapter->getAdaptee();
172         }
173         else if (header->get(0) == Link)
174         {
175             LinkAdapter* adapter = allocAndSet<LINK, LinkAdapter, model::Link>(modelElement, controller);
176             a = adapter;
177             o = adapter->getAdaptee();
178         }
179         else
180         {
181             a = nullptr;
182             o = nullptr;
183         }
184     }
185
186     // Helper to manage updates
187     //  * set an adapter at a position
188     //  * clear an adapter (eg. remove object) if the value is nullptr
189     struct update_t
190     {
191         update_t(int index, model::BaseObject* adaptee, types::InternalType* adapter) :
192             index(index), adaptee(adaptee), adapter(adapter) {}
193
194         int index;
195         model::BaseObject* adaptee;
196         types::InternalType* adapter;
197     };
198
199     // Reference all the inserted children before processing them.
200     // This struct (cstr and dstr) will avoid early deletion
201     struct ChildrenToUpdateOwner
202     {
203         ChildrenToUpdateOwner(Controller& controller, const std::vector<update_t>& childrenToUpdate) :
204             controller(controller),
205             childrenToUpdate(childrenToUpdate)
206         {
207             std::for_each(childrenToUpdate.begin(), childrenToUpdate.end(), [&controller] (const update_t & u)
208             {
209                 if (u.adapter != nullptr)
210                 {
211                     u.adapter->IncreaseRef();
212                 }
213                 else if (u.adaptee != nullptr)
214                 {
215                     controller.referenceBaseObject(u.adaptee);
216                 }
217             });
218         }
219
220         ~ChildrenToUpdateOwner()
221         {
222             std::for_each(childrenToUpdate.begin(), childrenToUpdate.end(), [this] (const update_t & u)
223             {
224                 if (u.adapter != nullptr)
225                 {
226                     u.adapter->DecreaseRef();
227                     u.adapter->killMe();
228                 }
229                 else if (u.adaptee != nullptr)
230                 {
231                     controller.deleteBaseObject(u.adaptee);
232                 }
233             });
234         }
235
236         Controller& controller;
237         const std::vector<update_t>& childrenToUpdate;
238     };
239
240     static bool set(DiagramAdapter& adaptor, types::InternalType* v, Controller& controller)
241     {
242         // Decode the list and set all children of the Diagram
243         if (v->getType() != types::InternalType::ScilabList)
244         {
245             get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for field %s: list expected.\n"), "objs");
246             return false;
247         }
248
249         model::BaseObject* adaptee = adaptor.getAdaptee();
250         types::List* argumentList = v->getAs<types::List>();
251         AdapterView update_partial_information;
252
253         // retrieve the current children to update
254         std::vector<ScicosID> children;
255         controller.getObjectProperty(adaptee, CHILDREN, children);
256
257         // A boolean to know if we are removing an object
258         bool deletion = children.size() > static_cast<size_t>(argumentList->getSize());
259         // There is going to be as many children as the input list suggests, but don't lose information on old children just yet
260         if (!deletion)
261         {
262             children.resize(argumentList->getSize());
263         }
264
265         /*
266          * Fill a buffer of things to update
267          */
268
269         // work buffer :
270         std::vector<update_t> childrenToUpdate;
271         Controller::cloned_t mapped;
272
273         // fill the work buffers accordingly to the arguments
274         for (int i = 0; i < argumentList->getSize(); ++i)
275         {
276             types::InternalType* pIT = argumentList->get(i);
277             switch (pIT->getType())
278             {
279                 case types::InternalType::ScilabUserType:
280                 {
281                     model::BaseObject* o = Adapters::instance().descriptor(pIT);
282                     if (o == nullptr)
283                     {
284                         get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for element %d of field %s: unknown Scicos object.\n"), i + 1, "objs");
285                         return false;
286                     }
287
288                     // filter out non modified children
289                     if (o->id() == children[i])
290                     {
291                         break;
292                     }
293
294                     // clone if the adapter is used somewhere else (eg. not owned by the list)
295                     // TODO add a insertionCall depth counter to use there, this will avoid extra copy on o.model.rpar.objs(1) modification
296                     if (!deletion && (pIT->getRef() > 1 || o->refCount() > 0))
297                     {
298                         model::BaseObject *cloned = controller.cloneBaseObject(mapped, o, true, true);
299                         types::InternalType *clonedAdapter = Adapters::instance().allocate_view(controller, cloned);
300
301                         childrenToUpdate.emplace_back(i, cloned, clonedAdapter);
302                         break;
303                     }
304
305                     // o have been edited in place, refresh partial information
306                     childrenToUpdate.emplace_back(i, o, pIT);
307                     break;
308                 }
309                 case types::InternalType::ScilabMList:
310                     model::BaseObject* o;
311                     types::UserType* a;
312                     allocateAsMList(pIT->getAs<types::MList>(), controller, o, a);
313                     childrenToUpdate.emplace_back(i, o, a);
314                     break;
315                 case types::InternalType::ScilabList:
316                     // clear on list()
317                     if (pIT->getAs<types::List>()->getSize() != 0)
318                     {
319                         get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for element %d of field %s: unknown Scicos object.\n"), i + 1, "objs");
320                         return false;
321                     }
322                     childrenToUpdate.emplace_back(i, nullptr, nullptr);
323                     break;
324                 default:
325                     get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for element %d of field %s: unknown Scicos object.\n"), i + 1, "objs");
326                     return false;
327             }
328         }
329
330         // clear trailing objects
331         for (int i = argumentList->getSize() ; i < static_cast<int>(children.size()); ++i)
332         {
333             childrenToUpdate.emplace_back(i, nullptr, nullptr);
334         }
335
336         /*
337          * Update parent / children
338          */
339         ChildrenToUpdateOwner tempOwning(controller, childrenToUpdate);
340
341         // Process the children / parent relationship
342         ScicosID parentDiagram;
343         controller.getObjectProperty(adaptee, PARENT_DIAGRAM, parentDiagram);
344         int offset = 0;
345         for (const auto & update : childrenToUpdate)
346         {
347             // reference / derefence the content
348             if (update.adapter == nullptr)
349             {
350                 ScicosID c = children[update.index];
351                 if (c == ScicosID())
352                 {
353                     continue;
354                 }
355
356                 auto o = controller.getBaseObject(c);
357                 controller.deleteBaseObject(o);
358             }
359             else
360             {
361                 if (deletion && children[update.index] == ScicosID())
362                 {
363                     // This object is the one being deleted in the diagram:
364                     //  - we are in effective delete mode
365                     //  - the old object is a "Deleted" mlist (deletion in two  steps)
366                     //  - the new object is not a "Deleted" mlist (just replacing  the old one)
367                     // Then 'offset' will skip the mlist so all the old children are deleted
368                     ++offset;
369                 }
370
371                 ScicosID deleted = children[update.index + offset];
372                 if (deleted)
373                 {
374                     LinkAdapter::store_partial_links_information(controller, update.adaptee, update.index + offset, children);
375                     GraphicsAdapter::store_partial_links_information(controller, update.adaptee, update.index + offset, children);
376                 }
377             }
378
379             // manage insertion and field update
380             if (update.adapter == nullptr)
381             {
382                 children[update.index] = ScicosID();
383             }
384             else
385             {
386                 controller.referenceBaseObject(update.adaptee);
387                 if (children[update.index] != ScicosID())
388                 {
389                     controller.deleteObject(children[update.index]);
390                 }
391                 children[update.index] = update.adaptee->id();
392
393                 if (adaptee->kind() == BLOCK)
394                 {
395                     controller.setObjectProperty(update.adaptee, PARENT_DIAGRAM, parentDiagram);
396                     controller.setObjectProperty(update.adaptee, PARENT_BLOCK, adaptee->id());
397                 }
398                 else
399                 {
400                     controller.setObjectProperty(update.adaptee, PARENT_DIAGRAM, adaptee->id());
401                     controller.setObjectProperty(update.adaptee, PARENT_BLOCK, ScicosID());
402                 }
403             }
404         }
405
406         // We don't need old children information anymore: reduce children size
407         if (deletion)
408         {
409             children.resize(argumentList->getSize());
410         }
411
412         /*
413          * Update partial linking information
414          */
415         for (auto it = childrenToUpdate.begin(); it != childrenToUpdate.end(); ++it)
416         {
417             if (it->adaptee != nullptr && it->adaptee->kind() == BLOCK)
418             {
419                 model::Block* adaptee = static_cast<model::Block*>(it->adaptee);
420                 GraphicsAdapter::relink(controller, adaptee, children);
421                 LinkAdapter::reverse_relink(controller, adaptee, it->index, children);
422             }
423         }
424         for (auto it = childrenToUpdate.begin(); it != childrenToUpdate.end(); ++it)
425         {
426             if (it->adaptee != nullptr && it->adaptee->kind() == LINK)
427             {
428                 model::Link* adaptee = static_cast<model::Link*>(it->adaptee);
429                 LinkAdapter::relink(controller, adaptee, children);
430                 GraphicsAdapter::reverse_relink(controller, adaptee, it->index, children);
431             }
432         }
433
434         // set the children after update
435         controller.setObjectProperty(adaptee, CHILDREN, children);
436
437         return true;
438     }
439 };
440
441 struct version
442 {
443
444     static types::InternalType* get(const DiagramAdapter& adaptor, const Controller& controller)
445     {
446         std::string version;
447         if (adaptor.getAdaptee()->kind() == BLOCK)
448         {
449             model::Block* adaptee = static_cast<model::Block*>(adaptor.getAdaptee());
450
451             ScicosID parentDiagram;
452             controller.getObjectProperty(adaptee, PARENT_DIAGRAM, parentDiagram);
453             controller.getObjectProperty(parentDiagram, DIAGRAM, VERSION_NUMBER, version);
454         }
455         else
456         {
457             model::Diagram *adaptee = static_cast<model::Diagram *>(adaptor.getAdaptee());
458             controller.getObjectProperty(adaptee, VERSION_NUMBER, version);
459         }
460
461         return new types::String(version.data());
462     }
463
464     static bool set(DiagramAdapter& adaptor, types::InternalType* v, Controller& controller)
465     {
466         if (v->getType() == types::InternalType::ScilabString)
467         {
468             types::String* current = v->getAs<types::String>();
469             if (current->getSize() != 1)
470             {
471                 get_or_allocate_logger()->log(LOG_ERROR, _("Wrong dimension for field %s: %d-by-%d expected.\n"), "version", 1, 1);
472                 return false;
473             }
474
475             if (adaptor.getAdaptee()->kind() != DIAGRAM)
476             {
477                 // version field is not present on the model for non-diagram ; let's pass it !
478                 return true;
479             }
480             model::Diagram* adaptee = static_cast<model::Diagram*>(adaptor.getAdaptee());
481
482             char* c_str = wide_string_to_UTF8(current->get(0));
483             std::string version (c_str);
484             FREE(c_str);
485
486             controller.setObjectProperty(adaptee, VERSION_NUMBER, version);
487             return true;
488         }
489         else if (v->getType() == types::InternalType::ScilabDouble)
490         {
491             types::Double* current = v->getAs<types::Double>();
492             if (current->getSize() != 0)
493             {
494                 get_or_allocate_logger()->log(LOG_ERROR, _("Wrong size for field %s: at least %d-by-%d expected.\n"), "version", 1, 1);
495                 return false;
496             }
497
498             if (adaptor.getAdaptee()->kind() != DIAGRAM)
499             {
500                 get_or_allocate_logger()->log(LOG_ERROR, _("\"%s\" is a read-only field.\n"), "version");
501                 return false;
502             }
503             model::Diagram* adaptee = static_cast<model::Diagram*>(adaptor.getAdaptee());
504
505             std::string version;
506             controller.setObjectProperty(adaptee, VERSION_NUMBER, version);
507             return true;
508         }
509
510         get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for field %s.%s: Real matrix expected.\n"), "graphics", "orig");
511         return false;
512     }
513 };
514
515 struct contrib
516 {
517
518     static types::InternalType* get(const DiagramAdapter& adaptor, const Controller& /*controller*/)
519     {
520         return adaptor.getContribContent();
521     }
522
523     static bool set(DiagramAdapter& adaptor, types::InternalType* v, Controller& /*controller*/)
524     {
525         adaptor.setContribContent(v);
526         return true;
527     }
528 };
529
530 } /* namespace */
531
532 template<> property<DiagramAdapter>::props_t property<DiagramAdapter>::fields = property<DiagramAdapter>::props_t();
533
534 DiagramAdapter::DiagramAdapter(const Controller& c, org_scilab_modules_scicos::model::BaseObject* adaptee) :
535     BaseAdapter<DiagramAdapter, org_scilab_modules_scicos::model::BaseObject>(c, adaptee),
536     contrib_content(default_value<types::List>())
537 {
538     if (property<DiagramAdapter>::properties_have_not_been_set())
539     {
540         property<DiagramAdapter>::reserve_properties(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         property<DiagramAdapter>::shrink_to_fit();
546     }
547 }
548
549 DiagramAdapter::DiagramAdapter(const DiagramAdapter& adapter) :
550     BaseAdapter<DiagramAdapter, org_scilab_modules_scicos::model::BaseObject>(adapter),
551     contrib_content(reference_value(adapter.contrib_content))
552 {
553 }
554
555 DiagramAdapter::~DiagramAdapter()
556 {
557     contrib_content->DecreaseRef();
558     contrib_content->killMe();
559 }
560
561 std::wstring DiagramAdapter::getTypeStr() const
562 {
563     return getSharedTypeStr();
564 }
565 std::wstring DiagramAdapter::getShortTypeStr() const
566 {
567     return getSharedTypeStr();
568 }
569
570 types::InternalType* DiagramAdapter::getContribContent() const
571 {
572     return contrib_content;
573 }
574
575 void DiagramAdapter::setContribContent(types::InternalType* v)
576 {
577     // v and contrib_content should not be nullptr
578
579     types::InternalType* temp = contrib_content;
580
581     v->IncreaseRef();
582     contrib_content = v;
583
584     temp->DecreaseRef();
585     temp->killMe();
586 }
587
588 } /* namespace view_scilab */
589 } /* namespace org_scilab_modules_scicos */