1 #include "pool.hpp"
2 #include "common/object_descr.hpp"
3 #include "entity.hpp"
4 #include "package.hpp"
5 #include "padstack.hpp"
6 #include "part.hpp"
7 #include "symbol.hpp"
8 #include "unit.hpp"
9 #include "pool_manager.hpp"
10 #include "logger/logger.hpp"
11 #include <glibmm/fileutils.h>
12 #include <glibmm/miscutils.h>
13 #include <giomm.h>
14 
15 namespace horizon {
16 
Pool(const std::string & bp,bool read_only)17 Pool::Pool(const std::string &bp, bool read_only)
18     : db(bp + "/pool.db", read_only ? SQLITE_OPEN_READONLY : SQLITE_OPEN_READWRITE), base_path(bp), pool_info(bp)
19 {
20 }
21 
22 const UUID Pool::tmp_pool_uuid = "5e8d1bb6-7e61-4c59-9f01-1e1307069df0";
23 
~Pool()24 Pool::~Pool()
25 {
26 }
27 
clear()28 void Pool::clear()
29 {
30     units.clear();
31     symbols.clear();
32     entities.clear();
33     padstacks.clear();
34     packages.clear();
35     parts.clear();
36     frames.clear();
37     decals.clear();
38     pool_uuid_cache.clear();
39 }
40 
get_rel_filename(ObjectType type,const UUID & uu)41 std::string Pool::get_rel_filename(ObjectType type, const UUID &uu)
42 {
43     const std::string query = "SELECT filename, pool_uuid FROM " + type_names.at(type) + " WHERE UUID = ?";
44     SQLite::Query q(db, query);
45     q.bind(1, uu);
46     if (q.step()) {
47         return q.get<std::string>(0);
48     }
49     else {
50         throw std::runtime_error(object_descriptions.at(type).name + " " + (std::string)uu + " not found");
51     }
52 }
53 
get_filename(ObjectType type,const UUID & uu,UUID * pool_uuid_out)54 std::string Pool::get_filename(ObjectType type, const UUID &uu, UUID *pool_uuid_out)
55 {
56     if (type_names.count(type) == 0)
57         throw std::runtime_error("pool doesn't support " + object_descriptions.at(type).name_pl);
58 
59     const std::string query = "SELECT filename, pool_uuid FROM " + type_names.at(type) + " WHERE UUID = ?";
60     SQLite::Query q(db, query);
61     q.bind(1, uu);
62     if (!q.step()) {
63         auto tf = get_tmp_filename(type, uu);
64         if (tf.size() && Glib::file_test(tf, Glib::FILE_TEST_IS_REGULAR)) {
65             if (pool_uuid_out)
66                 *pool_uuid_out = Pool::tmp_pool_uuid;
67             return tf;
68         }
69         else {
70             throw std::runtime_error(object_descriptions.at(type).name + " " + (std::string)uu + " not found");
71         }
72     }
73     auto filename = q.get<std::string>(0);
74     std::string bp = base_path;
75 
76     UUID other_pool_uuid(q.get<std::string>(1));
77     if (pool_uuid_out)
78         *pool_uuid_out = other_pool_uuid;
79     pool_uuid_cache.emplace(std::piecewise_construct, std::forward_as_tuple(type, uu),
80                             std::forward_as_tuple(other_pool_uuid));
81     const auto other_pool_info = PoolManager::get().get_by_uuid(other_pool_uuid);
82 
83     if (other_pool_info && pool_info.uuid != other_pool_info->uuid) // don't override if item is in local pool
84         bp = other_pool_info->base_path;
85 
86     return Glib::build_filename(bp, filename);
87 }
88 
get_base_path() const89 const std::string &Pool::get_base_path() const
90 {
91     return base_path;
92 }
93 
get_required_schema_version()94 int Pool::get_required_schema_version()
95 { // keep in sync with schema definition
96     return 21;
97 }
98 
get_tmp_filename(ObjectType type,const UUID & uu) const99 std::string Pool::get_tmp_filename(ObjectType type, const UUID &uu) const
100 {
101     auto suffix = static_cast<std::string>(uu) + ".json";
102     auto base = Glib::build_filename(Glib::get_tmp_dir(), "horizon-tmp");
103     if (!Glib::file_test(base, Glib::FILE_TEST_IS_DIR)) {
104         Gio::File::create_for_path(base)->make_directory();
105     }
106     return Glib::build_filename(base, get_flat_filename(type, uu));
107 }
108 
get_flat_filename(ObjectType type,const UUID & uu) const109 std::string Pool::get_flat_filename(ObjectType type, const UUID &uu) const
110 {
111     auto suffix = static_cast<std::string>(uu) + ".json";
112     switch (type) {
113     case ObjectType::UNIT:
114         return "unit_" + suffix;
115 
116     case ObjectType::ENTITY:
117         return "entity_" + suffix;
118 
119     case ObjectType::SYMBOL:
120         return "sym_" + suffix;
121 
122     case ObjectType::PACKAGE:
123         return "pkg_" + suffix;
124 
125     case ObjectType::PADSTACK:
126         return "ps_" + suffix;
127 
128     case ObjectType::PART:
129         return "part_" + suffix;
130 
131     case ObjectType::FRAME:
132         return "frame_" + suffix;
133 
134     case ObjectType::DECAL:
135         return "decal_" + suffix;
136 
137     default:
138         return "";
139     }
140 }
141 
get_pool_uuid(ObjectType type,const UUID & uu,UUID * pool_uuid_out)142 void Pool::get_pool_uuid(ObjectType type, const UUID &uu, UUID *pool_uuid_out)
143 {
144     if (pool_uuid_out)
145         *pool_uuid_out = pool_uuid_cache.at(std::make_pair(type, uu));
146 }
147 
get_unit(const UUID & uu,UUID * pool_uuid_out)148 const Unit *Pool::get_unit(const UUID &uu, UUID *pool_uuid_out)
149 {
150     if (units.count(uu) == 0) {
151         std::string path = get_filename(ObjectType::UNIT, uu, pool_uuid_out);
152         Unit u = Unit::new_from_file(path);
153         units.insert(std::make_pair(uu, u));
154     }
155     else {
156         get_pool_uuid(ObjectType::UNIT, uu, pool_uuid_out);
157     }
158     return &units.at(uu);
159 }
160 
get_entity(const UUID & uu,UUID * pool_uuid_out)161 const Entity *Pool::get_entity(const UUID &uu, UUID *pool_uuid_out)
162 {
163     if (entities.count(uu) == 0) {
164         std::string path = get_filename(ObjectType::ENTITY, uu, pool_uuid_out);
165         Entity e = Entity::new_from_file(path, *this);
166         entities.insert(std::make_pair(uu, e));
167     }
168     else {
169         get_pool_uuid(ObjectType::ENTITY, uu, pool_uuid_out);
170     }
171     return &entities.at(uu);
172 }
173 
get_symbol(const UUID & uu,UUID * pool_uuid_out)174 const Symbol *Pool::get_symbol(const UUID &uu, UUID *pool_uuid_out)
175 {
176     if (symbols.count(uu) == 0) {
177         std::string path = get_filename(ObjectType::SYMBOL, uu, pool_uuid_out);
178         Symbol s = Symbol::new_from_file(path, *this);
179         symbols.insert(std::make_pair(uu, s));
180     }
181     else {
182         get_pool_uuid(ObjectType::SYMBOL, uu, pool_uuid_out);
183     }
184     return &symbols.at(uu);
185 }
186 
get_package(const UUID & uu,UUID * pool_uuid_out)187 const Package *Pool::get_package(const UUID &uu, UUID *pool_uuid_out)
188 {
189     if (packages.count(uu) == 0) {
190         std::string path = get_filename(ObjectType::PACKAGE, uu, pool_uuid_out);
191         Package p = Package::new_from_file(path, *this);
192         packages.emplace(uu, p);
193     }
194     else {
195         get_pool_uuid(ObjectType::PACKAGE, uu, pool_uuid_out);
196     }
197     return &packages.at(uu);
198 }
199 
get_well_known_padstack(const std::string & name,UUID * pool_uuid_out)200 const Padstack *Pool::get_well_known_padstack(const std::string &name, UUID *pool_uuid_out)
201 {
202     SQLite::Query q(db, "SELECT uuid FROM padstacks WHERE well_known_name = ?");
203     q.bind(1, name);
204     if (q.step()) {
205         UUID uu = q.get<std::string>(0);
206         return get_padstack(uu, pool_uuid_out);
207     }
208     else {
209         return nullptr;
210     }
211 }
212 
get_padstack(const UUID & uu,UUID * pool_uuid_out)213 const Padstack *Pool::get_padstack(const UUID &uu, UUID *pool_uuid_out)
214 {
215     if (padstacks.count(uu) == 0) {
216         std::string path = get_filename(ObjectType::PADSTACK, uu, pool_uuid_out);
217         Padstack p = Padstack::new_from_file(path);
218         padstacks.insert(std::make_pair(uu, p));
219     }
220     else {
221         get_pool_uuid(ObjectType::PADSTACK, uu, pool_uuid_out);
222     }
223     return &padstacks.at(uu);
224 }
225 
get_part(const UUID & uu,UUID * pool_uuid_out)226 const Part *Pool::get_part(const UUID &uu, UUID *pool_uuid_out)
227 {
228     if (parts.count(uu) == 0) {
229         std::string path = get_filename(ObjectType::PART, uu, pool_uuid_out);
230         Part p = Part::new_from_file(path, *this);
231         parts.insert(std::make_pair(uu, p));
232     }
233     else {
234         get_pool_uuid(ObjectType::PART, uu, pool_uuid_out);
235     }
236     return &parts.at(uu);
237 }
238 
get_frame(const UUID & uu,UUID * pool_uuid_out)239 const Frame *Pool::get_frame(const UUID &uu, UUID *pool_uuid_out)
240 {
241     if (frames.count(uu) == 0) {
242         std::string path = get_filename(ObjectType::FRAME, uu, pool_uuid_out);
243         Frame f = Frame::new_from_file(path);
244         frames.insert(std::make_pair(uu, f));
245     }
246     else {
247         get_pool_uuid(ObjectType::FRAME, uu, pool_uuid_out);
248     }
249     return &frames.at(uu);
250 }
251 
get_decal(const UUID & uu,UUID * pool_uuid_out)252 const Decal *Pool::get_decal(const UUID &uu, UUID *pool_uuid_out)
253 {
254     if (decals.count(uu) == 0) {
255         std::string path = get_filename(ObjectType::DECAL, uu, pool_uuid_out);
256         Decal d = Decal::new_from_file(path);
257         decals.insert(std::make_pair(uu, d));
258     }
259     else {
260         get_pool_uuid(ObjectType::DECAL, uu, pool_uuid_out);
261     }
262     return &decals.at(uu);
263 }
264 
get_alternate_packages(const UUID & uu)265 std::set<UUID> Pool::get_alternate_packages(const UUID &uu)
266 {
267     std::set<UUID> r;
268     SQLite::Query q(db, "SELECT uuid FROM packages WHERE alternate_for = ?");
269     q.bind(1, uu);
270     while (q.step()) {
271         r.insert(q.get<std::string>(0));
272     }
273     return r;
274 }
275 
get_model_filename(const UUID & pkg_uuid,const UUID & model_uuid)276 std::string Pool::get_model_filename(const UUID &pkg_uuid, const UUID &model_uuid)
277 {
278     UUID pool_uuid;
279     auto pkg = get_package(pkg_uuid, &pool_uuid);
280     auto model = pkg->get_model(model_uuid);
281     if (!model)
282         return "";
283 
284     if (pool_uuid == pool_info.uuid) { // from this pool
285         return Glib::build_filename(base_path, model->filename);
286     }
287     else if (auto pool = PoolManager::get().get_by_uuid(pool_uuid)) {
288         return Glib::build_filename(pool->base_path, model->filename);
289     }
290     else {
291         return "";
292     }
293 }
294 
check_filename(ObjectType type,const std::string & filename,std::string * error_msg) const295 bool Pool::check_filename(ObjectType type, const std::string &filename, std::string *error_msg) const
296 {
297     if (!type_names.count(type)) {
298         if (error_msg)
299             *error_msg = "unsupported object type";
300         return false;
301     }
302     if (type == ObjectType::PADSTACK) {
303         const std::string bp_ps = Glib::build_filename(base_path, type_names.at(type));
304         if (Gio::File::create_for_path(filename)->has_prefix(Gio::File::create_for_path(bp_ps)))
305             return true;
306         const std::string bp_pkg = Glib::build_filename(base_path, type_names.at(type));
307         if (Gio::File::create_for_path(filename)->has_prefix(Gio::File::create_for_path(bp_pkg))) {
308             return true;
309         }
310         if (error_msg)
311             *error_msg = "incorrect directory";
312         return false;
313     }
314     else {
315         const std::string bp = Glib::build_filename(base_path, type_names.at(type));
316         auto r = Gio::File::create_for_path(filename)->has_prefix(Gio::File::create_for_path(bp));
317         if (!r) {
318             if (error_msg)
319                 *error_msg = "incorrect directory";
320         }
321         return r;
322     }
323 }
324 
get_actually_included_pools(bool include_self)325 std::map<std::string, UUID> Pool::get_actually_included_pools(bool include_self)
326 {
327     std::map<std::string, UUID> base_paths;
328     {
329         SQLite::Query q(db, "SELECT uuid FROM pools_included WHERE level > 0");
330         while (q.step()) {
331             const UUID pool_uu(q.get<std::string>(0));
332             if (auto pool2 = PoolManager::get().get_by_uuid(pool_uu)) {
333                 if (!base_paths.emplace(pool2->base_path, pool_uu).second) {
334                     throw std::runtime_error("conflicting base path " + pool2->base_path);
335                 }
336             }
337             else {
338                 Logger::log_warning("included pool " + (std::string)pool_uu + " not found");
339             }
340         }
341     }
342     if (include_self)
343         base_paths.emplace(get_base_path(), get_pool_info().uuid);
344     return base_paths;
345 }
346 
347 
348 } // namespace horizon
349