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