1 #define SC_CORE_EXPORT
2 #define SC_DAI_EXPORT
3
4 #include <algorithm>
5 #include <set>
6 #include <string>
7 #include <assert.h>
8
9 #include "Registry.h"
10 #include "sc_strtoull.h"
11 #include "sdaiApplication_instance.h"
12 #include "read_func.h"
13 #include "SdaiSchemaInit.h"
14 #include "STEPcomplex.h"
15
16 #include "sectionReader.h"
17 #include "lazyFileReader.h"
18 #include "lazyInstMgr.h"
19 #include "lazyTypes.h"
20 #include "instMgrHelper.h"
21
22 #include "current_function.hpp"
23
sectionReader(lazyFileReader * parent,std::ifstream & file,std::streampos start,sectionID sid)24 sectionReader::sectionReader( lazyFileReader * parent, std::ifstream & file, std::streampos start, sectionID sid ):
25 _lazyFile( parent ), _file( file ), _sectionStart( start ), _sectionID( sid ) {
26 _fileID = _lazyFile->ID();
27 _error = new ErrorDescriptor();
28 }
29
30
findNormalString(const std::string & str,bool semicolon)31 std::streampos sectionReader::findNormalString( const std::string & str, bool semicolon ) {
32 std::streampos found = -1, startPos = _file.tellg(), nextTry = startPos;
33 int i = 0, l = str.length();
34 char c;
35
36 //i is reset every time a character doesn't match; if i == l, this means that we've found the entire string
37 while( i < l || semicolon ) {
38 skipWS();
39 c = _file.get();
40 if( ( i == l ) && ( semicolon ) ) {
41 if( c == ';' ) {
42 break;
43 } else {
44 i = 0;
45 _file.seekg( nextTry );
46 continue;
47 }
48 }
49 if( c == '\'' ) {
50 //push past string
51 _file.unget();
52 GetLiteralStr( _file, _lazyFile->getInstMgr()->getErrorDesc() );
53 }
54 if( ( c == '/' ) && ( _file.peek() == '*' ) ) {
55 //push past comment
56 findNormalString( "*/" );
57 }
58 if( str[i] == c ) {
59 i++;
60 if( i == 1 ) {
61 nextTry = _file.tellg();
62 }
63 } else {
64 if( !_file.good() ) {
65 break;
66 }
67 if( i >= 1 ) {
68 _file.seekg( nextTry );
69 }
70 i = 0;
71 }
72 }
73 if( i == l ) {
74 found = _file.tellg();
75 }
76 if( _file.is_open() && _file.good() ) {
77 return found;
78 } else {
79 return -1;
80 }
81 }
82
83
84 //NOTE different behavior than const char * GetKeyword( istream & in, const char * delims, ErrorDescriptor & err ) in read_func.cc
85 // returns pointer to the contents of a static std::string
getDelimitedKeyword(const char * delimiters)86 const char * sectionReader::getDelimitedKeyword( const char * delimiters ) {
87 static std::string str;
88 char c;
89 str.clear();
90 str.reserve( 100 );
91 skipWS();
92 while( c = _file.get(), _file.good() ) {
93 if( c == '-' || c == '_' || isupper( c ) || isdigit( c ) ||
94 ( c == '!' && str.length() == 0 ) ) {
95 str.append( 1, c );
96 } else if( ( c == '/' ) && ( _file.peek() == '*' ) && ( str.length() == 0 ) ) {
97 //push past comment
98 findNormalString( "*/" );
99 skipWS();
100 continue;
101 } else {
102 _file.putback( c );
103 break;
104 }
105 }
106 c = _file.peek();
107 if( !strchr( delimiters, c ) ) {
108 std::cerr << SC_CURRENT_FUNCTION << ": missing delimiter. Found " << c << ", expected one of " << delimiters << " at end of keyword " << str << ". File offset: " << _file.tellg() << std::endl;
109 abort();
110 }
111 return str.c_str();
112 }
113
114 /// search forward in the file for the end of the instance. Start position should
115 /// be the opening parenthesis; otherwise, it is likely to fail.
116 ///NOTE *must* check return value!
seekInstanceEnd(instanceRefs ** refs)117 std::streampos sectionReader::seekInstanceEnd( instanceRefs ** refs ) {
118 char c;
119 int parenDepth = 0;
120 while( c = _file.get(), _file.good() ) {
121 switch( c ) {
122 case '(':
123 parenDepth++;
124 break;
125 case '/':
126 if( _file.peek() == '*' ) {
127 findNormalString( "*/" );
128 } else {
129 return -1;
130 }
131 break;
132 case '\'':
133 _file.unget();
134 GetLiteralStr( _file, _lazyFile->getInstMgr()->getErrorDesc() );
135 break;
136 case '=':
137 return -1;
138 case '#':
139 skipWS();
140 if( isdigit( _file.peek() ) ) {
141 if( refs != 0 ) {
142 if( ! * refs ) {
143 *refs = new std::vector< instanceID >;
144 }
145 instanceID n;
146 _file >> n;
147 ( * refs )->push_back( n );
148 }
149 } else {
150 return -1;
151 }
152 break;
153 case ')':
154 if( --parenDepth == 0 ) {
155 skipWS();
156 if( _file.get() == ';' ) {
157 return _file.tellg();
158 } else {
159 _file.unget();
160 }
161 }
162 default:
163 break;
164 }
165 }
166 return -1;
167 //NOTE - old way: return findNormalString( ")", true );
168 // old memory consumption: 673728kb; User CPU time: 35480ms; System CPU time: 17710ms (with 266MB catia-ferrari-sharknose.stp)
169 // new memory: 673340kb; User CPU time: 29890ms; System CPU time: 11650ms
170 }
171
locateAllInstances()172 void sectionReader::locateAllInstances() {
173 namedLazyInstance inst;
174 while( inst = nextInstance(), ( _file.good() ) && ( inst.loc.begin > 0 ) ) {
175 _lazyFile->getInstMgr()->addLazyInstance( inst );
176 }
177 }
178
readInstanceNumber()179 instanceID sectionReader::readInstanceNumber() {
180 char c;
181 size_t digits = 0;
182 instanceID id = 0;
183
184 //find instance number ("# nnnn ="), where ' ' is any whitespace found by isspace()
185 skipWS();
186 c = _file.get();
187 if( ( c == '/' ) && ( _file.peek() == '*' ) ) {
188 findNormalString( "*/" );
189 } else {
190 _file.unget();
191 }
192 skipWS();
193 c = _file.get();
194 if( c != '#' ) {
195 return 0;
196 }
197 skipWS();
198
199 // The largest instance ID yet supported is the maximum value of unsigned long long int
200 assert( std::numeric_limits<instanceID>::max() <= std::numeric_limits<unsigned long long int>::max() );
201
202 size_t instanceIDLength = std::numeric_limits<instanceID>::digits10 + 1;
203 char * buffer = new char( instanceIDLength + 1 ); // +1 for the terminating character
204
205 std::stringstream errorMsg;
206
207 do {
208 c = _file.get();
209 if( isdigit( c ) ) {
210 buffer[ digits ] = c; //copy the charcter into the buffer
211 digits++;
212
213 } else {
214 _file.unget();
215 break;
216 }
217
218 if( digits > instanceIDLength ) {
219 errorMsg << "A very large instance ID of string length greater then " << instanceIDLength << " found. Skipping data section " << _sectionID << ".";
220
221 _error->GreaterSeverity( SEVERITY_INPUT_ERROR );
222 _error->UserMsg( "A very large instance ID encountered" );
223 _error->DetailMsg( errorMsg.str() );
224
225 delete buffer;
226 return 0;
227 }
228
229 } while( _file.good() );
230 buffer[ digits ] = '\0'; //Append the terminating character
231 skipWS();
232
233 if( _file.good() && ( digits > 0 ) && ( _file.get() == '=' ) ) {
234 id = strtoull( buffer, NULL, 10);
235 if( id == std::numeric_limits<instanceID>::max() ) {
236 //Handling those cases where although the number of digits is equal, but the id value is greater then equal to the maximum allowed value.
237 errorMsg << "A very large instance ID caused an overflow. Skipping data section " << _sectionID << ".";
238
239 _error->GreaterSeverity( SEVERITY_INPUT_ERROR );
240 _error->UserMsg( "A very large instance ID encountered" );
241 _error->DetailMsg( errorMsg.str() );
242 }
243
244 assert( id > 0 );
245 }
246 delete buffer;
247 return id;
248 }
249
250 /** load an instance and return a pointer to it.
251 * side effect: recursively loads any instances the specified instance depends upon
252 */
getRealInstance(const Registry * reg,long int begin,instanceID instance,const std::string & typeName,const std::string & schName,bool header)253 SDAI_Application_instance * sectionReader::getRealInstance( const Registry * reg, long int begin, instanceID instance,
254 const std::string & typeName, const std::string & schName, bool header ) {
255 char c;
256 const char * tName = 0, * sName = 0; //these are necessary since typeName and schName are const
257 std::string comment;
258 Severity sev = SEVERITY_NULL;
259 SDAI_Application_instance * inst = 0;
260
261 tName = typeName.c_str();
262 if( schName.size() > 0 ) {
263 sName = schName.c_str();
264 } else if( !header ) {
265 SdaiFile_schema * fs = dynamic_cast< SdaiFile_schema * >( _lazyFile->getHeaderInstances()->find( 3 ) );
266 if( fs ) {
267 StringNode * sn = ( StringNode * ) fs->schema_identifiers_()->GetHead();
268 if( sn ) {
269 sName = sn->value.c_str();
270 if( sn->NextNode() ) {
271 std::cerr << "Warning - multiple schema names found. Only searching with first one." << std::endl;
272 }
273 }
274 } else {
275 std::cerr << "Warning - no schema names found; the file is probably invalid. Looking for typeName in any loaded schema." << std::endl;
276 }
277 }
278
279 _file.seekg( begin );
280 skipWS();
281 ReadTokenSeparator( _file, &comment );
282 if( !header ) {
283 findNormalString( "=" );
284 }
285 skipWS();
286 ReadTokenSeparator( _file, &comment );
287 c = _file.peek();
288 switch( c ) {
289 case '&':
290 std::cerr << "Can't handle scope instances. Skipping #" << instance << ", offset " << _file.tellg() << std::endl;
291 // sev = CreateScopeInstances( in, &scopelist );
292 break;
293 case '(':
294 inst = CreateSubSuperInstance( reg, instance, sev );
295 break;
296 case '!':
297 std::cerr << "Can't handle user-defined instances. Skipping #" << instance << ", offset " << _file.tellg() << std::endl;
298 break;
299 default:
300 if( ( !header ) && ( typeName.size() == 0 ) ) {
301 tName = getDelimitedKeyword( ";( /\\" );
302 }
303 inst = reg->ObjCreate( tName, sName );
304 break;
305 }
306
307 if( inst != & NilSTEPentity ) {
308 if( !comment.empty() ) {
309 inst->AddP21Comment( comment );
310 }
311 assert( inst->getEDesc() );
312 _file.seekg( begin );
313 findNormalString( "(" );
314 _file.unget();
315 sev = inst->STEPread( instance, 0, _lazyFile->getInstMgr()->getAdapter(), _file, sName, true, false );
316 //TODO do something with 'sev'
317 inst->InitIAttrs();
318 }
319 return inst;
320 }
321
CreateSubSuperInstance(const Registry * reg,instanceID fileid,Severity & sev)322 STEPcomplex * sectionReader::CreateSubSuperInstance( const Registry * reg, instanceID fileid, Severity & sev ) {
323 std::string buf;
324 ErrorDescriptor err;
325 std::vector<std::string *> typeNames;
326 _file.get(); //move past the first '('
327 skipWS();
328 while( _file.good() && ( _file.peek() != ')' ) ) {
329 typeNames.push_back( new std::string( getDelimitedKeyword( ";( /\\\n" ) ) );
330 if( typeNames.back()->empty() ) {
331 delete typeNames.back();
332 typeNames.pop_back();
333 } else {
334 SkipSimpleRecord( _file, buf, &err ); //exactly what does this do? if it doesn't count parenthesis, it probably should
335 buf.clear();
336 }
337 skipWS();
338 if( _file.peek() != ')' ) {
339 // do something
340 }
341 }
342 // STEPComplex needs an array of strings or of char*. construct the latter using c_str() on all strings in the vector
343 //FIXME: STEPComplex ctor should accept std::vector<std::string *> ?
344 const int s = typeNames.size();
345 const char ** names = new const char * [ s + 1 ];
346 names[ s ] = 0;
347 for( int i = 0; i < s; i++ ) {
348 names[ i ] = typeNames[i]->c_str();
349 }
350 //TODO still need the schema name
351 STEPcomplex * sc = new STEPcomplex( ( const_cast<Registry *>( reg ) ), names, ( int ) fileid /*, schnm*/ );
352 delete[] names;
353 //TODO also delete contents of typeNames!
354 return sc;
355 }
356
357