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