093c12c8497142b93fedbc0302b52e1447faa2f6
[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.adaptee != nullptr)
210                 {
211                     controller.referenceBaseObject(u.adaptee);
212                 }
213             });
214         }
215
216         ~ChildrenToUpdateOwner()
217         {
218             std::for_each(childrenToUpdate.begin(), childrenToUpdate.end(), [this] (const update_t & u)
219             {
220                 if (u.adaptee != nullptr)
221                 {
222                     controller.deleteBaseObject(u.adaptee);
223                 }
224             });
225         }
226
227         Controller& controller;
228         const std::vector<update_t>& childrenToUpdate;
229     };
230
231     static bool set(DiagramAdapter& adaptor, types::InternalType* v, Controller& controller)
232     {
233         // Decode the list and set all children of the Diagram
234         if (v->getType() != types::InternalType::ScilabList)
235         {
236             get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for field %s: list expected.\n"), "objs");
237             return false;
238         }
239
240         model::BaseObject* adaptee = adaptor.getAdaptee();
241         types::List* argumentList = v->getAs<types::List>();
242         AdapterView update_partial_information;
243
244         // retrieve the current children to update
245         std::vector<ScicosID> children;
246         controller.getObjectProperty(adaptee, CHILDREN, children);
247
248         // A boolean to know if we are removing an object
249         bool deletion = children.size() > static_cast<size_t>(argumentList->getSize());
250         // There is going to be as many children as the input list suggests, but don't lose information on old children just yet
251         if (!deletion)
252         {
253             children.resize(argumentList->getSize());
254         }
255
256         /*
257          * Fill a buffer of things to update
258          */
259
260         // work buffer :
261         std::vector<update_t> childrenToUpdate;
262         Controller::cloned_t mapped;
263
264         // fill the work buffers accordingly to the arguments
265         for (int i = 0; i < argumentList->getSize(); ++i)
266         {
267             types::InternalType* pIT = argumentList->get(i);
268             switch (pIT->getType())
269             {
270                 case types::InternalType::ScilabUserType:
271                 {
272                     model::BaseObject* o = Adapters::instance().descriptor(pIT);
273                     if (o == nullptr)
274                     {
275                         get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for element %d of field %s: unknown Scicos object.\n"), i + 1, "objs");
276                         return false;
277                     }
278
279                     // filter out non modified children
280                     if (o->id() == children[i])
281                     {
282                         break;
283                     }
284
285                     // clone if the adapter is used somewhere else (eg. not owned by the list)
286                     if (pIT->getRef() > 1 || o->refCount() > 0) // over-clone some elements but PASS the tests
287                         //                     if (pIT->getRef() > 1) // TODO: investigate why this expression is not enough
288                     {
289                         model::BaseObject* cloned = controller.cloneBaseObject(mapped, o, true, true);
290                         types::InternalType* clonedAdapter = Adapters::instance().allocate_view(controller, cloned);
291
292                         childrenToUpdate.emplace_back(i, cloned, clonedAdapter);
293                         break;
294                     }
295
296                     // o have been edited in place, refresh partial information
297                     pIT->IncreaseRef();
298                     childrenToUpdate.emplace_back(i, o, pIT);
299                     break;
300                 }
301                 case types::InternalType::ScilabMList:
302                     model::BaseObject* o;
303                     types::UserType* a;
304                     allocateAsMList(pIT->getAs<types::MList>(), controller, o, a);
305                     childrenToUpdate.emplace_back(i, o, a);
306                     break;
307                 case types::InternalType::ScilabList:
308                     // clear on list()
309                     if (pIT->getAs<types::List>()->getSize() != 0)
310                     {
311                         get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for element %d of field %s: unknown Scicos object.\n"), i + 1, "objs");
312                         return false;
313                     }
314                     childrenToUpdate.emplace_back(i, nullptr, nullptr);
315                     break;
316                 default:
317                     get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for element %d of field %s: unknown Scicos object.\n"), i + 1, "objs");
318                     return false;
319             }
320         }
321
322         // clear trailing objects
323         for (int i = argumentList->getSize() ; i < static_cast<int>(children.size()); ++i)
324         {
325             childrenToUpdate.emplace_back(i, nullptr, nullptr);
326         }
327
328         /*
329          * Update parent / children
330          */
331         ChildrenToUpdateOwner tempOwning(controller, childrenToUpdate);
332
333         // Process the children / parent relationship
334         ScicosID parentDiagram;
335         controller.getObjectProperty(adaptee, PARENT_DIAGRAM, parentDiagram);
336         int offset = 0;
337         for (const auto & update : childrenToUpdate)
338         {
339             // reference / derefence the content
340             if (update.adapter == nullptr)
341             {
342                 ScicosID c = children[update.index];
343                 if (c == ScicosID())
344                 {
345                     continue;
346                 }
347
348                 auto o = controller.getBaseObject(c);
349                 controller.deleteBaseObject(o);
350             }
351             else
352             {
353                 controller.referenceBaseObject(update.adaptee);
354                 if (deletion && children[update.index] == ScicosID())
355                 {
356                     // This object is the one being deleted in the diagram:
357                     //  - we are in effective delete mode
358                     //  - the old object is a "Deleted" mlist (deletion in two steps)
359                     //  - the new object is not a "Deleted" mlist (just replacing the old one)
360                     // Then 'offset' will skip the mlist so all the old children are deleted
361                     ++offset;
362                 }
363
364                 LinkAdapter::store_partial_links_information(controller, update.adaptee, update.index + offset, children);
365                 GraphicsAdapter::store_partial_links_information(controller, update.adaptee, update.index + offset, children);
366                 controller.deleteObject(children[update.index + offset]);
367             }
368
369             // manage insertion and field update
370             if (update.adapter == nullptr)
371             {
372                 children[update.index] = ScicosID();
373             }
374             else
375             {
376                 children[update.index] = update.adaptee->id();
377
378                 if (adaptee->kind() == BLOCK)
379                 {
380                     controller.setObjectProperty(update.adaptee, PARENT_DIAGRAM, parentDiagram);
381                     controller.setObjectProperty(update.adaptee, PARENT_BLOCK, adaptee->id());
382                 }
383                 else
384                 {
385                     controller.setObjectProperty(update.adaptee, PARENT_DIAGRAM, adaptee->id());
386                     controller.setObjectProperty(update.adaptee, PARENT_BLOCK, ScicosID());
387                 }
388             }
389         }
390
391         // We don't need old children information anymore: reduce children size
392         if (deletion)
393         {
394             children.resize(argumentList->getSize());
395         }
396
397         /*
398          * Update partial linking information
399          */
400         for (auto it = childrenToUpdate.begin(); it != childrenToUpdate.end(); ++it)
401         {
402             if (it->adaptee != nullptr && it->adaptee->kind() == BLOCK)
403             {
404                 model::Block* adaptee = static_cast<model::Block*>(it->adaptee);
405                 GraphicsAdapter::relink(controller, adaptee, children);
406                 LinkAdapter::reverse_relink(controller, adaptee, it->index, children);
407             }
408         }
409         for (auto it = childrenToUpdate.begin(); it != childrenToUpdate.end(); ++it)
410         {
411             if (it->adaptee != nullptr && it->adaptee->kind() == LINK)
412             {
413                 model::Link* adaptee = static_cast<model::Link*>(it->adaptee);
414                 LinkAdapter::relink(controller, adaptee, children);
415                 GraphicsAdapter::reverse_relink(controller, adaptee, it->index, children);
416             }
417         }
418
419         // set the children after update
420         controller.setObjectProperty(adaptee, CHILDREN, children);
421
422         return true;
423     }
424 };
425
426 struct version
427 {
428
429     static types::InternalType* get(const DiagramAdapter& adaptor, const Controller& controller)
430     {
431         std::string version;
432         if (adaptor.getAdaptee()->kind() == BLOCK)
433         {
434             model::Block* adaptee = static_cast<model::Block*>(adaptor.getAdaptee());
435
436             ScicosID parentDiagram;
437             controller.getObjectProperty(adaptee, PARENT_DIAGRAM, parentDiagram);
438             controller.getObjectProperty(parentDiagram, DIAGRAM, VERSION_NUMBER, version);
439         }
440         else
441         {
442             model::Diagram* adaptee = static_cast<model::Diagram*>(adaptor.getAdaptee());
443             controller.getObjectProperty(adaptee, VERSION_NUMBER, version);
444         }
445
446         return new types::String(version.data());
447     }
448
449     static bool set(DiagramAdapter& adaptor, types::InternalType* v, Controller& controller)
450     {
451         if (v->getType() == types::InternalType::ScilabString)
452         {
453             types::String* current = v->getAs<types::String>();
454             if (current->getSize() != 1)
455             {
456                 get_or_allocate_logger()->log(LOG_ERROR, _("Wrong dimension for field %s: %d-by-%d expected.\n"), "version", 1, 1);
457                 return false;
458             }
459
460             if (adaptor.getAdaptee()->kind() != DIAGRAM)
461             {
462                 // version field is not present on the model for non-diagram ; let's pass it !
463                 return true;
464             }
465             model::Diagram* adaptee = static_cast<model::Diagram*>(adaptor.getAdaptee());
466
467             char* c_str = wide_string_to_UTF8(current->get(0));
468             std::string version (c_str);
469             FREE(c_str);
470
471             controller.setObjectProperty(adaptee, VERSION_NUMBER, version);
472             return true;
473         }
474         else if (v->getType() == types::InternalType::ScilabDouble)
475         {
476             types::Double* current = v->getAs<types::Double>();
477             if (current->getSize() != 0)
478             {
479                 get_or_allocate_logger()->log(LOG_ERROR, _("Wrong size for field %s: at least %d-by-%d expected.\n"), "version", 1, 1);
480                 return false;
481             }
482
483             if (adaptor.getAdaptee()->kind() != DIAGRAM)
484             {
485                 get_or_allocate_logger()->log(LOG_ERROR, _("\"%s\" is a read-only field.\n"), "version");
486                 return false;
487             }
488             model::Diagram* adaptee = static_cast<model::Diagram*>(adaptor.getAdaptee());
489
490             std::string version;
491             controller.setObjectProperty(adaptee, VERSION_NUMBER, version);
492             return true;
493         }
494
495         get_or_allocate_logger()->log(LOG_ERROR, _("Wrong type for field %s.%s: Real matrix expected.\n"), "graphics", "orig");
496         return false;
497     }
498 };
499
500 struct contrib
501 {
502
503     static types::InternalType* get(const DiagramAdapter& adaptor, const Controller& /*controller*/)
504     {
505         return adaptor.getContribContent();
506     }
507
508     static bool set(DiagramAdapter& adaptor, types::InternalType* v, Controller& /*controller*/)
509     {
510         adaptor.setContribContent(v);
511         return true;
512     }
513 };
514
515 } /* namespace */
516
517 template<> property<DiagramAdapter>::props_t property<DiagramAdapter>::fields = property<DiagramAdapter>::props_t();
518
519 DiagramAdapter::DiagramAdapter(const Controller& c, org_scilab_modules_scicos::model::BaseObject* adaptee) :
520     BaseAdapter<DiagramAdapter, org_scilab_modules_scicos::model::BaseObject>(c, adaptee),
521     contrib_content(default_value<types::List>())
522 {
523     if (property<DiagramAdapter>::properties_have_not_been_set())
524     {
525         property<DiagramAdapter>::reserve_properties(4);
526         property<DiagramAdapter>::add_property(L"props", &props::get, &props::set);
527         property<DiagramAdapter>::add_property(L"objs", &objs::get, &objs::set);
528         property<DiagramAdapter>::add_property(L"version", &version::get, &version::set);
529         property<DiagramAdapter>::add_property(L"contrib", &contrib::get, &contrib::set);
530         property<DiagramAdapter>::shrink_to_fit();
531     }
532 }
533
534 DiagramAdapter::DiagramAdapter(const DiagramAdapter& adapter) :
535     BaseAdapter<DiagramAdapter, org_scilab_modules_scicos::model::BaseObject>(adapter),
536     contrib_content(reference_value(adapter.contrib_content))
537 {
538 }
539
540 DiagramAdapter::~DiagramAdapter()
541 {
542     contrib_content->DecreaseRef();
543     contrib_content->killMe();
544 }
545
546 std::wstring DiagramAdapter::getTypeStr() const
547 {
548     return getSharedTypeStr();
549 }
550 std::wstring DiagramAdapter::getShortTypeStr() const
551 {
552     return getSharedTypeStr();
553 }
554
555 types::InternalType* DiagramAdapter::getContribContent() const
556 {
557     return contrib_content;
558 }
559
560 void DiagramAdapter::setContribContent(types::InternalType* v)
561 {
562     // v and contrib_content should not be nullptr
563
564     types::InternalType* temp = contrib_content;
565
566     v->IncreaseRef();
567     contrib_content = v;
568
569     temp->DecreaseRef();
570     temp->killMe();
571 }
572
573 } /* namespace view_scilab */
574 } /* namespace org_scilab_modules_scicos */