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