1 /** \file STEPfile.cc
2 * NIST STEP Core Class Library
3 * cleditor/STEPfile.cc
4 * February, 1994
5 * Peter Carr
6 * K. C. Morris
7 * David Sauder
8
9 * Development of this software was funded by the United States Government,
10 * and is not subject to copyright.
11
12 TODO LIST:
13 - ReadHeader doesn't merge the information from more than one instance
14 of the same entity type name. i.e. it doesn't merge info from multiple files
15 - ReadWorkingFile does not handle references to entities which
16 aren't in the working session file. for other *incomplete* instances,
17 it prints error messages to cerr which may not need to be printed. It
18 doesn't keep track of the incomplete instances, it just puts them on the
19 list according to the symbol which precedes them in the working session
20 file
21 */
22 #include <iostream>
23 #include <iterator>
24 #include <algorithm>
25 #include <vector>
26
27 #include <STEPfile.h>
28 #include <sdai.h>
29 #include <STEPcomplex.h>
30 #include <STEPattribute.h>
31 #include <SdaiHeaderSchema.h>
32
33 // STEPundefined contains
34 // void PushPastString (istream& in, std::string &s, ErrorDescriptor *err)
35 #include <STEPundefined.h>
36
37 #include "sc_memmgr.h"
38
39 /**
40 * \returns The new file name for the class.
41 * \param newName The file name to be set.
42 *
43 * This function sets the _fileName member variable to an
44 * absolute path name to a directory on the file system. If newName
45 * cannot be resolved to a file, then an empty string is returned.
46 *
47 * side effects: STEPfile::_fileName value may change.
48 */
SetFileName(const std::string newName)49 std::string STEPfile::SetFileName( const std::string newName ) {
50 // if a newName is not given or is the same as the old, use the old name
51 if( ( newName.empty() ) || ( newName == _fileName ) ) {
52 return FileName();
53 }
54
55 _fileName = DirObj::Normalize( newName );
56 return _fileName;
57 }
58
59 /** Returns read progress,scaled 0-100. Will return a value < 0 if
60 * called *immediately* after read operation starts, if no file has
61 * been specified, or if file has been closed. If result < 50,
62 * ReadData1() hasn't completed yet.
63 *
64 * This function is useless unless it is called from another thread.
65 */
GetReadProgress() const66 float STEPfile::GetReadProgress() const {
67 if( _iFileSize < 1 ) {
68 return -1;
69 }
70 //the file is read once by ReadData1(), and again by ReadData2. Each gets 50%.
71 float percent = ( static_cast<float>( _iFileCurrentPosition ) / _iFileSize ) * 50.0;
72 if( _iFileStage1Done ) {
73 percent += 50;
74 }
75 return percent;
76 }
77
78 /** Returns write progress,scaled 0-100. Will return a value < 0 if
79 * called *immediately* after write operation starts, if no file has
80 * been specified, or if file has been closed.
81 * \sa GetReadProgress()
82 *
83 * Caveat: Only works for files written via WriteExchangeFile() / WriteData().
84 *
85 * This function is useless unless it is called from another thread.
86 */
GetWriteProgress() const87 float STEPfile::GetWriteProgress() const {
88 int total = _instances.InstanceCount();
89 if( total > 0 ) {
90 return ( static_cast<float>( _oFileInstsWritten ) / total ) * 100.0;
91 } else {
92 return -1;
93 }
94 }
95
96 /**
97 * \param in The input stream from which the file is read.
98 *
99 * This function reads in the header section of an exchange file. It
100 * parses the header section, popluates the _headerInstances, and
101 * returns an error descriptor.
102 * It expects to find the "HEADER;" symbol at the beginning of the istream.
103 *
104 * side effects: The function gobbles all characters up to and including the
105 * next "ENDSEC;" from in.
106 * The STEPfile::_headerInstances may change.
107 */
ReadHeader(istream & in)108 Severity STEPfile::ReadHeader( istream & in ) {
109 std::string cmtStr;
110
111 InstMgr * im = new InstMgr;
112 SDAI_Application_instance * obj;
113 Severity objsev = SEVERITY_NULL;
114
115 int endsec = 0;
116 int userDefined = 0;
117 int fileid;
118 std::string keywd;
119 char c = '\0';
120 char buf [BUFSIZ];
121
122 std::string strbuf;
123
124 ReadTokenSeparator( in );
125
126 // Read and gobble all 'junk' up to "HEADER;"
127 if( !FindHeaderSection( in ) ) {
128 delete im;
129 return SEVERITY_INPUT_ERROR;
130 }
131
132 //read the header instances
133 while( !endsec ) {
134 ReadTokenSeparator( in, &cmtStr );
135 if( in.eof() ) {
136 _error.AppendToDetailMsg( "End of file reached in reading header section.\n" );
137 _error.GreaterSeverity( SEVERITY_EXIT );
138 delete im;
139 return SEVERITY_EXIT;
140 }
141
142 //check for user defined instances
143 //if it is userDefined, the '!' does not get put back on the istream
144 in.get( c );
145 if( c == '!' ) {
146 userDefined = 1;
147 } else {
148 in.putback( c );
149 }
150
151 //get the entity keyword
152 keywd = GetKeyword( in, ";( /\\", _error );
153 ReadTokenSeparator( in, &cmtStr );
154
155 //check for "ENDSEC"
156 if( !strncmp( const_cast<char *>( keywd.c_str() ), "ENDSEC", 7 ) ) {
157 //get the token delimiter
158 in.get( c ); //should be ';'
159 endsec = 1;
160 break; //from while-loop
161 } else {
162 //create and read the header instance
163 // SDAI_Application_instance::STEPread now reads the opening parenthesis
164
165 //create header instance
166 buf[0] = '\0';
167 if( _fileType == VERSION_OLD ) {
168 _error.AppendToDetailMsg( "N279 header detected. Files this old are no longer supported.\n" );
169 _error.GreaterSeverity( SEVERITY_EXIT );
170 delete im;
171 return SEVERITY_EXIT;
172 } else {
173 strncpy( buf, const_cast<char *>( keywd.c_str() ), BUFSIZ );
174 }
175
176 if( userDefined ) {
177 //create user defined header instance
178 // BUG: user defined entities are ignored
179 //obj = _headerUserDefined->ObjCreate (buf);
180 //objsev = AppendEntityErrorMsg( &(obj->Error()) );
181
182 SkipInstance( in, strbuf );
183 cerr << "User defined entity in header section " <<
184 "is ignored.\n\tdata lost: !" << buf << strbuf << "\n";
185 _error.GreaterSeverity( SEVERITY_WARNING );
186 break; //from while loop
187 } else { //not userDefined
188 obj = _headerRegistry->ObjCreate( buf );
189 }
190
191 //read header instance
192 if( !obj || ( obj == ENTITY_NULL ) ) {
193 ++_errorCount;
194 SkipInstance( in, strbuf );
195 cerr << "Unable to create header section entity: \'" <<
196 keywd << "\'.\n\tdata lost: " << strbuf << "\n";
197 _error.GreaterSeverity( SEVERITY_WARNING );
198 } else { //not ENTITY_NULL
199 //read the header instance
200
201 //check obj's Error Descriptor
202 objsev = AppendEntityErrorMsg( &( obj->Error() ) );
203
204 //set file_id to reflect the appropriate Header Section Entity
205 fileid = HeaderId( const_cast<char *>( keywd.c_str() ) );
206
207 //read the values from the istream
208 objsev = obj->STEPread( fileid, 0, ( InstMgr * )0, in, NULL, true, _strict );
209 if( !cmtStr.empty() ) {
210 obj->PrependP21Comment( cmtStr );
211 }
212
213 in >> ws;
214 c = in.peek(); // check for semicolon or keyword 'ENDSEC'
215 if( c != 'E' ) {
216 in >> c; // read the semicolon
217 }
218
219 //check to see if object was successfully read
220 AppendEntityErrorMsg( &( obj->Error() ) );
221 //append to header instance manager
222 im->Append( obj, completeSE );
223 }
224 }
225 cmtStr.clear();
226 }
227
228 HeaderVerifyInstances( im );
229 HeaderMergeInstances( im ); // handles delete for im
230
231 return _error.severity();
232 }
233
234 /**
235 * Verify the instances read from the header section of an exchange file.
236 * If some required instances aren't present, then create them,
237 * and populate them with default values.
238 * The required instances are:
239 * #1 = FILE_DESCRIPTION
240 * #2 = FILE_NAME
241 * #3 = FILE_SCHEMA
242 */
HeaderVerifyInstances(InstMgr * im)243 Severity STEPfile::HeaderVerifyInstances( InstMgr * im ) {
244 int err = 0;
245 int fileid;
246 SDAI_Application_instance * obj;
247
248 //check File_Name
249 fileid = HeaderId( "File_Name" );
250 if( !( im->FindFileId( fileid ) ) ) {
251 ++err;
252 cerr << "FILE_NAME instance not found in header section\n";
253 // create a File_Name entity and assign default values
254 obj = HeaderDefaultFileName();
255 im->Append( obj, completeSE );
256 }
257
258 //check File_Description
259 fileid = HeaderId( "File_Description" );
260 if( !( im->FindFileId( fileid ) ) ) {
261 ++err;
262 cerr << "FILE_DESCRIPTION instance not found in header section\n";
263 // create a File_Description entity and assign default values
264 obj = HeaderDefaultFileDescription();
265 im->Append( obj, completeSE );
266 }
267
268 //check File_Schema
269 fileid = HeaderId( "File_Schema" );
270 if( !( im->FindFileId( fileid ) ) ) {
271 ++err;
272 cerr << "FILE_SCHEMA instance not found in header section\n";
273 // create a File_Schema entity and read in default values
274 obj = HeaderDefaultFileSchema();
275 im->Append( obj, completeSE );
276 }
277
278 if( !err ) {
279 return SEVERITY_NULL;
280 }
281 _error.AppendToUserMsg( "Missing required entity in header section.\n" );
282 _error.GreaterSeverity( SEVERITY_WARNING );
283 return SEVERITY_WARNING;
284 }
285
HeaderDefaultFileName()286 SDAI_Application_instance * STEPfile::HeaderDefaultFileName() {
287 SdaiFile_name * fn = new SdaiFile_name;
288 StringAggregate_ptr tmp = new StringAggregate;
289
290 fn->name_( "" );
291 fn->time_stamp_( "" );
292 tmp->StrToVal( "", &_error, fn->attributes[2].getADesc()->DomainType(), _headerInstances );
293 fn->author_( tmp );
294
295 tmp->StrToVal( "", &_error, fn->attributes[3].getADesc()->DomainType(), _headerInstances );
296 fn->organization_( tmp );
297
298 fn->preprocessor_version_( "" );
299 fn->originating_system_( "" );
300 fn->authorization_( "" );
301
302 fn->STEPfile_id = HeaderId( "File_Name" );
303
304 return fn;
305 }
306
HeaderDefaultFileDescription()307 SDAI_Application_instance * STEPfile::HeaderDefaultFileDescription() {
308 SdaiFile_description * fd = new SdaiFile_description;
309
310 fd->implementation_level_( "" );
311
312 fd->STEPfile_id = HeaderId( "File_Description" );
313
314 return fd;
315 }
316
HeaderDefaultFileSchema()317 SDAI_Application_instance * STEPfile::HeaderDefaultFileSchema() {
318 SdaiFile_schema * fs = new SdaiFile_schema;
319 StringAggregate_ptr tmp = new StringAggregate;
320
321 tmp->StrToVal( "", &_error, fs->attributes[0].getADesc()->DomainType(), _headerInstances );
322 fs->schema_identifiers_( tmp );
323
324 fs->STEPfile_id = HeaderId( "File_Schema" );
325
326 return fs;
327 }
328
329
330
331 /**
332 * This function has an effect when more than one file
333 * is being read into a working session.
334 *
335 * This function manages space allocation for the Instance
336 * Manager for the header instances. If the instances in im are
337 * copied onto the instances of _headerInstances, then im is
338 * deleted. Otherwise no space is deleted.
339 *
340 * Append the values of the given instance manager onto the _headerInstances.
341 * If the _headerInstances contain no instances, then copy the instances
342 * from im onto the _headerInstances.
343 * This only works for an instance manager which contains the following
344 * header section entites. The file id numbers are important.
345 *
346 * #1 = FILE_DESCRIPTION
347 * #2 = FILE_NAME
348 * #3 = FILE_SCHEMA
349 */
HeaderMergeInstances(InstMgr * im)350 void STEPfile::HeaderMergeInstances( InstMgr * im ) {
351 SDAI_Application_instance * se = 0;
352 SDAI_Application_instance * from = 0;
353
354 int idnum;
355
356 //check for _headerInstances
357 if( !_headerInstances ) {
358 _headerInstances = im;
359 return;
360 }
361
362 if( _headerInstances->InstanceCount() < 4 ) {
363 delete _headerInstances;
364 _headerInstances = im;
365 return;
366 }
367
368 //checking for _headerInstances::FILE_NAME
369 idnum = HeaderId( "File_Name" );
370 se = _headerInstances->GetApplication_instance( _headerInstances->FindFileId( idnum ) );
371 if( se ) {
372 from = im->GetApplication_instance( im->FindFileId( idnum ) );
373
374 // name:
375 // time_stamp: keep the newer time_stamp
376 // author: append the list of authors
377 // organization: append the organization list
378 // preprocessor_version:
379 // originating_system:
380 // authorization:
381 } else { // No current File_Name instance
382 from = im->GetApplication_instance( im->FindFileId( idnum ) );
383 _headerInstances->Append( from, completeSE );
384 }
385
386 //checking for _headerInstances::FILE_DESCRIPTION
387 idnum = HeaderId( "File_Description" );
388 se = _headerInstances->GetApplication_instance( _headerInstances->FindFileId( idnum ) );
389 if( se ) {
390 from = im->GetApplication_instance( im->FindFileId( idnum ) );
391
392 //description
393 //implementation_level
394 } else {
395 from = im->GetApplication_instance( im->FindFileId( idnum ) );
396 _headerInstances->Append( from, completeSE );
397 }
398
399 //checking for _headerInstances::FILE_SCHEMA
400 idnum = HeaderId( "File_Schema" );
401 se = _headerInstances->GetApplication_instance( _headerInstances->FindFileId( idnum ) );
402 if( se ) {
403 from = im->GetApplication_instance( im->FindFileId( idnum ) );
404
405 //description
406 //implementation_level
407 } else {
408 from = im->GetApplication_instance( im->FindFileId( idnum ) );
409 _headerInstances->Append( from, completeSE );
410 }
411
412 delete im;
413 return;
414 }
415
416
EntityWfState(char c)417 stateEnum STEPfile::EntityWfState( char c ) {
418 switch( c ) {
419 case wsSaveComplete:
420 return completeSE;
421 case wsSaveIncomplete:
422 return incompleteSE;
423 case wsDelete:
424 return deleteSE;
425 case wsNew:
426 return newSE;
427 default:
428 return noStateSE;
429 }
430 }
431
432 /**
433 * PASS 1: create instances
434 * starts at the data section
435 */
ReadData1(istream & in)436 int STEPfile::ReadData1( istream & in ) {
437 int endsec = 0;
438 _entsNotCreated = 0;
439
440 _errorCount = 0; // reset error count
441 _warningCount = 0; // reset error count
442
443 char c;
444 int instance_count = 0;
445 char buf[BUFSIZ];
446 buf[0] = '\0';
447 std::string tmpbuf;
448
449 SDAI_Application_instance * obj = ENTITY_NULL;
450 stateEnum inst_state = noStateSE; // used if reading working file
451
452 ErrorDescriptor e;
453
454 // PASS 1: create instances
455 endsec = FoundEndSecKywd( in );
456 while( in.good() && !endsec ) {
457 e.ClearErrorMsg();
458 ReadTokenSeparator( in ); // also skips white space
459 in >> c;
460
461 if( _fileType == WORKING_SESSION ) {
462 if( strchr( "CIND", c ) ) { // if there is a valid char
463 inst_state = EntityWfState( c );
464 ReadTokenSeparator( in );
465 in >> c; // read the ENTITY_NAME_DELIM
466 } else {
467 e.AppendToDetailMsg( "Invalid editing state character: " );
468 e.AppendToDetailMsg( c );
469 e.AppendToDetailMsg( "\nAssigning editing state to be INCOMPLETE\n" );
470 e.GreaterSeverity( SEVERITY_WARNING );
471 inst_state = incompleteSE;
472 }
473 }
474
475 if( c != ENTITY_NAME_DELIM ) {
476 in.putback( c );
477 while( c != ENTITY_NAME_DELIM && in.good() &&
478 !( endsec = FoundEndSecKywd( in ) ) ) {
479 tmpbuf.clear();
480 FindStartOfInstance( in, tmpbuf );
481 cout << "ERROR: trying to recover from invalid data. skipping: "
482 << tmpbuf << endl;
483 in >> c;
484 ReadTokenSeparator( in );
485 }
486 }
487
488 if( !endsec ) {
489 obj = ENTITY_NULL;
490 if( ( _fileType == WORKING_SESSION ) && ( inst_state == deleteSE ) ) {
491 SkipInstance( in, tmpbuf );
492 } else {
493 obj = CreateInstance( in, cout );
494 _iFileCurrentPosition = in.tellg();
495 }
496
497 if( obj != ENTITY_NULL ) {
498 if( obj->Error().severity() < SEVERITY_WARNING ) {
499 ++_errorCount;
500 } else if( obj->Error().severity() < SEVERITY_NULL ) {
501 ++_warningCount;
502 }
503 obj->Error().ClearErrorMsg();
504
505 if( _fileType == WORKING_SESSION ) {
506 instances().Append( obj, inst_state );
507 } else {
508 instances().Append( obj, newSE );
509 }
510
511 ++instance_count;
512 } else {
513 ++_entsNotCreated;
514 //old
515 ++_errorCount;
516 }
517
518 if( _entsNotCreated > _maxErrorCount ) {
519 _error.AppendToUserMsg( "Warning: Too Many Errors in File. Read function aborted.\n" );
520 cerr << Error().UserMsg();
521 cerr << Error().DetailMsg();
522 Error().ClearErrorMsg();
523 Error().severity( SEVERITY_EXIT );
524 return instance_count;
525 }
526
527 endsec = FoundEndSecKywd( in );
528
529 }
530 } // end while loop
531
532 if( _entsNotCreated ) {
533 sprintf( buf,
534 "STEPfile Reading File: Unable to create %d instances.\n\tIn first pass through DATA section. Check for invalid entity types.\n",
535 _entsNotCreated );
536 _error.AppendToUserMsg( buf );
537 _error.GreaterSeverity( SEVERITY_WARNING );
538 }
539 if( !in.good() ) {
540 _error.AppendToUserMsg( "Error in input file.\n" );
541 }
542
543 _iFileStage1Done = true;
544 return instance_count;
545 }
546
ReadWorkingData1(istream & in)547 int STEPfile::ReadWorkingData1( istream & in ) {
548 return ReadData1( in );
549 }
550
551 /**
552 * \returns number of valid instances read
553 * reads in the data portion of the instances in an exchange file
554 */
ReadData2(istream & in,bool useTechCor)555 int STEPfile::ReadData2( istream & in, bool useTechCor ) {
556 _entsInvalid = 0;
557 _entsIncomplete = 0;
558 _entsWarning = 0;
559
560 int total_instances = 0;
561 int valid_insts = 0; // used for exchange file only
562 stateEnum inst_state = noStateSE; // used if reading working file
563
564 _errorCount = 0; // reset error count
565 _warningCount = 0; // reset error count
566
567 char c;
568 char buf[BUFSIZ];
569 buf[0] = '\0';
570 std::string tmpbuf;
571
572 SDAI_Application_instance * obj = ENTITY_NULL;
573 std::string cmtStr;
574
575 int endsec = FoundEndSecKywd( in );
576
577 // PASS 2: read instances
578 while( in.good() && !endsec ) {
579 ReadTokenSeparator( in, &cmtStr );
580 in >> c;
581
582 if( _fileType == WORKING_SESSION ) {
583 if( strchr( "CIND", c ) ) { // if there is a valid char
584 inst_state = EntityWfState( c );
585 ReadTokenSeparator( in, &cmtStr );
586 in >> c; // read the ENTITY_NAME_DELIM
587 }
588 }
589
590 if( c != ENTITY_NAME_DELIM ) {
591 in.putback( c );
592 while( c != ENTITY_NAME_DELIM && in.good() &&
593 !( endsec = FoundEndSecKywd( in ) ) ) {
594
595 tmpbuf.clear();
596 FindStartOfInstance( in, tmpbuf );
597 cout << "ERROR: trying to recover from invalid data. skipping: "
598 << tmpbuf << endl;
599 in >> c;
600 ReadTokenSeparator( in, &cmtStr );
601 }
602 }
603
604
605 if( !endsec ) {
606 obj = ENTITY_NULL;
607 if( ( _fileType == WORKING_SESSION ) && ( inst_state == deleteSE ) ) {
608 SkipInstance( in, tmpbuf );
609 } else {
610 obj = ReadInstance( in, cout, cmtStr, useTechCor );
611 _iFileCurrentPosition = in.tellg();
612 }
613
614 cmtStr.clear();
615 if( obj != ENTITY_NULL ) {
616 if( obj->Error().severity() < SEVERITY_INCOMPLETE ) {
617 ++_entsInvalid;
618 // old
619 ++_errorCount;
620 } else if( obj->Error().severity() == SEVERITY_INCOMPLETE ) {
621 ++_entsIncomplete;
622 ++_entsInvalid;
623 } else if( obj->Error().severity() == SEVERITY_USERMSG ) {
624 ++_entsWarning;
625 } else { // i.e. if severity == SEVERITY_NULL
626 ++valid_insts;
627 }
628
629 obj->Error().ClearErrorMsg();
630
631 ++total_instances;
632 } else {
633 ++_entsInvalid;
634 // old
635 ++_errorCount;
636 }
637
638 if( _entsInvalid > _maxErrorCount ) {
639 _error.AppendToUserMsg( "Warning: Too Many Errors in File. Read function aborted.\n" );
640 cerr << Error().UserMsg();
641 cerr << Error().DetailMsg();
642 Error().ClearErrorMsg();
643 Error().severity( SEVERITY_EXIT );
644 return valid_insts;
645 }
646
647 endsec = FoundEndSecKywd( in );
648 }
649 } // end while loop
650
651 if( _entsInvalid ) {
652 sprintf( buf,
653 "%s \n\tTotal instances: %d \n\tInvalid instances: %d \n\tIncomplete instances (includes invalid instances): %d \n\t%s: %d.\n",
654 "Second pass complete - instance summary:", total_instances,
655 _entsInvalid, _entsIncomplete, "Warnings",
656 _entsWarning );
657 cout << buf << endl;
658 _error.AppendToUserMsg( buf );
659 _error.AppendToDetailMsg( buf );
660 _error.GreaterSeverity( SEVERITY_WARNING );
661 }
662 if( !in.good() ) {
663 _error.AppendToUserMsg( "Error in input file.\n" );
664 }
665
666 return valid_insts;
667 }
668
ReadWorkingData2(istream & in,bool useTechCor)669 int STEPfile::ReadWorkingData2( istream & in, bool useTechCor ) {
670 return ReadData2( in, useTechCor );
671 }
672
673 /** Looks for the word DATA followed by optional whitespace
674 * followed by a semicolon. When it is looking for the word
675 * DATA it skips over strings and comments.
676 */
FindDataSection(istream & in)677 int STEPfile::FindDataSection( istream & in ) {
678 ErrorDescriptor errs;
679 SDAI_String tmp;
680 std::string s; // used if need to read a comment
681 char c;
682
683 while( in.good() ) {
684 in >> c;
685 if( in.eof() ) {
686 _error.AppendToUserMsg( "Can't find \"DATA;\" section." );
687 return 0; //ERROR_WARNING
688 }
689 switch( c ) {
690 case 'D': // look for string "DATA;" with optional whitespace after "DATA"
691 c = in.peek(); // look at next char (for 'A')
692 // only peek since it may be 'D' again a we need to start over
693 if( c == 'A' ) {
694 in.get( c ); // read char 'A'
695 c = in.peek(); // look for 'T'
696 if( c == 'T' ) {
697 in.get( c ); // read 'T'
698 c = in.peek(); // look for 'A'
699 if( c == 'A' ) {
700 in.get( c ); // read 'A'
701 in >> ws; // may want to skip comments or print control directives?
702 c = in.peek(); // look for semicolon
703 if( c == ';' ) {
704 in.get( c ); // read the semicolon
705 return 1; // success
706 }
707 }
708 }
709 }
710 break;
711
712 case '\'': // get past the string
713 in.putback( c );
714 tmp.STEPread( in, &errs );
715 break;
716
717 case '/': // read p21 file comment
718 in.putback( c ); // looks like a comment
719 ReadComment( in, s );
720 break;
721
722 case '\0': // problem in input ?
723 return 0;
724
725 default:
726 break;
727 }
728 }
729 return 0;
730 }
731
FindHeaderSection(istream & in)732 int STEPfile::FindHeaderSection( istream & in ) {
733 char buf[BUFSIZ];
734 char * b = buf;
735
736 *b = '\0';
737
738 ReadTokenSeparator( in );
739 // find the header section
740 while( !( b = strstr( buf, "HEADER" ) ) ) {
741 if( in.eof() ) {
742 _error.AppendToUserMsg(
743 "Error: Unable to find HEADER section. File not read.\n" );
744 _error.GreaterSeverity( SEVERITY_INPUT_ERROR );
745 return 0;
746 }
747 in.getline( buf, BUFSIZ, ';' ); // reads but does not store the ;
748 }
749 return 1;
750 }
751 //dasdelete
752
753 //dasdelete
754 /**
755 description:
756 This function creates an instance based on the KEYWORD
757 read from the istream.
758 It expects an ENTITY_NAME (int) from the first set of
759 characters on the istream. If the (int) is not found,
760 ENTITY_NULL is returned.
761
762 side effects:
763 The function leaves the istream set to the end of the stream
764 of characters representing the ENTITY_INSTANCE. It attempts to
765 recover on errors by reading up to and including the next ';'.
766
767 an ENTITY_INSTANCE consists of:
768 '#'(int)'=' [SCOPE] SIMPLE_RECORD ';' ||
769 '#'(int)'=' [SCOPE] SUBSUPER_RECORD ';'
770 The '#' is read from the istream before CreateInstance is called.
771 */
CreateInstance(istream & in,ostream & out)772 SDAI_Application_instance * STEPfile::CreateInstance( istream & in, ostream & out ) {
773 std::string tmpbuf;
774 std::string objnm;
775
776 char c;
777 std::string schnm;
778
779 int fileid = -1;
780 SDAI_Application_instance_ptr * scopelist = 0;
781
782 SDAI_Application_instance * obj;
783 ErrorDescriptor result;
784 // Sent down to CreateSubSuperInstance() to receive error info
785
786 ReadTokenSeparator( in );
787
788 in >> fileid; // read instance id
789 fileid = IncrementFileId( fileid );
790 if( instances().FindFileId( fileid ) ) {
791 SkipInstance( in, tmpbuf );
792 out << "ERROR: instance #" << fileid
793 << " already exists.\n\tData lost: " << tmpbuf << endl;
794 return ENTITY_NULL;
795 }
796
797 ReadTokenSeparator( in );
798 in.get( c ); // read equal sign
799 if( c != '=' ) {
800 // ERROR: '=' expected
801 SkipInstance( in, tmpbuf );
802 out << "ERROR: instance #" << fileid
803 << " \'=\' expected.\n\tData lost: " << tmpbuf << endl;
804 return ENTITY_NULL;
805 }
806
807 ReadTokenSeparator( in );
808 c = in.peek(); // peek at the next character on the istream
809
810 //check for optional "&SCOPE" construct
811 if( c == '&' ) { // TODO check this out
812 Severity s = CreateScopeInstances( in, &scopelist );
813 if( s < SEVERITY_WARNING ) {
814 return ENTITY_NULL;
815 }
816 ReadTokenSeparator( in );
817 c = in.peek(); // peek at next char on istream again
818 }
819
820 //check for subtype/supertype record
821 if( c == '(' ) {
822 // TODO: implement complex inheritance
823
824 obj = CreateSubSuperInstance( in, fileid, result );
825 if( obj == ENTITY_NULL ) {
826 SkipInstance( in, tmpbuf );
827 out << "ERROR: instance #" << fileid
828 << " Illegal complex entity.\n"
829 << result.UserMsg() << ".\n\n";
830 return ENTITY_NULL;
831 }
832 } else { // not a complex entity
833 // check for User Defined Entity
834 int userDefined = 0;
835 if( c == '!' ) {
836 userDefined = 1;
837 in.get( c );
838 }
839
840 ReadStdKeyword( in, objnm, 1 ); // read the type name
841 if( !in.good() ) {
842 out << "ERROR: instance #" << fileid
843 << " Unexpected file problem in "
844 << "STEPfile::CreateInstance.\n";
845 }
846
847 //create the instance using the Registry object
848 if( userDefined ) {
849 SkipInstance( in, tmpbuf );
850 out << "WARNING: instance #" << fileid
851 << " User Defined Entity in DATA section ignored.\n"
852 << "\tData lost: \'!" << objnm << "\': " << tmpbuf
853 << endl;
854 return ENTITY_NULL;
855 } else {
856 schnm = schemaName();
857 obj = reg().ObjCreate( objnm.c_str(), schnm.c_str() );
858 if( obj == ENTITY_NULL ) {
859 // This will be the case if objnm does not exist in the reg.
860 result.UserMsg( "Unknown ENTITY type" );
861 } else if( obj->Error().severity() <= SEVERITY_WARNING ) {
862 // Common causes of error is that obj is an abstract supertype
863 // or that it can only be instantiated using external mapping.
864 // If neither are the case, create a generic message.
865 if( !obj->Error().UserMsg().empty() ) {
866 result.UserMsg( obj->Error().UserMsg() );
867 } else {
868 result.UserMsg( "Could not create ENTITY" );
869 }
870 // Delete obj so that below we'll know that an error occur:
871 delete obj;
872 obj = ENTITY_NULL;
873 }
874 }
875 }
876
877 if( obj == ENTITY_NULL ) {
878 SkipInstance( in, tmpbuf );
879 out << "ERROR: instance #" << fileid << " \'" << objnm
880 << "\': " << result.UserMsg()
881 << ".\n\tData lost: " << tmpbuf << "\n\n";
882 return ENTITY_NULL;
883 }
884 obj -> STEPfile_id = fileid;
885
886 // scan values
887 SkipInstance( in, tmpbuf );
888
889 ReadTokenSeparator( in );
890 return obj;
891 }
892
893
894 /**
895 description:
896 This function reads the SCOPE list for an entity instance,
897 creates each instance on the scope list, and appending each
898 instance to the instance manager.
899 side-effects:
900 It first searches for "&SCOPE" and reads all characters
901 from the istream up to and including "ENDSCOPE"
902 returns: ErrorDescriptor
903 >= SEVERITY_WARNING: the istream was read up to and including the
904 "ENDSCOPE" and the export list /#1, ... #N/
905 < SEVERITY_WARNING: the istream was read up to and including the next ";"
906 < SEVERITY_BUG: fatal
907 */
CreateScopeInstances(istream & in,SDAI_Application_instance_ptr ** scopelist)908 Severity STEPfile::CreateScopeInstances( istream & in, SDAI_Application_instance_ptr ** scopelist ) {
909 Severity rval = SEVERITY_NULL;
910 SDAI_Application_instance * se;
911 std::string tmpbuf;
912 char c;
913 std::vector< SDAI_Application_instance_ptr > inscope;
914 std::string keywd;
915
916 keywd = GetKeyword( in, " \n\t/\\#;", _error );
917 if( strncmp( const_cast<char *>( keywd.c_str() ), "&SCOPE", 6 ) ) {
918 //ERROR: "&SCOPE" expected
919 //TODO: should attempt to recover by reading through ENDSCOPE
920 //currently recovery is attempted by reading through next ";"
921 SkipInstance( in, tmpbuf );
922 cerr << "ERROR: " << "\'&SCOPE\' expected." <<
923 "\n\tdata lost: " << tmpbuf << "\n";
924 return SEVERITY_INPUT_ERROR;
925 }
926
927 ReadTokenSeparator( in );
928
929 in.get( c );
930 while( c == '#' ) {
931 se = CreateInstance( in, cout );
932 if( se != ENTITY_NULL ) {
933 //TODO: apply scope information to se
934 // Add se to scopelist
935 inscope.push_back( se );
936
937 //append the se to the instance manager
938 instances().Append( se, newSE );
939 } else {
940 //ERROR: instance in SCOPE not created
941 rval = SEVERITY_WARNING;
942 SkipInstance( in, tmpbuf );
943 cerr << "instance in SCOPE not created.\n\tdata lost: "
944 << tmpbuf << "\n";
945 ++_errorCount;
946 }
947
948 ReadTokenSeparator( in );
949 in.get( c );
950 }
951 in.putback( c );
952 *scopelist = new SDAI_Application_instance_ptr [inscope.size()];
953 for( size_t i = 0; i < inscope.size(); ++i ) {
954 *scopelist[i] = inscope[i];
955 }
956
957 //check for "ENDSCOPE"
958 keywd = GetKeyword( in, " \t\n/\\#;", _error );
959 if( strncmp( const_cast<char *>( keywd.c_str() ), "ENDSCOPE", 8 ) ) {
960 //ERROR: "ENDSCOPE" expected
961 SkipInstance( in, tmpbuf );
962 cerr << "ERROR: " << "\'ENDSCOPE\' expected."
963 << "\n\tdata lost: " << tmpbuf << "\n";
964 ++_errorCount;
965 return SEVERITY_INPUT_ERROR;
966 }
967
968 //check for export list
969 ReadTokenSeparator( in );
970 in.get( c );
971 in.putback( c );
972 if( c == '/' ) {
973 //read export list
974 in.get( c );
975 c = ',';
976 while( c == ',' ) {
977 int exportid;
978 ReadTokenSeparator( in );
979 in.get( c );
980 if( c != '#' ) { } //ERROR
981 in >> exportid;
982 //TODO: nothing is done with the idnums on the export list
983 ReadTokenSeparator( in );
984 in.get( c );
985 }
986 if( c != '/' ) {
987 //ERROR: '/' expected while reading export list
988 SkipInstance( in, tmpbuf );
989 cerr << "ERROR: \'/\' expected in export list.\n\tdata lost: " << tmpbuf << "\n";
990 ++_errorCount;
991 rval = SEVERITY_INPUT_ERROR;
992 }
993 ReadTokenSeparator( in );
994 }
995 return rval;
996 }
997
CreateSubSuperInstance(istream & in,int fileid,ErrorDescriptor & e)998 SDAI_Application_instance * STEPfile::CreateSubSuperInstance( istream & in, int fileid, ErrorDescriptor & e ) {
999 SDAI_Application_instance * obj = ENTITY_NULL;
1000
1001 char c;
1002 std::string schnm;
1003
1004 std::string buf; // used to hold the simple record that is read
1005 ErrorDescriptor err; // used to catch error msgs
1006
1007 const int enaSize = 64;
1008 std::string * entNmArr[enaSize]; // array of entity type names
1009 int enaIndex = 0;
1010
1011 in >> ws;
1012 in.get( c ); // read the open paren
1013 c = in.peek(); // see if you have closed paren (ending the record)
1014 while( in.good() && ( c != ')' ) && ( enaIndex < enaSize ) ) {
1015 entNmArr[enaIndex] = new std::string( "" );
1016 ReadStdKeyword( in, *( entNmArr[enaIndex] ), 1 ); // read the type name
1017 if( entNmArr[enaIndex]->empty() ) {
1018 delete entNmArr[enaIndex];
1019 entNmArr[enaIndex] = 0;
1020 } else {
1021 SkipSimpleRecord( in, buf, &err );
1022 buf.clear();
1023 enaIndex++;
1024 }
1025 in >> ws;
1026 c = in.peek(); // see if you have closed paren (ending the record)
1027 // If someone separates the entities with commas (or some other
1028 // garbage or a comment) this will keep the read function from
1029 // infinite looping. If the entity name starts with a digit it is
1030 // incorrect and will be invalid.
1031 while( in.good() && ( c != ')' ) && !isalpha( c ) ) {
1032 in >> c; // skip the invalid char
1033 c = in.peek(); // see if you have closed paren (ending the record)
1034 }
1035 }
1036 entNmArr[enaIndex] = 0;
1037 schnm = schemaName();
1038
1039 obj = new STEPcomplex( &_reg, ( const std::string ** )entNmArr, fileid, schnm.c_str() );
1040
1041 if( obj->Error().severity() <= SEVERITY_WARNING ) {
1042 // If obj is not legal, record its error info and delete it:
1043 e.severity( obj->Error().severity() );
1044 e.UserMsg( obj->Error().UserMsg() );
1045 delete obj;
1046 obj = ENTITY_NULL;
1047 }
1048
1049 enaIndex = 0;
1050 while( entNmArr[enaIndex] != 0 ) {
1051 delete entNmArr[enaIndex];
1052 enaIndex ++;
1053 }
1054
1055 return obj;
1056 }
1057
1058 /**
1059 description:
1060 This function reads the SCOPE list for an entity instance,
1061 and reads the values for each instance from the istream.
1062 side-effects:
1063 It first searches for "&SCOPE" and reads all characters
1064 from the istream up to and including "ENDSCOPE"
1065 */
ReadScopeInstances(istream & in)1066 Severity STEPfile::ReadScopeInstances( istream & in ) {
1067 Severity rval = SEVERITY_NULL;
1068 SDAI_Application_instance * se;
1069 std::string tmpbuf;
1070 char c;
1071 std::string keywd;
1072 std::string cmtStr;
1073
1074 keywd = GetKeyword( in, " \n\t/\\#;", _error );
1075 if( strncmp( const_cast<char *>( keywd.c_str() ), "&SCOPE", 6 ) ) {
1076 //ERROR: "&SCOPE" expected
1077 SkipInstance( in, tmpbuf );
1078 cerr << "\'&SCOPE\' expected.\n\tdata lost: " << tmpbuf << "\n";
1079 ++_errorCount;
1080 return SEVERITY_WARNING;
1081 }
1082
1083 ReadTokenSeparator( in );
1084
1085 in.get( c );
1086 while( c == '#' ) {
1087 se = ReadInstance( in, cout, cmtStr );
1088 if( se != ENTITY_NULL ) {
1089 //apply scope information to se
1090 //TODO: not yet implemented
1091 } else {
1092 //ERROR: unable to read instance in SCOPE
1093 rval = SEVERITY_WARNING;
1094 }
1095
1096 ReadTokenSeparator( in );
1097 in.get( c );
1098 }
1099 in.putback( c );
1100
1101 //check for "ENDSCOPE"
1102 keywd = GetKeyword( in, " \t\n/\\#;", _error );
1103 if( strncmp( const_cast<char *>( keywd.c_str() ), "ENDSCOPE", 8 ) ) {
1104 //ERROR: "ENDSCOPE" expected
1105 SkipInstance( in, tmpbuf );
1106 cerr << " \'ENDSCOPE\' expected.\n\tdata lost: " << tmpbuf << "\n";
1107 ++_errorCount;
1108 return SEVERITY_WARNING;
1109 }
1110
1111 //check for export list
1112 ReadTokenSeparator( in );
1113 in.get( c );
1114 in.putback( c );
1115 if( c == '/' ) {
1116 //read through export list
1117 in.get( c );
1118 c = ',';
1119 while( c == ',' ) {
1120 int exportid;
1121 ReadTokenSeparator( in );
1122 in.get( c );
1123 in >> exportid;
1124 ReadTokenSeparator( in );
1125 in.get( c );
1126 }
1127 if( c != '/' ) {
1128 //ERROR: '/' expected while reading export list
1129 SkipInstance( in, tmpbuf );
1130 cerr << " \'/\' expected while reading export list.\n\tdata lost: "
1131 << tmpbuf << "\n";
1132 ++_errorCount;
1133 rval = SEVERITY_WARNING;
1134 }
1135 ReadTokenSeparator( in );
1136 }
1137 return rval;
1138 }
1139
1140 /**
1141 This function populates a SDAI_Application_instance with the values read from
1142 the istream.
1143
1144 This function must keeps track of error messages encountered when
1145 reading the SDAI_Application_instance. It passes SDAI_Application_instance error information onto
1146 the STEPfile ErrorDescriptor.
1147 */
ReadInstance(istream & in,ostream & out,std::string & cmtStr,bool useTechCor)1148 SDAI_Application_instance * STEPfile::ReadInstance( istream & in, ostream & out, std::string & cmtStr,
1149 bool useTechCor ) {
1150 Severity sev = SEVERITY_NULL;
1151
1152 std::string tmpbuf;
1153 char errbuf[BUFSIZ];
1154 errbuf[0] = '\0';
1155 std::string currSch;
1156 std::string objnm;
1157
1158 char c;
1159 int fileid;
1160 SDAI_Application_instance * obj = ENTITY_NULL;
1161 int idIncrNum = FileIdIncr();
1162
1163 ReadComment( in, cmtStr );
1164
1165 in >> fileid;
1166 fileid = IncrementFileId( fileid );
1167
1168 // check to see that instance was created on PASS 1
1169 MgrNode * node = instances().FindFileId( fileid );
1170 if( ( !node ) || ( ( obj = node -> GetApplication_instance() ) == ENTITY_NULL ) ) {
1171 SkipInstance( in, tmpbuf );
1172 // Changed the 2nd pass error message to report Part 21 User
1173 // Defined Entities. STEPfile still includes them in the error count
1174 // which is not valid.
1175 // Check to see if an User Defined Entity has been found.
1176 const char * ude = tmpbuf.c_str();
1177 while( *ude && ( *ude != '=' ) ) {
1178 ude++;
1179 }
1180 if( *ude == '=' ) {
1181 ude++;
1182 }
1183 while( *ude && isspace( *ude ) ) {
1184 ude++;
1185 }
1186 if( *ude == '!' ) {
1187 out << "\nWARNING: #" << fileid <<
1188 " - Ignoring User Defined Entity.\n\tData lost: "
1189 << tmpbuf << endl;
1190 } else
1191 out << "\nERROR: in 2nd pass, instance #" << fileid
1192 << " not found.\n\tData lost: " << tmpbuf << endl;
1193 return ENTITY_NULL;
1194 } else if( ( _fileType != WORKING_SESSION ) && ( node->CurrState() != newSE ) ) {
1195 SkipInstance( in, tmpbuf );
1196 out << "\nERROR: in 2nd pass, instance #" << fileid
1197 << " already exists - ignoring duplicate.\n\tData lost: "
1198 << tmpbuf << endl;
1199 return ENTITY_NULL;
1200 }
1201
1202 ReadTokenSeparator( in, &cmtStr );
1203
1204 in.get( c );
1205 if( c != '=' ) {
1206 //ERROR: '=' expected
1207 SkipInstance( in, tmpbuf );
1208 out << "ERROR: instance #" << fileid
1209 << " \'=\' expected.\n\tData lost: " << tmpbuf << endl;
1210 return ENTITY_NULL;
1211 }
1212
1213 ReadTokenSeparator( in, &cmtStr );
1214
1215 //peek at the next character on the istream
1216 c = in.peek();
1217
1218 //check for optional "&SCOPE" construct
1219 if( c == '&' ) {
1220 ReadScopeInstances( in );
1221 ReadTokenSeparator( in, &cmtStr );
1222 in.get( c );
1223 in.putback( c );
1224 }
1225
1226 currSch = schemaName();
1227
1228 //check for subtype/supertype record
1229 if( c == '(' ) {
1230 // TODO
1231 sev = obj->STEPread( fileid, idIncrNum, &instances(), in, currSch.c_str(),
1232 useTechCor, _strict );
1233
1234 ReadTokenSeparator( in, &cmtStr );
1235
1236 if( !cmtStr.empty() ) {
1237 obj->AddP21Comment( cmtStr );
1238 }
1239
1240 c = in.peek(); // check for semicolon or keyword 'ENDSEC'
1241 if( c != 'E' ) {
1242 in >> c; // read the semicolon
1243 }
1244 } else {
1245 ReadTokenSeparator( in, &cmtStr );
1246 c = in.peek();
1247
1248 // check for User Defined Entity
1249 // DAS - I checked this out. It doesn't get into this code for user
1250 // defined entities because a ude isn't created.
1251 int userDefined = 0;
1252 if( c == '!' ) {
1253 userDefined = 1;
1254 in.get( c );
1255 }
1256
1257 ReadStdKeyword( in, objnm, 1 ); // read the type name
1258 if( !in.good() ) {
1259 out << "ERROR: instance #" << fileid
1260 << " Unexpected file problem in "
1261 << "STEPfile::ReadInstance." << endl;
1262 }
1263 ReadTokenSeparator( in, &cmtStr );
1264
1265 // read values
1266 if( userDefined ) {
1267 SkipInstance( in, tmpbuf );
1268 out << "WARNING: #" << fileid <<
1269 ". Ignoring User defined entity." << endl << " data lost: !"
1270 << objnm << tmpbuf << endl;
1271 ++_warningCount;
1272 return ENTITY_NULL;
1273 }
1274
1275 // NOTE: this function is called for all FileTypes
1276 // (WORKING_SESSION included)
1277
1278 sev = obj->STEPread( fileid, idIncrNum, &instances(), in, currSch.c_str(),
1279 useTechCor, _strict );
1280
1281 ReadTokenSeparator( in, &cmtStr );
1282
1283 if( !cmtStr.empty() ) {
1284 obj->AddP21Comment( cmtStr );
1285 }
1286
1287 c = in.peek(); // check for semicolon or keyword 'ENDSEC'
1288 if( c != 'E' ) {
1289 in >> c; // read the semicolon
1290 }
1291
1292 AppendEntityErrorMsg( &( obj->Error() ) );
1293 }
1294
1295 //set the node's state,
1296 //and set the STEPfile:_error (based on the type of file being read)
1297 switch( sev ) {
1298 case SEVERITY_NULL:
1299 case SEVERITY_USERMSG:
1300 if( _fileType != WORKING_SESSION ) {
1301 node->ChangeState( completeSE );
1302 }
1303 break;
1304
1305 case SEVERITY_WARNING:
1306 case SEVERITY_INPUT_ERROR:
1307 case SEVERITY_BUG:
1308
1309 case SEVERITY_INCOMPLETE:
1310 if( ( _fileType == VERSION_CURRENT ) ) {
1311 cerr << "ERROR in EXCHANGE FILE: incomplete instance #"
1312 << obj -> STEPfile_id << ".\n";
1313 if( _fileType != WORKING_SESSION ) {
1314 node->ChangeState( incompleteSE );
1315 }
1316 } else {
1317 if( node->CurrState() == completeSE ) {
1318 sprintf( errbuf, "WARNING in WORKING FILE: changing instance #%d state from completeSE to incompleteSE.\n", fileid );
1319 _error.AppendToUserMsg( errbuf );
1320 if( _fileType != WORKING_SESSION ) {
1321 node->ChangeState( incompleteSE );
1322 }
1323 }
1324 }
1325 break;
1326
1327 case SEVERITY_EXIT:
1328 case SEVERITY_DUMP:
1329 case SEVERITY_MAX:
1330 if( _fileType != WORKING_SESSION ) {
1331 node->ChangeState( noStateSE );
1332 }
1333 break;
1334
1335 default:
1336 break;
1337 }
1338
1339 // check ErrorDesc severity and set the state for MgrNode *node
1340 // according to completeSE or incompleteSE
1341 // watch how you set it based on whether you are reading an
1342 // exchange or working file.
1343
1344 return obj;
1345
1346 }
1347
1348
1349
1350 /**
1351 This function uses the C library function system to issue
1352 a shell command which checks for the existence of the
1353 file name and creates a backup file if the file exists.
1354 The system command takes a string, which must be a valid
1355 sh command. The command is:
1356 if (test -f filename) then mv filename filename.bak; fi
1357
1358 BUG: doesn't check to see if the backup command works.
1359 the results of the system call are not used by the
1360 by this function
1361 */
MakeBackupFile()1362 void STEPfile::MakeBackupFile() {
1363 std::string bckup = FileName();
1364 bckup.append( ".bak" );
1365
1366 std::fstream f( FileName().c_str(), std::fstream::in | std::fstream::binary );
1367 f << std::noskipws;
1368 std::istream_iterator<unsigned char> begin( f );
1369 std::istream_iterator<unsigned char> end;
1370
1371 std::fstream f2( bckup.c_str(), std::fstream::out | std::fstream::trunc | std::fstream::binary );
1372 std::ostream_iterator<char> begin2( f2 );
1373
1374 copy( begin, end, begin2 );
1375
1376 _error.AppendToDetailMsg( "Making backup file: " );
1377 _error.AppendToDetailMsg( bckup.c_str() );
1378 _error.AppendToDetailMsg( "\n" );
1379 }
1380
WriteExchangeFile(ostream & out,int validate,int clearError,int writeComments)1381 Severity STEPfile::WriteExchangeFile( ostream & out, int validate, int clearError,
1382 int writeComments ) {
1383 Severity rval = SEVERITY_NULL;
1384 SetFileType( VERSION_CURRENT );
1385 if( clearError ) {
1386 _error.ClearErrorMsg();
1387 }
1388
1389 if( validate ) {
1390 rval = instances().VerifyInstances( _error );
1391 _error.GreaterSeverity( rval );
1392 if( rval < SEVERITY_USERMSG ) {
1393 _error.AppendToUserMsg( "Unable to verify instances. File not written. Try saving as working session file." );
1394 _error.GreaterSeverity( SEVERITY_INCOMPLETE );
1395 return rval;
1396 }
1397 }
1398
1399 out << FILE_DELIM << "\n";
1400 WriteHeader( out );
1401 WriteData( out, writeComments );
1402 out << END_FILE_DELIM << "\n";
1403 return rval;
1404 }
1405
WriteExchangeFile(const std::string filename,int validate,int clearError,int writeComments)1406 Severity STEPfile::WriteExchangeFile( const std::string filename, int validate, int clearError,
1407 int writeComments ) {
1408 Severity rval = SEVERITY_NULL;
1409
1410 if( clearError ) {
1411 _error.ClearErrorMsg();
1412 }
1413
1414 if( validate ) {
1415 rval = instances().VerifyInstances( _error );
1416 _error.GreaterSeverity( rval );
1417 if( rval < SEVERITY_USERMSG ) {
1418 _error.AppendToUserMsg( "Unable to verify instances. File wasn't opened. Try saving as working session file.\n" );
1419 _error.GreaterSeverity( SEVERITY_INCOMPLETE );
1420 return rval;
1421 }
1422 }
1423
1424 ostream * out = OpenOutputFile( filename );
1425 if( _error.severity() < SEVERITY_WARNING ) {
1426 return _error.severity();
1427 }
1428 rval = WriteExchangeFile( *out, 0, 0, writeComments );
1429 CloseOutputFile( out );
1430 return rval;
1431 }
1432
WriteValuePairsFile(ostream & out,int validate,int clearError,int writeComments,int mixedCase)1433 Severity STEPfile::WriteValuePairsFile( ostream & out, int validate, int clearError,
1434 int writeComments, int mixedCase ) {
1435 Severity rval = SEVERITY_NULL;
1436 SetFileType( VERSION_CURRENT );
1437 if( clearError ) {
1438 _error.ClearErrorMsg();
1439 }
1440
1441 if( validate ) {
1442 rval = instances().VerifyInstances( _error );
1443 _error.GreaterSeverity( rval );
1444 if( rval < SEVERITY_USERMSG ) {
1445 _error.AppendToUserMsg( "Unable to verify instances. File not written. Try saving as working session file." );
1446 _error.GreaterSeverity( SEVERITY_INCOMPLETE );
1447 return rval;
1448 }
1449 }
1450
1451 WriteValuePairsData( out, writeComments, mixedCase );
1452 return rval;
1453 }
1454
1455 /**
1456 This function returns an integer value for
1457 the file id for a header section entity,
1458 based on a given entity name.
1459
1460 This function may change the value of the
1461 STEPfile member variable: _headerId
1462
1463 The header section entities must be numbered in the following manner:
1464
1465 #1=FILE_DESCRIPTION
1466 #2=FILE_NAME
1467 #3=FILE_SCHEMA
1468 */
HeaderId(const char * name)1469 int STEPfile::HeaderId( const char * name ) {
1470 std::string tmp = name;
1471
1472 std::transform( tmp.begin(), tmp.end(), tmp.begin(), ::toupper );
1473
1474 if( tmp == "FILE_DESCRIPTION" ) {
1475 return 1;
1476 }
1477 if( tmp == "FILE_NAME" ) {
1478 return 2;
1479 }
1480 if( tmp == "FILE_SCHEMA" ) {
1481 return 3;
1482 }
1483 return ++_headerId;
1484 }
1485
1486 /***************************
1487 ***************************/
WriteHeader(ostream & out)1488 void STEPfile::WriteHeader( ostream & out ) {
1489 out << "HEADER;\n";
1490
1491 WriteHeaderInstanceFileDescription( out );
1492 WriteHeaderInstanceFileName( out );
1493 WriteHeaderInstanceFileSchema( out );
1494
1495 // Write the rest of the header instances
1496 SDAI_Application_instance * se;
1497 int n = _headerInstances->InstanceCount();
1498 for( int i = 0; i < n; ++i ) {
1499 se = _headerInstances->GetMgrNode( i ) ->GetApplication_instance();
1500 if( !(
1501 ( se->StepFileId() == HeaderId( "File_Name" ) ) ||
1502 ( se->StepFileId() == HeaderId( "File_Description" ) ) ||
1503 ( se->StepFileId() == HeaderId( "File_Schema" ) )
1504 ) )
1505 WriteHeaderInstance(
1506 _headerInstances->GetMgrNode( i )->GetApplication_instance(), out );
1507 }
1508 out << "ENDSEC;\n";
1509 }
1510
1511 /***************************
1512 ***************************/
WriteHeaderInstance(SDAI_Application_instance * obj,ostream & out)1513 void STEPfile::WriteHeaderInstance( SDAI_Application_instance * obj, ostream & out ) {
1514 std::string tmp;
1515 if( !obj->P21Comment().empty() ) {
1516 out << obj->P21Comment();
1517 }
1518 out << StrToUpper( obj->EntityName(), tmp ) << "(";
1519 int n = obj->attributes.list_length();
1520 for( int i = 0; i < n; ++i ) {
1521 ( obj->attributes[i] ).STEPwrite( out );
1522 if( i < n - 1 ) {
1523 out << ",";
1524 }
1525 }
1526 out << ");\n";
1527 }
1528
1529 /***************************
1530 ***************************/
WriteHeaderInstanceFileName(ostream & out)1531 void STEPfile::WriteHeaderInstanceFileName( ostream & out ) {
1532 // Get the FileName instance from _headerInstances
1533 SDAI_Application_instance * se = 0;
1534 se = _headerInstances->GetApplication_instance( "File_Name" );
1535 if( se == ENTITY_NULL ) {
1536 se = ( SDAI_Application_instance * )HeaderDefaultFileName();
1537 }
1538
1539 //set some of the attribute values at time of output
1540 SdaiFile_name * fn = ( SdaiFile_name * )se;
1541
1542 /* I'm not sure this is a good idea that Peter did but I'll leave around - DAS
1543 // write time_stamp (as specified in ISO Standard 8601)
1544 // output the current system time to the file, using the following format:
1545 // example: '1994-04-12T15:27:46'
1546 // for Calendar Date, 12 April 1994, 27 minute 46 seconds past 15 hours
1547 */
1548 time_t t = time( NULL );
1549 struct tm * timeptr = localtime( &t );
1550 char time_buf[26];
1551 strftime( time_buf, 26, "'%Y-%m-%dT%H:%M:%S'", timeptr );
1552 fn->time_stamp_( time_buf );
1553
1554 //output the values to the file
1555 WriteHeaderInstance( se, out );
1556 }
1557
WriteHeaderInstanceFileDescription(ostream & out)1558 void STEPfile::WriteHeaderInstanceFileDescription( ostream & out ) {
1559 // Get the FileDescription instance from _headerInstances
1560 SDAI_Application_instance * se = 0;
1561 se = _headerInstances->GetApplication_instance( "File_Description" );
1562 if( se == ENTITY_NULL ) {
1563 // ERROR: no File_Name instance in _headerInstances
1564 // create a File_Name instance
1565 se = ( SDAI_Application_instance * )HeaderDefaultFileDescription();
1566 }
1567 WriteHeaderInstance( se, out );
1568 }
1569
WriteHeaderInstanceFileSchema(ostream & out)1570 void STEPfile::WriteHeaderInstanceFileSchema( ostream & out ) {
1571 // Get the FileName instance from _headerInstances
1572 SDAI_Application_instance * se = 0;
1573 se = _headerInstances->GetApplication_instance( "File_Schema" );
1574 if( se == ENTITY_NULL ) {
1575 // ERROR: no File_Name instance in _headerInstances
1576 // create a File_Name instance
1577 se = ( SDAI_Application_instance * ) HeaderDefaultFileSchema();
1578 }
1579 WriteHeaderInstance( se, out );
1580 }
1581
WriteData(ostream & out,int writeComments)1582 void STEPfile::WriteData( ostream & out, int writeComments ) {
1583 _oFileInstsWritten = 0;
1584 std::string currSch = schemaName();
1585 out << "DATA;\n";
1586
1587 int n = instances().InstanceCount();
1588 for( int i = 0; i < n; ++i ) {
1589 instances().GetMgrNode( i )->GetApplication_instance()->STEPwrite( out, currSch.c_str(), writeComments );
1590 _oFileInstsWritten++;
1591 }
1592
1593 out << "ENDSEC;\n";
1594 }
1595
WriteValuePairsData(ostream & out,int writeComments,int mixedCase)1596 void STEPfile::WriteValuePairsData( ostream & out, int writeComments, int mixedCase ) {
1597 std::string currSch = schemaName();
1598 int n = instances().InstanceCount();
1599 for( int i = 0; i < n; ++i ) {
1600 instances().GetMgrNode( i )->GetApplication_instance()->WriteValuePairs( out, currSch.c_str(), writeComments, mixedCase );
1601 }
1602 }
1603
AppendFile(istream * in,bool useTechCor)1604 Severity STEPfile::AppendFile( istream * in, bool useTechCor ) {
1605 Severity rval = SEVERITY_NULL;
1606 char errbuf[BUFSIZ];
1607
1608 SetFileIdIncrement();
1609 int total_insts = 0, valid_insts = 0;
1610
1611 ReadTokenSeparator( *in );
1612 std::string keywd = GetKeyword( *in, "; #", _error );
1613 // get the delimiter off the istream
1614 char c;
1615 in->get( c );
1616
1617 if( !strncmp( const_cast<char *>( keywd.c_str() ), "ISO-10303-21",
1618 strlen( const_cast<char *>( keywd.c_str() ) ) ) ) {
1619 SetFileType( VERSION_CURRENT );
1620 } else if( !strncmp( const_cast<char *>( keywd.c_str() ), "STEP_WORKING_SESSION",
1621 strlen( const_cast<char *>( keywd.c_str() ) ) ) ) {
1622 if( _fileType != WORKING_SESSION ) {
1623 _error.AppendToUserMsg(
1624 "Warning: Reading in file as Working Session file.\n" );
1625 _error.GreaterSeverity( SEVERITY_WARNING );
1626 }
1627 SetFileType( WORKING_SESSION );
1628 } else {
1629 sprintf( errbuf,
1630 "Faulty input at beginning of file. \"ISO-10303-21;\" or"
1631 " \"STEP_WORKING_SESSION;\" expected. File not read: %s\n",
1632 ( ( FileName().compare( "-" ) == 0 ) ? "standard input" : FileName().c_str() ) );
1633 _error.AppendToUserMsg( errbuf );
1634 _error.GreaterSeverity( SEVERITY_INPUT_ERROR );
1635 return SEVERITY_INPUT_ERROR;
1636 }
1637
1638 cout << "Reading Data from " << ( ( FileName().compare( "-" ) == 0 ) ? "standard input" : FileName().c_str() ) << "...\n";
1639
1640 // Read header
1641 rval = ReadHeader( *in );
1642 cout << "\nHEADER read:";
1643 if( rval < SEVERITY_WARNING ) {
1644 sprintf( errbuf,
1645 "Error: non-recoverable error in reading header section. "
1646 "There were %d errors encountered. Rest of file is ignored.\n",
1647 _errorCount );
1648 _error.AppendToUserMsg( errbuf );
1649 return rval;
1650 } else if( rval != SEVERITY_NULL ) {
1651 sprintf( errbuf, " %d ERRORS\t %d WARNINGS\n\n",
1652 _errorCount, _warningCount );
1653 cout << errbuf;
1654 } else {
1655 cout << endl;
1656 }
1657
1658 if( !FindDataSection( *in ) ) {
1659 _error.AppendToUserMsg( "Error: Unable to find DATA section delimiter. Data section not read. Rest of file ignored.\n" );
1660 return SEVERITY_INPUT_ERROR;
1661 }
1662
1663 // PASS 1
1664 _errorCount = 0;
1665 total_insts = ReadData1( *in );
1666
1667 cout << "\nFIRST PASS complete: " << total_insts
1668 << " instances created.\n";
1669 sprintf( errbuf,
1670 " %d ERRORS\t %d WARNINGS\n\n",
1671 _errorCount, _warningCount );
1672 cout << errbuf;
1673
1674 // PASS 2
1675 // This would be nicer if you didn't actually have to close the
1676 // file but could just reposition the pointer back to the
1677 // beginning of the data section. It looks like you can do this
1678 // with the GNU File class, but that class doesn't have the
1679 // operator >> overloaded which is used to do the rest of the
1680 // parsing. SO we are using istreams and this works, but could
1681 // be better.
1682
1683 // reset the error count so you're not counting things twice:
1684 _errorCount = 0;
1685 istream * in2;
1686 if( !( ( in2 = OpenInputFile() ) && ( in2 -> good() ) ) ) {
1687 // if the stream is not readable, there's an error
1688 _error.AppendToUserMsg( "Cannot open file for 2nd pass -- No data read.\n" );
1689 CloseInputFile( in2 );
1690 return SEVERITY_INPUT_ERROR;
1691 }
1692 if( !FindDataSection( *in2 ) ) {
1693 _error.AppendToUserMsg( "Error: Unable to find DATA section delimiter in second pass. \nData section not read. Rest of file ignored.\n" );
1694 CloseInputFile( in2 );
1695 return SEVERITY_INPUT_ERROR;
1696 }
1697
1698 switch( _fileType ) {
1699 case VERSION_CURRENT:
1700 case VERSION_UNKNOWN:
1701 case WORKING_SESSION:
1702 valid_insts = ReadData2( *in2, useTechCor );
1703 break;
1704 default:
1705 _error.AppendToUserMsg( "STEPfile::AppendFile: STEP file version set to unrecognized value.\n" );
1706 CloseInputFile( in2 );
1707 return SEVERITY_BUG;
1708 }
1709
1710 //check for "ENDSEC;"
1711 ReadTokenSeparator( *in2 );
1712 if( total_insts != valid_insts ) {
1713 sprintf( errbuf, "%d invalid instances in file: %s\n",
1714 total_insts - valid_insts, ( ( FileName().compare( "-" ) == 0 ) ? "standard input" : FileName().c_str() ) );
1715 _error.AppendToUserMsg( errbuf );
1716 CloseInputFile( in2 );
1717 return _error.GreaterSeverity( SEVERITY_WARNING );
1718 }
1719
1720 cout << "\nSECOND PASS complete: " << valid_insts
1721 << " instances valid.\n";
1722 sprintf( errbuf,
1723 " %d ERRORS\t %d WARNINGS\n\n",
1724 _errorCount, _warningCount );
1725 _error.AppendToUserMsg( errbuf );
1726 cout << errbuf;
1727
1728
1729 //check for "ENDSTEP;" || "END-ISO-10303-21;"
1730
1731 if( in2 -> good() ) {
1732 ReadTokenSeparator( *in2 );
1733 keywd = GetKeyword( *in2, ";", _error );
1734 //yank the ";" from the istream
1735 //if (';' == in2->peek()) in2->get();
1736 char ch;
1737 in2->get( ch );
1738 if( ch != ';' ) {
1739 std::cerr << __FILE__ << ":" << __LINE__ << " - Expected ';' at Part 21 EOF, found '" << c << "'." << std::endl;
1740 }
1741 }
1742
1743 if( ( !keywd.compare( 0, keywd.size(), END_FILE_DELIM ) ) || !( in2 -> good() ) ) {
1744 _error.AppendToUserMsg( END_FILE_DELIM );
1745 _error.AppendToUserMsg( " missing at end of file.\n" );
1746 CloseInputFile( in2 );
1747 return _error.GreaterSeverity( SEVERITY_WARNING );
1748 }
1749 CloseInputFile( in2 );
1750 cout << "Finished reading file.\n\n";
1751 return SEVERITY_NULL;
1752 }
1753
WriteWorkingFile(ostream & out,int clearError,int writeComments)1754 Severity STEPfile::WriteWorkingFile( ostream & out, int clearError, int writeComments ) {
1755 SetFileType( WORKING_SESSION );
1756 if( clearError ) {
1757 _error.ClearErrorMsg();
1758 }
1759
1760 if( instances().VerifyInstances( _error ) < SEVERITY_INCOMPLETE ) {
1761 _error.AppendToUserMsg( "WARNING: some invalid instances written to working session file. Data may have been lost." );
1762 _error.GreaterSeverity( SEVERITY_INCOMPLETE );
1763 }
1764
1765 out << FILE_DELIM << "\n";
1766 WriteHeader( out );
1767
1768 WriteWorkingData( out, writeComments );
1769 out << END_FILE_DELIM << "\n";
1770 SetFileType();
1771
1772 return _error.severity();
1773 }
1774
WriteWorkingFile(const std::string filename,int clearError,int writeComments)1775 Severity STEPfile::WriteWorkingFile( const std::string filename, int clearError,
1776 int writeComments ) {
1777 if( clearError ) {
1778 _error.ClearErrorMsg();
1779 }
1780 ostream * out = OpenOutputFile( filename );
1781 if( _error.severity() < SEVERITY_WARNING ) {
1782 return _error.severity();
1783 }
1784 Severity rval = WriteWorkingFile( *out, 0, writeComments );
1785 CloseOutputFile( out );
1786
1787 return rval;
1788 }
1789
WriteWorkingData(ostream & out,int writeComments)1790 void STEPfile::WriteWorkingData( ostream & out, int writeComments ) {
1791 std::string currSch = schemaName();
1792 out << "DATA;\n";
1793 int n = instances().InstanceCount();
1794
1795 for( int i = 0; i < n; ++i ) {
1796 switch( instances().GetMgrNode( i )->CurrState() ) {
1797 case deleteSE:
1798 out << wsDelete;
1799 instances().GetMgrNode( i )->GetApplication_instance()->
1800 STEPwrite( out, currSch.c_str(), writeComments );
1801 break;
1802 case completeSE:
1803 out << wsSaveComplete;
1804 instances().GetMgrNode( i )->GetApplication_instance()->
1805 STEPwrite( out, currSch.c_str(), writeComments );
1806 break;
1807 case incompleteSE:
1808 out << wsSaveIncomplete;
1809 instances().GetMgrNode( i )->GetApplication_instance()->
1810 STEPwrite( out, currSch.c_str(), writeComments );
1811 break;
1812 case newSE:
1813 out << wsNew;
1814 instances().GetMgrNode( i )->GetApplication_instance()->
1815 STEPwrite( out, currSch.c_str(), writeComments );
1816 break;
1817 case noStateSE:
1818 _error.AppendToUserMsg( "no state information for this node\n" );
1819 break;
1820 }
1821 }
1822 out << "ENDSEC;\n";
1823 }
1824
1825 /**
1826 This function sends messages to 'cerr'
1827 based on the values in the given ErrorDescriptor (e), which is
1828 supposed to be from a SDAI_Application_instance object, being manipulated by one
1829 of the STEPfile member functions.
1830
1831 The given error descriptor's messages are then cleared.
1832
1833 The STEPfile's error descriptor is set no lower than SEVERITY_WARNING.
1834
1835 */
AppendEntityErrorMsg(ErrorDescriptor * e)1836 Severity STEPfile::AppendEntityErrorMsg( ErrorDescriptor * e ) {
1837 ErrorDescriptor * ed = e;
1838
1839 Severity sev = ed->severity();
1840
1841 if( ( sev < SEVERITY_MAX ) || ( sev > SEVERITY_NULL ) ) {
1842 //ERROR: something wrong with ErrorDescriptor
1843 _error.GreaterSeverity( SEVERITY_WARNING );
1844 return SEVERITY_BUG;
1845 }
1846
1847 switch( sev ) {
1848 case SEVERITY_NULL:
1849 return SEVERITY_NULL;
1850
1851 default: {
1852 cerr << e->DetailMsg();
1853 e->ClearErrorMsg();
1854
1855 if( sev < SEVERITY_USERMSG ) {
1856 ++_errorCount;
1857 }
1858 if( sev < SEVERITY_WARNING ) {
1859 sev = SEVERITY_WARNING;
1860 }
1861
1862 _error.GreaterSeverity( sev );
1863 return sev;
1864 }
1865 }
1866 }
1867