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