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