/src/assimp/code/AssetLib/STEPParser/STEPFileReader.cpp
Line | Count | Source |
1 | | /* |
2 | | Open Asset Import Library (assimp) |
3 | | ---------------------------------------------------------------------- |
4 | | |
5 | | Copyright (c) 2006-2026, assimp team |
6 | | |
7 | | All rights reserved. |
8 | | |
9 | | Redistribution and use of this software in source and binary forms, |
10 | | with or without modification, are permitted provided that the |
11 | | following conditions are met: |
12 | | |
13 | | * Redistributions of source code must retain the above |
14 | | copyright notice, this list of conditions and the |
15 | | following disclaimer. |
16 | | |
17 | | * Redistributions in binary form must reproduce the above |
18 | | copyright notice, this list of conditions and the |
19 | | following disclaimer in the documentation and/or other |
20 | | materials provided with the distribution. |
21 | | |
22 | | * Neither the name of the assimp team, nor the names of its |
23 | | contributors may be used to endorse or promote products |
24 | | derived from this software without specific prior |
25 | | written permission of the assimp team. |
26 | | |
27 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
28 | | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
29 | | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
30 | | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
31 | | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
32 | | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
33 | | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
34 | | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
35 | | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
36 | | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
37 | | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
38 | | |
39 | | ---------------------------------------------------------------------- |
40 | | */ |
41 | | |
42 | | /** |
43 | | * @file STEPFileReader.cpp |
44 | | * @brief Implementation of the STEP file parser, which fills a |
45 | | * STEP::DB with data read from a file. |
46 | | */ |
47 | | |
48 | | #include "STEPFileReader.h" |
49 | | #include "STEPFileEncoding.h" |
50 | | #include <assimp/TinyFormatter.h> |
51 | | #include <assimp/fast_atof.h> |
52 | | #include <functional> |
53 | | #include <memory> |
54 | | #include <utility> |
55 | | |
56 | | using namespace Assimp; |
57 | | |
58 | | namespace EXPRESS = STEP::EXPRESS; |
59 | | |
60 | | // ------------------------------------------------------------------------------------------------ |
61 | 0 | std::string AddLineNumber(const std::string& s,uint64_t line /*= LINE_NOT_SPECIFIED*/, const std::string& prefix = std::string()) { |
62 | 0 | return line == STEP::SyntaxError::LINE_NOT_SPECIFIED ? prefix+s : static_cast<std::string>( (Formatter::format(),prefix,"(line ",line,") ",s) ); |
63 | 0 | } |
64 | | |
65 | | // ------------------------------------------------------------------------------------------------ |
66 | 0 | std::string AddEntityID(const std::string& s,uint64_t entity /*= ENTITY_NOT_SPECIFIED*/, const std::string& prefix = std::string()) { |
67 | 0 | return entity == STEP::TypeError::ENTITY_NOT_SPECIFIED ? prefix+s : static_cast<std::string>( (Formatter::format(),prefix,"(entity #",entity,") ",s)); |
68 | 0 | } |
69 | | |
70 | | |
71 | | // ------------------------------------------------------------------------------------------------ |
72 | 0 | STEP::SyntaxError::SyntaxError (const std::string& s,uint64_t line) : DeadlyImportError(AddLineNumber(s,line)) { |
73 | | // empty |
74 | 0 | } |
75 | | |
76 | | // ------------------------------------------------------------------------------------------------ |
77 | 0 | STEP::TypeError::TypeError (const std::string& s,uint64_t entity, uint64_t line) : DeadlyImportError(AddLineNumber(AddEntityID(s,entity),line)) { |
78 | | // empty |
79 | 0 | } |
80 | | |
81 | | static constexpr char ISO_Token[] = "ISO-10303-21;"; |
82 | | static constexpr char FILE_SCHEMA_Token[] = "FILE_SCHEMA"; |
83 | | // ------------------------------------------------------------------------------------------------ |
84 | 4 | STEP::DB* STEP::ReadFileHeader(std::shared_ptr<IOStream> stream) { |
85 | 4 | std::shared_ptr<StreamReaderLE> reader = std::shared_ptr<StreamReaderLE>(new StreamReaderLE(std::move(stream))); |
86 | 4 | std::unique_ptr<STEP::DB> db = std::unique_ptr<STEP::DB>(new STEP::DB(reader)); |
87 | | |
88 | 4 | LineSplitter &splitter = db->GetSplitter(); |
89 | 4 | if (!splitter || *splitter != ISO_Token ) { |
90 | 0 | throw STEP::SyntaxError("expected magic token: " + std::string( ISO_Token ), 1); |
91 | 0 | } |
92 | | |
93 | 4 | HeaderInfo& head = db->GetHeader(); |
94 | 606 | for(++splitter; splitter; ++splitter) { |
95 | 602 | const std::string& s = *splitter; |
96 | 602 | if (s == "DATA;") { |
97 | | // here we go, header done, start of data section |
98 | 0 | ++splitter; |
99 | 0 | break; |
100 | 0 | } |
101 | | |
102 | | // want one-based line numbers for human readers, so +1 |
103 | 602 | const uint64_t line = splitter.get_index()+1; |
104 | | |
105 | 602 | if (s.substr(0,11) == FILE_SCHEMA_Token) { |
106 | 212 | const char* sz = s.c_str()+11; |
107 | 212 | const char *end = s.c_str() + s.size(); |
108 | 212 | SkipSpaces(sz,&sz, end); |
109 | 212 | std::shared_ptr< const EXPRESS::DataType > schema = EXPRESS::DataType::Parse(sz, end); |
110 | | |
111 | | // the file schema should be a regular list entity, although it usually contains exactly one entry |
112 | | // since the list itself is contained in a regular parameter list, we actually have |
113 | | // two nested lists. |
114 | 212 | const EXPRESS::LIST* list = dynamic_cast<const EXPRESS::LIST*>(schema.get()); |
115 | 212 | if (list && list->GetSize()) { |
116 | 0 | list = dynamic_cast<const EXPRESS::LIST*>( (*list)[0].get() ); |
117 | 0 | if (!list) { |
118 | 0 | throw STEP::SyntaxError("expected FILE_SCHEMA to be a list",line); |
119 | 0 | } |
120 | | |
121 | | // XXX need support for multiple schemas? |
122 | 0 | if (list->GetSize() > 1) { |
123 | 0 | ASSIMP_LOG_WARN(AddLineNumber("multiple schemas currently not supported",line)); |
124 | 0 | } |
125 | 0 | const EXPRESS::STRING *string = dynamic_cast<const EXPRESS::STRING *>((*list)[0].get()); |
126 | 0 | if (!list->GetSize() || nullptr == string ) { |
127 | 0 | throw STEP::SyntaxError("expected FILE_SCHEMA to contain a single string literal",line); |
128 | 0 | } |
129 | 0 | head.fileSchema = *string; |
130 | 0 | } |
131 | 212 | } |
132 | | |
133 | | // XXX handle more header fields |
134 | 602 | } |
135 | | |
136 | 4 | return db.release(); |
137 | 4 | } |
138 | | |
139 | | |
140 | | namespace { |
141 | | |
142 | | // ------------------------------------------------------------------------------------------------ |
143 | | // check whether the given line contains an entity definition (i.e. starts with "#<number>=") |
144 | | bool IsEntityDef(const std::string& snext) |
145 | 0 | { |
146 | 0 | if (snext[0] == '#') { |
147 | | // it is only a new entity if it has a '=' after the |
148 | | // entity ID. |
149 | 0 | for(std::string::const_iterator it = snext.begin()+1; it != snext.end(); ++it) { |
150 | 0 | if (*it == '=') { |
151 | 0 | return true; |
152 | 0 | } |
153 | 0 | if ((*it < '0' || *it > '9') && *it != ' ') { |
154 | 0 | break; |
155 | 0 | } |
156 | 0 | } |
157 | 0 | } |
158 | 0 | return false; |
159 | 0 | } |
160 | | |
161 | | } |
162 | | |
163 | | |
164 | | // ------------------------------------------------------------------------------------------------ |
165 | | void STEP::ReadFile(DB& db,const EXPRESS::ConversionSchema& scheme, |
166 | | const char* const* types_to_track, size_t len, |
167 | | const char* const* inverse_indices_to_track, size_t len2) |
168 | 0 | { |
169 | 0 | db.SetSchema(scheme); |
170 | 0 | db.SetTypesToTrack(types_to_track,len); |
171 | 0 | db.SetInverseIndicesToTrack(inverse_indices_to_track,len2); |
172 | |
|
173 | 0 | const DB::ObjectMap& map = db.GetObjects(); |
174 | 0 | LineSplitter& splitter = db.GetSplitter(); |
175 | |
|
176 | 0 | while (splitter) { |
177 | 0 | bool has_next = false; |
178 | 0 | std::string s = *splitter; |
179 | 0 | if (s == "ENDSEC;") { |
180 | 0 | break; |
181 | 0 | } |
182 | 0 | s.erase(std::remove(s.begin(), s.end(), ' '), s.end()); |
183 | | |
184 | | // want one-based line numbers for human readers, so +1 |
185 | 0 | const uint64_t line = splitter.get_index()+1; |
186 | | // LineSplitter already ignores empty lines |
187 | 0 | ai_assert(s.length()); |
188 | 0 | if (s[0] != '#') { |
189 | 0 | ASSIMP_LOG_WARN(AddLineNumber("expected token \'#\'",line)); |
190 | 0 | ++splitter; |
191 | 0 | continue; |
192 | 0 | } |
193 | | // --- |
194 | | // extract id, entity class name and argument string, |
195 | | // but don't create the actual object yet. |
196 | | // --- |
197 | 0 | const std::string::size_type n0 = s.find_first_of('='); |
198 | 0 | if (n0 == std::string::npos) { |
199 | 0 | ASSIMP_LOG_WARN(AddLineNumber("expected token \'=\'",line)); |
200 | 0 | ++splitter; |
201 | 0 | continue; |
202 | 0 | } |
203 | | |
204 | 0 | const uint64_t id = strtoul10_64(s.substr(1,n0-1).c_str()); |
205 | 0 | if (!id) { |
206 | 0 | ASSIMP_LOG_WARN(AddLineNumber("expected positive, numeric entity id",line)); |
207 | 0 | ++splitter; |
208 | 0 | continue; |
209 | 0 | } |
210 | 0 | std::string::size_type n1 = s.find_first_of('(',n0); |
211 | 0 | if (n1 == std::string::npos) { |
212 | 0 | has_next = true; |
213 | 0 | bool ok = false; |
214 | 0 | for( ++splitter; splitter; ++splitter) { |
215 | 0 | const std::string& snext = *splitter; |
216 | 0 | if (snext.empty()) { |
217 | 0 | continue; |
218 | 0 | } |
219 | | |
220 | | // the next line doesn't start an entity, so maybe it is |
221 | | // just a continuation for this line, keep going |
222 | 0 | if (!IsEntityDef(snext)) { |
223 | 0 | s.append(snext); |
224 | 0 | n1 = s.find_first_of('(',n0); |
225 | 0 | ok = (n1 != std::string::npos); |
226 | 0 | } |
227 | 0 | else { |
228 | 0 | break; |
229 | 0 | } |
230 | 0 | } |
231 | |
|
232 | 0 | if(!ok) { |
233 | 0 | ASSIMP_LOG_WARN(AddLineNumber("expected token \'(\'",line)); |
234 | 0 | continue; |
235 | 0 | } |
236 | 0 | } |
237 | | |
238 | 0 | std::string::size_type n2 = s.find_last_of(')'); |
239 | 0 | if (n2 == std::string::npos || n2 < n1 || n2 == s.length() - 1 || s[n2 + 1] != ';') { |
240 | 0 | has_next = true; |
241 | 0 | bool ok = false; |
242 | 0 | for( ++splitter; splitter; ++splitter) { |
243 | 0 | const std::string& snext = *splitter; |
244 | 0 | if (snext.empty()) { |
245 | 0 | continue; |
246 | 0 | } |
247 | | |
248 | | // the next line doesn't start an entity, so maybe it is |
249 | | // just a continuation for this line, keep going |
250 | 0 | if (!IsEntityDef(snext)) { |
251 | 0 | s.append(snext); |
252 | 0 | n2 = s.find_last_of(')'); |
253 | 0 | ok = !(n2 == std::string::npos || n2 < n1 || n2 == s.length() - 1 || s[n2 + 1] != ';'); |
254 | 0 | } else { |
255 | 0 | break; |
256 | 0 | } |
257 | 0 | } |
258 | 0 | if(!ok) { |
259 | 0 | ASSIMP_LOG_WARN(AddLineNumber("expected token \')\'",line)); |
260 | 0 | continue; |
261 | 0 | } |
262 | 0 | } |
263 | | |
264 | 0 | if (map.find(id) != map.end()) { |
265 | 0 | ASSIMP_LOG_WARN(AddLineNumber((Formatter::format(),"an object with the id #",id," already exists"),line)); |
266 | 0 | } |
267 | |
|
268 | 0 | std::string::size_type ns = n0; |
269 | 0 | do { |
270 | 0 | ++ns; |
271 | 0 | } while (IsSpace(s.at(ns))); |
272 | 0 | std::string::size_type ne = n1; |
273 | 0 | do { |
274 | 0 | --ne; |
275 | 0 | } while (IsSpace(s.at(ne))); |
276 | 0 | std::string type = s.substr(ns, ne - ns + 1); |
277 | 0 | type = ai_tolower(type); |
278 | 0 | const char* sz = scheme.GetStaticStringForToken(type); |
279 | 0 | if(sz) { |
280 | 0 | const std::string::size_type szLen = n2-n1+1; |
281 | 0 | char* const copysz = new char[szLen+1]; |
282 | 0 | std::copy(s.c_str()+n1,s.c_str()+n2+1,copysz); |
283 | 0 | copysz[szLen] = '\0'; |
284 | 0 | db.InternInsert(new LazyObject(db,id,line,sz,copysz)); |
285 | 0 | } |
286 | 0 | if(!has_next) { |
287 | 0 | ++splitter; |
288 | 0 | } |
289 | 0 | } |
290 | |
|
291 | 0 | if (!splitter) { |
292 | 0 | ASSIMP_LOG_WARN("STEP: ignoring unexpected EOF"); |
293 | 0 | } |
294 | |
|
295 | 0 | if ( !DefaultLogger::isNullLogger()){ |
296 | 0 | ASSIMP_LOG_DEBUG("STEP: got ",map.size()," object records with ", |
297 | 0 | db.GetRefs().size()," inverse index entries"); |
298 | 0 | } |
299 | 0 | } |
300 | | |
301 | | // ------------------------------------------------------------------------------------------------ |
302 | | std::shared_ptr<const EXPRESS::DataType> EXPRESS::DataType::Parse(const char*& inout, const char *end, uint64_t line, const EXPRESS::ConversionSchema* schema /*= nullptr*/) |
303 | 212 | { |
304 | 212 | const char* cur = inout; |
305 | 212 | SkipSpaces(&cur, end); |
306 | 212 | if (*cur == ',' || IsSpaceOrNewLine(*cur)) { |
307 | 0 | throw STEP::SyntaxError("unexpected token, expected parameter",line); |
308 | 0 | } |
309 | | |
310 | | // just skip over constructions such as IFCPLANEANGLEMEASURE(0.01) and read only the value |
311 | 212 | if (schema) { |
312 | 0 | bool ok = false; |
313 | 0 | for(const char* t = cur; *t && *t != ')' && *t != ','; ++t) { |
314 | 0 | if (*t=='(') { |
315 | 0 | if (!ok) { |
316 | 0 | break; |
317 | 0 | } |
318 | 0 | for(--t;IsSpace(*t);--t); |
319 | 0 | std::string s(cur,static_cast<size_t>(t-cur+1)); |
320 | 0 | std::transform(s.begin(),s.end(),s.begin(),&ai_tolower<char> ); |
321 | 0 | if (schema->IsKnownToken(s)) { |
322 | 0 | for(cur = t+1;*cur++ != '(';); |
323 | 0 | std::shared_ptr<const EXPRESS::DataType> dt = Parse(cur, end); |
324 | 0 | inout = *cur ? cur+1 : cur; |
325 | 0 | return dt; |
326 | 0 | } |
327 | 0 | break; |
328 | 0 | } |
329 | 0 | else if (!IsSpace(*t)) { |
330 | 0 | ok = true; |
331 | 0 | } |
332 | 0 | } |
333 | 0 | } |
334 | | |
335 | 212 | if (*cur == '*' ) { |
336 | 0 | inout = cur+1; |
337 | 0 | return std::make_shared<EXPRESS::ISDERIVED>(); |
338 | 0 | } |
339 | 212 | else if (*cur == '$' ) { |
340 | 0 | inout = cur+1; |
341 | 0 | return std::make_shared<EXPRESS::UNSET>(); |
342 | 0 | } |
343 | 212 | else if (*cur == '(' ) { |
344 | | // start of an aggregate, further parsing is done by the LIST factory constructor |
345 | 0 | inout = cur; |
346 | 0 | return EXPRESS::LIST::Parse(inout, end, line, schema); |
347 | 0 | } |
348 | 212 | else if (*cur == '.' ) { |
349 | | // enum (includes boolean) |
350 | 0 | const char* start = ++cur; |
351 | 0 | for(;*cur != '.';++cur) { |
352 | 0 | if (*cur == '\0') { |
353 | 0 | throw STEP::SyntaxError("enum not closed",line); |
354 | 0 | } |
355 | 0 | } |
356 | 0 | inout = cur+1; |
357 | 0 | return std::make_shared<EXPRESS::ENUMERATION>(std::string(start, static_cast<size_t>(cur-start) )); |
358 | 0 | } |
359 | 212 | else if (*cur == '#' ) { |
360 | | // object reference |
361 | 0 | return std::make_shared<EXPRESS::ENTITY>(strtoul10_64(++cur,&inout)); |
362 | 0 | } |
363 | 212 | else if (*cur == '\'' ) { |
364 | | // string literal |
365 | 212 | const char* start = ++cur; |
366 | | |
367 | 112k | for(;*cur != '\'';++cur) { |
368 | 112k | if (*cur == '\0') { |
369 | 0 | throw STEP::SyntaxError("string literal not closed",line); |
370 | 0 | } |
371 | 112k | } |
372 | | |
373 | 212 | if (cur[1] == '\'') { |
374 | | // Vesanen: more than 2 escaped ' in one literal! |
375 | 0 | do { |
376 | 0 | for(cur += 2;*cur != '\'';++cur) { |
377 | 0 | if (*cur == '\0') { |
378 | 0 | throw STEP::SyntaxError("string literal not closed",line); |
379 | 0 | } |
380 | 0 | } |
381 | 0 | } |
382 | 0 | while(cur[1] == '\''); |
383 | 0 | } |
384 | | |
385 | 212 | inout = cur + 1; |
386 | | |
387 | | // assimp is supposed to output UTF8 strings, so we have to deal |
388 | | // with foreign encodings. |
389 | 212 | std::string stemp = std::string(start, static_cast<size_t>(cur - start)); |
390 | 212 | if(!StringToUTF8(stemp)) { |
391 | | // TODO: route this to a correct logger with line numbers etc., better error messages |
392 | 3 | ASSIMP_LOG_ERROR("an error occurred reading escape sequences in ASCII text"); |
393 | 3 | } |
394 | | |
395 | 212 | return std::make_shared<EXPRESS::STRING>(stemp); |
396 | 212 | } |
397 | 0 | else if (*cur == '\"' ) { |
398 | 0 | throw STEP::SyntaxError("binary data not supported yet",line); |
399 | 0 | } |
400 | | |
401 | | // else -- must be a number. if there is a decimal dot in it, |
402 | | // parse it as real value, otherwise as integer. |
403 | 0 | const char* start = cur; |
404 | 0 | for(;*cur && *cur != ',' && *cur != ')' && !IsSpace(*cur);++cur) { |
405 | 0 | if (*cur == '.') { |
406 | 0 | double f; |
407 | 0 | inout = fast_atoreal_move(start,f); |
408 | 0 | return std::make_shared<EXPRESS::REAL>(f); |
409 | 0 | } |
410 | 0 | } |
411 | | |
412 | 0 | bool neg = false; |
413 | 0 | if (*start == '-') { |
414 | 0 | neg = true; |
415 | 0 | ++start; |
416 | 0 | } |
417 | 0 | else if (*start == '+') { |
418 | 0 | ++start; |
419 | 0 | } |
420 | 0 | int64_t num = static_cast<int64_t>( strtoul10_64(start,&inout) ); |
421 | 0 | return std::make_shared<EXPRESS::INTEGER>(neg?-num:num); |
422 | 0 | } |
423 | | |
424 | | // ------------------------------------------------------------------------------------------------ |
425 | | std::shared_ptr<const EXPRESS::LIST> EXPRESS::LIST::Parse(const char*& inout, const char *end, |
426 | 0 | uint64_t line, const EXPRESS::ConversionSchema* schema) { |
427 | 0 | const std::shared_ptr<EXPRESS::LIST> list = std::make_shared<EXPRESS::LIST>(); |
428 | 0 | EXPRESS::LIST::MemberList& cur_members = list->members; |
429 | |
|
430 | 0 | const char* cur = inout; |
431 | 0 | if (*cur++ != '(') { |
432 | 0 | throw STEP::SyntaxError("unexpected token, expected \'(\' token at beginning of list",line); |
433 | 0 | } |
434 | | |
435 | | // estimate the number of items upfront - lists can grow large |
436 | 0 | size_t count = 1; |
437 | 0 | for(const char* c=cur; *c && *c != ')'; ++c) { |
438 | 0 | count += (*c == ',' ? 1 : 0); |
439 | 0 | } |
440 | |
|
441 | 0 | cur_members.reserve(count); |
442 | |
|
443 | 0 | for(;;++cur) { |
444 | 0 | if (!*cur) { |
445 | 0 | throw STEP::SyntaxError("unexpected end of line while reading list"); |
446 | 0 | } |
447 | 0 | SkipSpaces(cur,&cur, end); |
448 | 0 | if (*cur == ')') { |
449 | 0 | break; |
450 | 0 | } |
451 | | |
452 | 0 | cur_members.push_back(EXPRESS::DataType::Parse(cur, end, line, schema)); |
453 | 0 | SkipSpaces(cur, &cur, end); |
454 | |
|
455 | 0 | if (*cur != ',') { |
456 | 0 | if (*cur == ')') { |
457 | 0 | break; |
458 | 0 | } |
459 | 0 | throw STEP::SyntaxError("unexpected token, expected \',\' or \')\' token after list element",line); |
460 | 0 | } |
461 | 0 | } |
462 | | |
463 | 0 | inout = cur + 1; |
464 | 0 | return list; |
465 | 0 | } |
466 | | |
467 | | // ------------------------------------------------------------------------------------------------ |
468 | 0 | static void handleSkippedDepthFromToken(const char *a, int64_t &skip_depth ) { |
469 | 0 | if (*a == '(') { |
470 | 0 | ++skip_depth; |
471 | 0 | } else if (*a == ')') { |
472 | 0 | --skip_depth; |
473 | 0 | } |
474 | 0 | } |
475 | | |
476 | | // ------------------------------------------------------------------------------------------------ |
477 | 0 | static int64_t getIdFromToken(const char *a) { |
478 | 0 | const char *tmp; |
479 | 0 | const int64_t num = static_cast<int64_t>(strtoul10_64(a + 1, &tmp)); |
480 | |
|
481 | 0 | return num; |
482 | 0 | } |
483 | | |
484 | | // ------------------------------------------------------------------------------------------------ |
485 | | STEP::LazyObject::LazyObject(DB& db, uint64_t id,uint64_t /*line*/, const char* const type,const char* args) |
486 | 0 | : id(id) |
487 | 0 | , type(type) |
488 | 0 | , db(db) |
489 | 0 | , args(args) |
490 | 0 | , obj() { |
491 | | // find any external references and store them in the database. |
492 | | // this helps us emulate STEPs INVERSE fields. |
493 | 0 | if (!db.KeepInverseIndicesForType(type)) { |
494 | 0 | return; |
495 | 0 | } |
496 | | |
497 | | // do a quick scan through the argument tuple and watch out for entity references |
498 | 0 | const char *a( args ); |
499 | 0 | int64_t skip_depth( 0 ); |
500 | 0 | while ( *a ) { |
501 | 0 | handleSkippedDepthFromToken(a, skip_depth); |
502 | |
|
503 | 0 | if (skip_depth >= 1 && *a=='#') { |
504 | 0 | if (*(a + 1) != '#') { |
505 | 0 | db.MarkRef(getIdFromToken(a), id); |
506 | 0 | } else { |
507 | 0 | ++a; |
508 | 0 | } |
509 | 0 | } |
510 | 0 | ++a; |
511 | 0 | } |
512 | 0 | } |
513 | | |
514 | | // ------------------------------------------------------------------------------------------------ |
515 | 0 | STEP::LazyObject::~LazyObject() { |
516 | | // make sure the right dtor/operator delete get called |
517 | 0 | if (obj) { |
518 | 0 | delete obj; |
519 | 0 | } else { |
520 | 0 | delete[] args; |
521 | 0 | } |
522 | 0 | } |
523 | | |
524 | | // ------------------------------------------------------------------------------------------------ |
525 | 0 | void STEP::LazyObject::LazyInit() const { |
526 | 0 | const EXPRESS::ConversionSchema& schema = db.GetSchema(); |
527 | 0 | STEP::ConvertObjectProc proc = schema.GetConverterProc(type); |
528 | |
|
529 | 0 | if (!proc) { |
530 | 0 | throw STEP::TypeError("unknown object type: " + std::string(type),id); |
531 | 0 | } |
532 | | |
533 | 0 | const char* acopy = args; |
534 | 0 | const char *end = acopy + std::strlen(args); |
535 | 0 | std::shared_ptr<const EXPRESS::LIST> conv_args = EXPRESS::LIST::Parse(acopy, end, (uint64_t)STEP::SyntaxError::LINE_NOT_SPECIFIED,&db.GetSchema()); |
536 | 0 | delete[] args; |
537 | 0 | args = nullptr; |
538 | | |
539 | | // if the converter fails, it should throw an exception, but it should never return nullptr |
540 | 0 | try { |
541 | 0 | obj = proc(db,*conv_args); |
542 | 0 | } |
543 | 0 | catch(const TypeError& t) { |
544 | | // augment line and entity information |
545 | 0 | throw TypeError(t.what(),id); |
546 | 0 | } |
547 | 0 | ++db.evaluated_count; |
548 | 0 | ai_assert(obj); |
549 | | |
550 | | // store the original id in the object instance |
551 | 0 | obj->SetID(id); |
552 | 0 | } |