4fd76af31765afe80570079240753904af8ec6e0
[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 "internal.hxx"
19 #include "double.hxx"
20 #include "list.hxx"
21 #include "mlist.hxx"
22 #include "tlist.hxx"
23 #include "string.hxx"
24 #include "types.hxx"
25 #include "user.hxx"
26
27 #include "utilities.hxx"
28 #include "adapters_utilities.hxx"
29 #include "Controller.hxx"
30 #include "DiagramAdapter.hxx"
31
32 #include "view_scilab/Adapters.hxx"
33 #include "ParamsAdapter.hxx"
34 #include "BlockAdapter.hxx"
35 #include "LinkAdapter.hxx"
36 #include "TextAdapter.hxx"
37 #include "model/BaseObject.hxx"
38
39 extern "C" {
40 #include "sci_malloc.h"
41 #include "charEncoding.h"
42 }
43
44 namespace org_scilab_modules_scicos
45 {
46 namespace view_scilab
47 {
48 namespace
49 {
50
51 const std::wstring Deleted (L"Deleted");
52 const std::wstring TextSharedTypeStr (L"Text");
53 const std::wstring BlockSharedTypeStr (L"Block");
54 const std::wstring LinkSharedTypeStr (L"Link");
55
56 struct props
57 {
58
59     static types::InternalType* get(const DiagramAdapter& adaptor, const Controller& controller)
60     {
61         ParamsAdapter localAdaptor(controller, controller.referenceObject(adaptor.getAdaptee()));
62         return localAdaptor.getAsTList(new types::TList(), controller);
63     }
64
65     static bool set(DiagramAdapter& adaptor, types::InternalType* v, Controller& controller)
66     {
67         ParamsAdapter localAdaptor(controller, controller.referenceObject(adaptor.getAdaptee()));
68         return localAdaptor.setAsTList(v, controller);
69     }
70 };
71
72 struct objs
73 {
74
75     static types::InternalType* get(const DiagramAdapter& adaptor, const Controller& /*controller*/)
76     {
77         // FIXME: reconstruct the list of objects accordingly to the CHILDREN content to manage xcos modification
78         return adaptor.getListObjects();
79     }
80
81     /**
82      * Clone the object if it is owned by something else than the list
83      */
84     static types::InternalType* cloneIfNeeded(types::InternalType* v, const std::vector<types::InternalType*>& oldChildren)
85     {
86         if (v->getRef() == 0)
87         {
88             return v;
89         }
90
91         // avoid a copy, if v is already in the previous children list and will be cleanup later
92         if (v->getRef() == 1 && std::binary_search(oldChildren.begin(), oldChildren.end(), v))
93         {
94             return v;
95         }
96
97         return v->clone();
98     }
99
100     static std::vector<types::InternalType*> extractAndSort(types::List* v)
101     {
102         std::vector<types::InternalType*> ret;
103         if (v == nullptr)
104         {
105             return ret;
106         }
107
108         ret.reserve(v->getSize());
109         for (int i = 0; i < v->getSize(); ++i)
110         {
111             ret.push_back(v->get(i));
112         }
113
114         std::sort(ret.begin(), ret.end());
115         return ret;
116     }
117
118
119     static bool set(DiagramAdapter& adaptor, types::InternalType* v, Controller& controller)
120     {
121         // Decode the list and set all children of the Diagram
122         if (v->getType() != types::InternalType::ScilabList)
123         {
124             return false;
125         }
126
127         /*
128          * Always deep clone both the list and children as the PARENT_DIAGRAM property will be updated
129          */
130         model::Diagram* adaptee = adaptor.getAdaptee();
131
132         types::List* argumentList = v->getAs<types::List>();
133         types::List* list = new types::List();
134         std::vector<types::InternalType*> oldChildren = extractAndSort(adaptor.getListObjects());
135
136         std::vector<ScicosID> oldDiagramChildren;
137         controller.getObjectProperty(adaptee->id(), DIAGRAM, CHILDREN, oldDiagramChildren);
138
139         /*
140          * First pass on objects :
141          *  - store IDs if they exists and are valid ; 0ll otherwise
142          *  - store all the links to update link connections later
143          *  - store all the valid mlist content ('Text' content)
144          *  - store all the deleted mlist content ('Deleted' content)
145          */
146
147         std::vector<ScicosID> diagramChildren;
148         diagramChildren.reserve(argumentList->getSize());
149         std::vector<kind_t> diagramChildrenKind;
150         diagramChildrenKind.reserve(argumentList->getSize());
151
152         std::vector<LinkAdapter*> links;
153         std::vector<int> textAsMListIndices;
154         std::vector<int> deletedAsMListIndices;
155
156         for (int i = 0; i < argumentList->getSize(); ++i)
157         {
158             if (argumentList->get(i)->getType() == types::InternalType::ScilabUserType)
159             {
160                 const Adapters::adapters_index_t adapter_index = Adapters::instance().lookup_by_typename(argumentList->get(i)->getShortTypeStr());
161
162                 ScicosID id;
163                 kind_t kind;
164                 types::InternalType* adapter;
165                 switch (adapter_index)
166                 {
167                     case Adapters::BLOCK_ADAPTER:
168                     {
169                         BlockAdapter* modelElement = cloneIfNeeded(argumentList->get(i), oldChildren)->getAs<BlockAdapter>();
170                         id = modelElement->getAdaptee()->id();
171                         kind = modelElement->getAdaptee()->kind();
172                         adapter = modelElement;
173                         break;
174                     }
175                     case Adapters::LINK_ADAPTER:
176                     {
177                         LinkAdapter* modelElement = cloneIfNeeded(argumentList->get(i), oldChildren)->getAs<LinkAdapter>();
178                         id = modelElement->getAdaptee()->id();
179                         kind = modelElement->getAdaptee()->kind();
180                         adapter = modelElement;
181
182                         // Do the linking in the next loop, in case the Link points to a Block that has not been added yet
183                         links.push_back(modelElement);
184                         break;
185                     }
186                     case Adapters::TEXT_ADAPTER:
187                     {
188                         TextAdapter* modelElement = cloneIfNeeded(argumentList->get(i), oldChildren)->getAs<TextAdapter>();
189                         id = modelElement->getAdaptee()->id();
190                         kind = modelElement->getAdaptee()->kind();
191                         adapter = modelElement;
192                         break;
193                     }
194                     default:
195                         list->killMe();
196                         return false;
197                 }
198
199                 diagramChildren.push_back(id);
200                 diagramChildrenKind.push_back(kind);
201                 list->set(i, adapter);
202             }
203             else if (argumentList->get(i)->getType() == types::InternalType::ScilabMList)
204             {
205                 // Allow to pass mlists to 'objs', representing 'Deleted' or 'Text' objects
206                 types::MList* modelElement = argumentList->get(i)->getAs<types::MList>();
207                 types::String* header = modelElement->getFieldNames();
208
209                 if (header->get(0) == Deleted)
210                 {
211                     deletedAsMListIndices.push_back(i);
212                     diagramChildren.push_back(0ll);
213                     diagramChildrenKind.push_back(ANNOTATION);
214                 }
215                 else if (header->get(0) == TextSharedTypeStr)
216                 {
217                     textAsMListIndices.push_back(i);
218                     // will be filled later
219                     diagramChildren.push_back(0ll);
220                     diagramChildrenKind.push_back(ANNOTATION);
221                 }
222                 else if (header->get(0) == BlockSharedTypeStr)
223                 {
224                     ScicosID localAdaptee = controller.createObject(BLOCK);
225                     BlockAdapter* localAdaptor = new BlockAdapter(controller, controller.getObject<model::Block>(localAdaptee));
226                     if (!localAdaptor->setAsTList(modelElement, controller))
227                     {
228                         list->killMe();
229                         return false;
230                     }
231
232                     diagramChildren.push_back(localAdaptee);
233                     list->set(i, localAdaptor);
234                 }
235                 else if (header->get(0) == LinkSharedTypeStr)
236                 {
237                     ScicosID localAdaptee = controller.createObject(LINK);
238                     LinkAdapter* localAdaptor = new LinkAdapter(controller, controller.getObject<model::Link>(localAdaptee));
239                     if (!localAdaptor->setAsTList(modelElement, controller))
240                     {
241                         list->killMe();
242                         return false;
243                     }
244
245                     diagramChildren.push_back(localAdaptee);
246                     list->set(i, localAdaptor);
247                 }
248                 else
249                 {
250                     list->killMe();
251                     return false;
252                 }
253             }
254             else if (argumentList->get(i)->getType() == types::InternalType::ScilabList)
255             {
256                 // Allow to pass empty lists to 'objs', representing deleted Blocks
257                 types::List* modelElement = argumentList->get(i)->getAs<types::List>();
258                 if (modelElement->getSize() != 0)
259                 {
260                     list->killMe();
261                     return false;
262                 }
263
264                 deletedAsMListIndices.push_back(i);
265                 // Mark deleted objects with value '0'
266                 diagramChildren.push_back(0ll);
267                 diagramChildrenKind.push_back(ANNOTATION);
268             }
269             else
270             {
271                 list->killMe();
272                 return false;
273             }
274         }
275
276         /*
277          * Create all Annotation, decoding mlist content
278          */
279         for (const auto index : textAsMListIndices)
280         {
281             ScicosID localAdaptee = controller.createObject(ANNOTATION);
282             TextAdapter* localAdaptor = new TextAdapter(controller, controller.getObject<model::Annotation>(localAdaptee));
283             if (!localAdaptor->setAsTList(argumentList->get(index), controller))
284             {
285                 // do not return there ; the annotation will be empty
286             }
287
288             diagramChildren[index] = localAdaptee;
289             list->set(index, localAdaptor);
290         }
291
292         /*
293          * Recreate 'Deleted' mlist if needed
294          */
295         for (const auto index : deletedAsMListIndices)
296         {
297             types::MList* deleted = new types::MList();
298             deleted->set(0, new types::String(Deleted.data()));
299
300             list->set(index, deleted);
301         }
302
303         /*
304          * Set the parent diagram of all the blocks
305          */
306         controller.setObjectProperty(adaptee->id(), DIAGRAM, CHILDREN, diagramChildren);
307         {
308             std::sort(oldDiagramChildren.begin(), oldDiagramChildren.end());
309             for (const ScicosID id : diagramChildren)
310             {
311                 if (id != 0 && !std::binary_search(oldDiagramChildren.begin(), oldDiagramChildren.end(), id))
312                 {
313                     auto o = controller.getObject(id);
314                     controller.setObjectProperty(o->id(), o->kind(), PARENT_DIAGRAM, adaptee->id());
315
316                     controller.referenceObject(id);
317                 }
318             }
319
320             std::sort(diagramChildren.begin(), diagramChildren.end());
321             for (const ScicosID id : oldDiagramChildren)
322             {
323                 if (id != 0 && !std::binary_search(diagramChildren.begin(), diagramChildren.end(), id))
324                 {
325                     auto o = controller.getObject(id);
326                     controller.setObjectProperty(o->id(), o->kind(), PARENT_DIAGRAM, ScicosID());
327
328                     controller.deleteObject(id);
329                 }
330             }
331         }
332
333         /*
334          * Store the children to the local adaptor, avoiding the deletion of argument
335          */
336         v->IncreaseRef();
337         adaptor.setListObjects(list);
338
339         /*
340          * Re-sync the partial link information
341          */
342         std::vector<link_t> from_content (links.size());
343         std::vector<link_t> to_content (links.size());
344         // Do the linking at model-level
345         for (int i = 0; i < static_cast<int>(links.size()); ++i)
346         {
347             // Trigger 'from' and 'to' properties
348             from_content[i] = links[i]->getFrom();
349             links[i]->setFromInModel(from_content[i], controller);
350
351             to_content[i] = links[i]->getTo();
352             links[i]->setToInModel(to_content[i], controller);
353         }
354         adaptor.setFrom(from_content);
355         adaptor.setTo(to_content);
356
357         v->DecreaseRef();
358         return true;
359     }
360 };
361
362 struct version
363 {
364
365     static types::InternalType* get(const DiagramAdapter& adaptor, const Controller& controller)
366     {
367         model::Diagram* adaptee = adaptor.getAdaptee();
368
369         std::string version;
370         controller.getObjectProperty(adaptee->id(), DIAGRAM, VERSION_NUMBER, version);
371
372         return new types::String(version.data());
373     }
374
375     static bool set(DiagramAdapter& adaptor, types::InternalType* v, Controller& controller)
376     {
377         if (v->getType() == types::InternalType::ScilabString)
378         {
379             types::String* current = v->getAs<types::String>();
380             if (current->getSize() != 1)
381             {
382                 return false;
383             }
384
385             model::Diagram* adaptee = adaptor.getAdaptee();
386
387             char* c_str = wide_string_to_UTF8(current->get(0));
388             std::string version (c_str);
389             FREE(c_str);
390
391             controller.setObjectProperty(adaptee->id(), DIAGRAM, VERSION_NUMBER, version);
392             return true;
393         }
394         else if (v->getType() == types::InternalType::ScilabDouble)
395         {
396             types::Double* current = v->getAs<types::Double>();
397             if (current->getSize() != 0)
398             {
399                 return false;
400             }
401
402             model::Diagram* adaptee = adaptor.getAdaptee();
403
404             std::string version;
405             controller.setObjectProperty(adaptee->id(), DIAGRAM, VERSION_NUMBER, version);
406             return true;
407         }
408
409         return false;
410     }
411 };
412
413 struct contrib
414 {
415
416     static types::InternalType* get(const DiagramAdapter& adaptor, const Controller& /*controller*/)
417     {
418         return adaptor.getContribContent();
419     }
420
421     static bool set(DiagramAdapter& adaptor, types::InternalType* v, Controller& /*controller*/)
422     {
423         adaptor.setContribContent(v->clone());
424         return true;
425     }
426 };
427
428 } /* namespace */
429
430 template<> property<DiagramAdapter>::props_t property<DiagramAdapter>::fields = property<DiagramAdapter>::props_t();
431
432 DiagramAdapter::DiagramAdapter(const Controller& c, org_scilab_modules_scicos::model::Diagram* adaptee) :
433     BaseAdapter<DiagramAdapter, org_scilab_modules_scicos::model::Diagram>(c, adaptee),
434     list_objects(nullptr),
435     from_vec(),
436     to_vec(),
437     contrib_content(nullptr)
438 {
439     if (property<DiagramAdapter>::properties_have_not_been_set())
440     {
441         property<DiagramAdapter>::fields.reserve(4);
442         property<DiagramAdapter>::add_property(L"props", &props::get, &props::set);
443         property<DiagramAdapter>::add_property(L"objs", &objs::get, &objs::set);
444         property<DiagramAdapter>::add_property(L"version", &version::get, &version::set);
445         property<DiagramAdapter>::add_property(L"contrib", &contrib::get, &contrib::set);
446     }
447
448     setListObjects(new types::List());
449     setContribContent(new types::List());
450 }
451
452 DiagramAdapter::DiagramAdapter(const DiagramAdapter& adapter) :
453     BaseAdapter<DiagramAdapter, org_scilab_modules_scicos::model::Diagram>(adapter, false),
454     list_objects(nullptr),
455     from_vec(),
456     to_vec(),
457     contrib_content(nullptr)
458 {
459     Controller controller;
460
461     // set the list and perform from / to links update
462     objs::set(*this, adapter.getListObjects(), controller);
463
464     setContribContent(adapter.getContribContent());
465 }
466
467 DiagramAdapter::~DiagramAdapter()
468 {
469     // CHILDREN will be unreferenced on Controller::deleteObject
470
471     list_objects->DecreaseRef();
472     list_objects->killMe();
473
474     contrib_content->DecreaseRef();
475     contrib_content->killMe();
476 }
477
478 std::wstring DiagramAdapter::getTypeStr()
479 {
480     return getSharedTypeStr();
481 }
482 std::wstring DiagramAdapter::getShortTypeStr()
483 {
484     return getSharedTypeStr();
485 }
486
487 types::InternalType* DiagramAdapter::getContribContent() const
488 {
489     return contrib_content;
490 }
491
492 void DiagramAdapter::setContribContent(types::InternalType* v)
493 {
494     types::InternalType* temp = contrib_content;
495
496     // do not check if v is nullptr on purpose ; it *should* not
497     v->IncreaseRef();
498     contrib_content = v;
499
500     if (temp != nullptr)
501     {
502         temp->DecreaseRef();
503         temp->killMe();
504     }
505
506
507 }
508
509 types::List* DiagramAdapter::getListObjects() const
510 {
511     return list_objects;
512 }
513
514 void DiagramAdapter::setListObjects(types::List* v)
515 {
516     types::InternalType* temp = list_objects;
517
518     // do not check if v is nullptr on purpose ; it *should* not
519     v->IncreaseRef();
520     list_objects = v;
521
522     if (temp != nullptr)
523     {
524         temp->DecreaseRef();
525         temp->killMe();
526     }
527 }
528
529 std::vector<link_t> DiagramAdapter::getFrom() const
530 {
531     return from_vec;
532 }
533
534 void DiagramAdapter::setFrom(const std::vector<link_t>& from)
535 {
536     from_vec = from;
537 }
538
539 std::vector<link_t> DiagramAdapter::getTo() const
540 {
541     return to_vec;
542 }
543
544 void DiagramAdapter::setTo(const std::vector<link_t>& to)
545 {
546     to_vec = to;
547 }
548
549 } /* namespace view_scilab */
550 } /* namespace org_scilab_modules_scicos */