Update CHANGES.md before the release
[scilab.git] / scilab / modules / scicos / src / cpp / Model.cpp
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2014-2017 - 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 <cassert>
17 #include <string>
18 #include <vector>
19 #include <utility>
20 #include <algorithm>
21
22 #include "Model.hxx"
23 #include "utilities.hxx"
24
25 #include "model/BaseObject.hxx"
26 #include "model/Annotation.hxx"
27 #include "model/Diagram.hxx"
28 #include "model/Block.hxx"
29 #include "model/Link.hxx"
30 #include "model/Port.hxx"
31
32 namespace org_scilab_modules_scicos
33 {
34
35 Model::Model() :
36     lastId(ScicosID()), has_looped(false), allObjects()
37 {
38     std::vector<int> datatypeDefault (3, 1);
39     datatypeDefault[0] = -1;
40     datatypes.push_back(new model::Datatype(datatypeDefault));
41 }
42
43 Model::~Model()
44 {
45     while (!datatypes.empty())
46     {
47         Model::erase(datatypes[0]);
48     }
49     datatypes.clear();
50 }
51
52 /* define a custom delete as the BaseObject class is fully abstract */
53 static inline void deleteBaseObject(model::BaseObject* o)
54 {
55     switch (o->kind())
56     {
57         case ANNOTATION:
58             delete static_cast<model::Annotation*>(o);
59             break;
60         case DIAGRAM:
61             delete static_cast<model::Diagram*>(o);
62             break;
63         case BLOCK:
64             delete static_cast<model::Block*>(o);
65             break;
66         case LINK:
67             delete static_cast<model::Link*>(o);
68             break;
69         case PORT:
70             delete static_cast<model::Port*>(o);
71             break;
72         default:
73             break;
74     }
75 };
76
77
78 model::BaseObject* Model::createObject(kind_t k)
79 {
80     /*
81      * Allocate the object per kind
82      */
83     model::BaseObject* o;
84     switch (k)
85     {
86         case ANNOTATION:
87             o = new model::Annotation();
88             break;
89         case DIAGRAM:
90             o = new model::Diagram();
91             break;
92         case BLOCK:
93             o = new model::Block();
94             break;
95         case LINK:
96             o = new model::Link();
97             break;
98         case PORT:
99             o = new model::Port();
100             break;
101         default:
102             return nullptr;
103     }
104
105     /*
106      * Found the next unused id
107      */
108     lastId++;
109     if (lastId == ScicosID())
110     {
111         lastId++;
112         has_looped = true;
113     }
114
115     if (has_looped)
116     {
117         bool has_looped_twice = false;
118
119         // while key is found
120         for (allobjects_t::iterator iter = allObjects.find(lastId);
121                 iter != allObjects.end();
122                 iter = allObjects.find(lastId))
123         {
124             // try a valid ID
125             lastId++;
126             if (lastId == ScicosID())
127             {
128                 lastId++;
129
130                 // return the invalid value if the loop counter encounter 2 zeros.
131                 if (has_looped_twice)
132                 {
133                     deleteBaseObject(o);
134                     return nullptr;
135                 }
136                 else
137                 {
138                     has_looped_twice = true;
139                 }
140             }
141         }
142     }
143
144     /*
145      * Insert then return
146      */
147     o->id(lastId);
148     allObjects.emplace(lastId, o);
149     return o;
150 }
151
152 unsigned Model::referenceObject(model::BaseObject* object)
153 {
154     return ++object->refCount();
155 }
156
157 unsigned& Model::referenceCount(model::BaseObject* object)
158 {
159     return object->refCount();
160 }
161
162 void Model::deleteObject(model::BaseObject* object)
163 {
164     if (object->refCount() == 0)
165     {
166         if (allObjects.erase(object->id()))
167         {
168             deleteBaseObject(object);
169         }
170         else
171         {
172             // something went wrong, assert !
173             assert(0);
174         }
175     }
176     else
177     {
178         --object->refCount();
179     }
180 }
181
182 kind_t Model::getKind(ScicosID uid) const
183 {
184     model::BaseObject* o = getObject(uid);
185     if (o != nullptr)
186     {
187         return o->kind();
188     }
189     else
190     {
191         // return the first kind, it will be always ignored as the object is no more valid
192         return ANNOTATION;
193     }
194 }
195
196 std::vector<model::BaseObject*> Model::getAll(kind_t k) const
197 {
198     std::vector<model::BaseObject*> all;
199     for (const auto& it : allObjects)
200         if (it.second->kind() == k)
201         {
202             all.emplace_back(it.second);
203         }
204
205     return all;
206 }
207
208 model::BaseObject* Model::getObject(ScicosID uid) const
209 {
210     allobjects_t::const_iterator iter = allObjects.find(uid);
211     if (iter != allObjects.end() && iter->first == uid)
212     {
213         return iter->second;
214     }
215
216     return nullptr;
217 }
218
219 // datatypes being a vector of Datatype pointers, we need a dereferencing comparison operator to use std::lower_bound()
220 static bool isInferior(const model::Datatype* d1, const model::Datatype* d2)
221 {
222     return *d1 < *d2;
223 }
224
225 model::Datatype* Model::flyweight(const model::Datatype& d)
226 {
227     datatypes_set_t::iterator iter = std::lower_bound(datatypes.begin(), datatypes.end(), &d, isInferior);
228     if (iter != datatypes.end() && !(d < **iter)) // if d is found
229     {
230         (*iter)->m_refCount++;
231         return *iter;
232     }
233     else
234     {
235         return *datatypes.insert(iter, new model::Datatype(d));
236     }
237 }
238
239 void Model::erase(model::Datatype* d)
240 {
241     datatypes_set_t::iterator iter = std::lower_bound(datatypes.begin(), datatypes.end(), d, isInferior);
242     if (iter != datatypes.end() && !(*d < **iter)) // if d is found
243     {
244         (*iter)->m_refCount--;
245         if ((*iter)->m_refCount < 0)
246         {
247             delete *iter;
248             datatypes.erase(iter);
249         }
250     }
251 }
252
253 } /* namespace org_scilab_modules_scicos */