1 
2 /*
3 * NIST STEP Core Class Library
4 * clstepcore/sdaiSelect.cc
5 * April 1997
6 * Dave Helfrick
7 * KC Morris
8 
9 * Development of this software was funded by the United States Government,
10 * and is not subject to copyright.
11 */
12 
13 #include <stdio.h> // to get the BUFSIZ #define
14 #include <ExpDict.h>
15 #include <sstream>
16 #include <string>
17 #include <sdai.h>
18 #include <STEPattribute.h>
19 
20 #ifdef  SC_LOGGING
21 #include <fstream.h>
22 extern ofstream * logStream;
23 #endif
24 
25 /**********
26     (member) functions for the select class SDAI_Select
27 **********/
SDAI_Select(const SelectTypeDescriptor * s,const TypeDescriptor * td)28 SDAI_Select::SDAI_Select( const SelectTypeDescriptor * s,
29                           const TypeDescriptor * td )
30     : _type( s ), underlying_type( td ) {
31 #ifdef SC_LOGGING
32     *logStream << "Exiting SDAI_Select constructor." << endl;
33 #endif
34 }
35 
SDAI_Select(const SDAI_Select & other)36 SDAI_Select::SDAI_Select( const SDAI_Select & other ) {
37     underlying_type = other.underlying_type;
38     base_type = other.base_type;
39     _type = other._type;
40 #ifdef SC_LOGGING
41     *logStream << "Exiting SDAI_Select constructor." << endl;
42 #endif
43 }
44 
~SDAI_Select()45 SDAI_Select::~SDAI_Select() {
46 }
47 
operator =(const SDAI_Select & other)48 SDAI_Select & SDAI_Select::operator=( const SDAI_Select & other ) {
49     if( &other != this ) {
50         _error = other._error;
51         _type = other._type;
52         base_type = other.base_type;
53         underlying_type = other.underlying_type;
54         val = other.val;
55     }
56     return *this;
57 }
58 
severity() const59 Severity SDAI_Select::severity() const {
60     return _error.severity();
61 }
62 
severity(Severity s)63 Severity  SDAI_Select::severity( Severity s ) {
64     return _error.severity( s );
65 }
66 
Error()67 std::string SDAI_Select::Error() {
68     return _error.DetailMsg();
69 }
70 
Error(const char * e)71 void SDAI_Select::Error( const char * e ) {
72     _error.DetailMsg( e );
73 }
74 
ClearError()75 void  SDAI_Select::ClearError() {
76     _error.ClearErrorMsg();
77 }
78 
79 const TypeDescriptor *
CanBe(const char * n) const80 SDAI_Select::CanBe( const char * n ) const {
81     return _type -> CanBe( n );
82 }
83 
84 
85 const TypeDescriptor *
CanBe(BASE_TYPE bt) const86 SDAI_Select::CanBe( BASE_TYPE bt ) const {
87     const TypeDescLinkNode * tdn =
88         ( const TypeDescLinkNode * ) _type -> GetElements().GetHead();
89     const TypeDescriptor * td = tdn -> TypeDesc();
90     BASE_TYPE bt_thisnode;
91 
92     while( tdn )  {
93         td = tdn -> TypeDesc();
94         if( ( ( bt_thisnode = td -> NonRefType() ) == bt ) ||
95                 ( bt == AGGREGATE_TYPE && ( ( bt_thisnode == ARRAY_TYPE ) ||
96                                             ( bt_thisnode == LIST_TYPE ) ||
97                                             ( bt_thisnode == SET_TYPE ) ||
98                                             ( bt_thisnode == BAG_TYPE ) ) ) ) {
99             return td;    // they are the same
100         }
101         tdn = ( TypeDescLinkNode * )( tdn -> NextNode() );
102     }
103     return 0;
104 }
105 
106 const TypeDescriptor *
CanBe(const TypeDescriptor * td) const107 SDAI_Select::CanBe( const TypeDescriptor * td ) const {
108     return _type -> CanBe( td );
109 }
110 
111 const TypeDescriptor *
CanBeSet(const char * n,const char * schnm) const112 SDAI_Select::CanBeSet( const char * n, const char * schnm ) const {
113     return _type -> CanBeSet( n, schnm );
114 }
115 
116 int
IsUnique(const BASE_TYPE bt) const117 SDAI_Select::IsUnique( const BASE_TYPE bt ) const {
118     if( bt == ARRAY_TYPE ||
119             bt == LIST_TYPE ||
120             bt == BAG_TYPE ||
121             bt == SET_TYPE ) {
122         return ( ( _type->UniqueElements() ) & AGGREGATE_TYPE );
123     } else {
124         return ( ( _type->UniqueElements() ) & bt );
125     }
126 }
127 
128 
UnderlyingTypeName() const129 SDAI_String SDAI_Select::UnderlyingTypeName() const {
130     return underlying_type -> Name();
131 }
132 
CurrentUnderlyingType() const133 const TypeDescriptor  * SDAI_Select::CurrentUnderlyingType() const {
134     return underlying_type;
135 }
136 
137 const TypeDescriptor *
SetUnderlyingType(const TypeDescriptor * td)138 SDAI_Select::SetUnderlyingType( const TypeDescriptor * td ) {
139     //  don\'t do anything if the descriptor is bad
140     if( !td || !( _type -> CanBe( td ) ) ) {
141         return 0;
142     }
143 
144     base_type = td -> NonRefType();
145 
146     return underlying_type = td;
147 }
148 
exists() const149 bool SDAI_Select::exists() const {
150     return underlying_type != NULL;
151 }
152 
nullify()153 void SDAI_Select::nullify() {
154     underlying_type = 0;
155 }
156 
SelectValidLevel(const char * attrValue,ErrorDescriptor * err,InstMgrBase * im)157 Severity SDAI_Select::SelectValidLevel( const char * attrValue, ErrorDescriptor * err,
158                                         InstMgrBase * im ) {
159     SDAI_Select * tmp = NewSelect();
160     Severity s = SEVERITY_NULL;
161 
162     istringstream strtmp( attrValue );
163     s = tmp -> STEPread( strtmp, err, im );
164     delete tmp;
165     return s;
166 }
167 
StrToVal(const char * Val,const char * selectType,ErrorDescriptor * err,InstMgrBase * instances)168 Severity SDAI_Select::StrToVal( const char * Val, const char * selectType,
169                                 ErrorDescriptor * err, InstMgrBase * instances ) {
170     severity( SEVERITY_NULL );
171     if( SetUnderlyingType( CanBe( selectType ) ) )
172 
173         //  the underlying type is set to a valid type
174         // call read on underlying type in subclass
175 
176         switch( base_type )  {
177             case ENTITY_TYPE: {
178                 STEPentity * tmp =
179                     ReadEntityRef( Val, err, ",)", instances, 0 );
180                 if( tmp && ( tmp != ENTITY_NULL ) ) {
181                     AssignEntity( tmp );
182                     return severity();
183                 } else {
184                     err->AppendToDetailMsg(
185                         "Reference to entity that is not a valid type for SELECT.\n" );
186                     nullify();
187                     err->GreaterSeverity( SEVERITY_WARNING );
188                     return SEVERITY_WARNING;
189                 }
190             }
191 
192             // call StrToVal on the contents
193             case STRING_TYPE:
194             case AGGREGATE_TYPE:
195             case ARRAY_TYPE:      // DAS
196             case BAG_TYPE:        // DAS
197             case SET_TYPE:        // DAS
198             case LIST_TYPE:       // DAS
199             case ENUM_TYPE:
200             case SELECT_TYPE:
201             case BOOLEAN_TYPE:
202             case LOGICAL_TYPE: {
203                 err->GreaterSeverity( StrToVal_content( Val, instances ) );
204                 if( _error.severity() != SEVERITY_NULL ) {
205                     err->AppendFromErrorArg( &_error );
206                 }
207                 return err->severity();
208             }
209 
210             // do the same as STEPread_content
211             case BINARY_TYPE:
212             case NUMBER_TYPE:
213             case REAL_TYPE:
214             case INTEGER_TYPE:
215             default: {
216                 istringstream strtmp( Val );
217                 err->GreaterSeverity( STEPread_content( strtmp ) );
218                 if( _error.severity() != SEVERITY_NULL ) {
219                     err->AppendFromErrorArg( &_error );
220                 }
221                 return err->severity();
222             }
223         }
224     return SEVERITY_INPUT_ERROR;
225 }
226 
227 /** updated to Technical Corrigendum. DAS 2/4/97
228  * This function does the following:
229  */
STEPread(istream & in,ErrorDescriptor * err,InstMgrBase * instances,const char * utype,int addFileId,const char * currSch)230 Severity SDAI_Select::STEPread( istream & in, ErrorDescriptor * err,
231                                 InstMgrBase * instances, const char * utype,
232                                 int addFileId, const char * currSch ) {
233     char c = '\0';
234     std::string tmp;
235 
236 #ifdef SC_LOGGING
237 //    *logStream << "DAVE ERR Entering SDAI_Select::STEPread." << endl;
238 #endif
239     // find out what case we have
240     //  NOTE case C falls out of recursive calls in cases A and B
241 
242     /**
243     ** This section of code is used to read a value belonging to a select
244     ** contained in another select. If you have read the text part of the
245     ** TYPED_PARAMETER and it needs to fall down thru some levels of contained
246     ** select types, then the text is passed down to each select in the utype
247     ** parameter as STEPread is called on each contained select type.DAS 2/4/97
248     */
249     if( utype ) {
250         if( SetUnderlyingType( CanBeSet( utype, currSch ) ) ) {
251             //  assign the value to the underlying type
252             in >> ws; // skip white space
253             if( ( underlying_type->Type() == REFERENCE_TYPE ) &&
254                     ( underlying_type->NonRefType() == sdaiSELECT ) ) {
255                 // See comments below for a similar code segment.
256                 STEPread_content( in, instances, 0, addFileId, currSch );
257             } else {
258                 STEPread_content( in, instances, utype, addFileId, currSch );
259             }
260             err->AppendToDetailMsg( Error() );
261             err->GreaterSeverity( severity() );
262         }
263         return err->severity();
264     }
265     in >> ws;
266     in >> c;
267 
268     /**
269     ** the if part of this code reads a value according to the Technical
270     ** Corrigendum for Part 21 (except for Entity instance refs which are read
271     ** in the else part - everything else in the else stmt is invalid). The
272     ** idea here is to read text and find out if it is specifying the final
273     ** type or is a simple defined type which is typed to be a select some
274     ** number of levels down. The first case will cause the value to be read
275     ** directly by STEPread_content() or will cause STEPread_content() to pass
276     ** the text naming the type to the underlying Select containing it. When
277     ** the latter is the case, STEPread_content calls STEPread for the
278     ** contained select and passes in the text. The convoluted case is when
279     ** the text describes a simple defined type that translates directly into
280     ** a select some number of levels down. In this case there will be more
281     ** text inside the containing parens to indicate the type. Since the text
282     ** specifying the type still needs to be read utype is passed as null which
283     ** will cause the contained Select STEPread function to read it. DAS 2/4/97
284     */
285 
286     if( isalpha( c ) ) { //  case B
287         int eot = 0; // end of token flag
288         //  token is a type name - get the type
289         while( ( c != '(' ) && in.good() ) {
290             if( !eot && !( eot = isspace( c ) ) )
291                 // as long as eot hasn\'t been reached keep appending
292             {
293                 tmp += c;
294             }
295             in >> c;
296         }
297 
298         //  check for valid type and set the underlying type
299         if( SetUnderlyingType( CanBeSet( tmp.c_str(), currSch ) ) ) {
300             /**
301             ** Assign the value to the underlying type.  CanBeSet() is a
302             ** slightly modified CanBe().  It ensures that a renamed select
303             ** type (see big comment below) "CantBe" one of its member items.
304             ** This is because such a select type requires its own name to
305             ** appear first ("selX("), so even if we read a valid element of
306             ** selX, selX can't be the underlying type.  That can only be the
307             ** case if "selX" appears first and is what we just read.
308             */
309             in >> ws; // skip white space
310             if( ( underlying_type->Type() == REFERENCE_TYPE ) &&
311                     ( underlying_type->NonRefType() == sdaiSELECT ) ) {
312                 /**
313                  * This means (1) that the underlying type is itself a select
314                 ** (cond 2), and (2) it's not defined in the EXPRESS as a
315                 ** select, but as a renamed select.  (E.g., TYPE sel2 = sel1.)
316                 ** In such a case, according to the TC, "sel2(" must appear in
317                 ** the text.  (We must have found one for SetUnderlyingType()
318                 ** above to set underlying_type to sel2.)  If all of the above
319                 ** is true, we read "sel2" but not the value of sel2.  We
320                 ** therefore do not pass down what we read to STEPread_content
321                 ** below, so that we can still read the value of sel2.  If,
322                 ** however, under_type was a non-renamed select, no "sel1"
323                 ** would appear (according to TC) and we already read the value
324                 ** of sel1.  If so, we pass the already-read value down.
325                  */
326                 STEPread_content( in, instances, 0, addFileId, currSch );
327             } else {
328                 /**
329                 ** In most cases (see above note), we've already read the value
330                 ** we're interested in.
331                 ** This also handles all other cases? other than the if part
332                 ** above and elements of type entity ref?
333                 */
334                 STEPread_content( in, instances, tmp.c_str(), addFileId,
335                                   currSch );
336                 // STEPread_content uses the ErrorDesc data member from the
337                 // SDAI_Select class
338             }
339             err->AppendToDetailMsg( Error() );
340             err->GreaterSeverity( severity() );
341             in >> ws >> c;
342             if( c != ')' ) {
343                 err->AppendToDetailMsg(
344                     "Bad data or missing closing ')' for SELECT type.\n" );
345                 err->GreaterSeverity( SEVERITY_WARNING );
346                 in.putback( c );
347 #ifdef SC_LOGGING
348 //    *logStream << "DAVE ERR Exiting SDAI_Select::STEPread for " << _type->Name() << endl;
349 #endif
350                 return SEVERITY_WARNING;
351             }
352 #ifdef SC_LOGGING
353 //    *logStream << "DAVE ERR Exiting SDAI_Select::STEPread for " << _type->Name() << endl;
354 #endif
355             return err->severity();
356         } else { // ERROR  -- the type wasn't one of the choices
357             if( !in.good() ) {
358                 err->GreaterSeverity( SEVERITY_INPUT_ERROR );
359 #ifdef SC_LOGGING
360 //    *logStream << "DAVE ERR Exiting SDAI_Select::STEPread for " << _type->Name() << endl;
361 #endif
362                 return SEVERITY_INPUT_ERROR;
363             } else {
364                 err->AppendToDetailMsg(
365                     "The type name for the SELECT type is not valid.\n" );
366                 err->GreaterSeverity( SEVERITY_WARNING );
367 #ifdef SC_LOGGING
368 //    *logStream << "DAVE ERR Exiting SDAI_Select::STEPread for " << _type->Name() << endl;
369 #endif
370                 return SEVERITY_WARNING;
371             }
372         }
373     }
374     /**
375     ** NOTE **** anything that gets read below here is invalid according to the
376     ** Technical Corrigendum for Part 21 (except for reading an entity instance
377     ** where below is the only place they get read). I am leaving all of this
378     ** here since it is valuable to 'read what you can' and flag an error which
379     ** is what it does (for every type but entity instance refs). DAS 2/4/97
380     */
381 
382     else { /// case A
383         switch( c ) {
384             case '$':
385                 nullify();
386                 err->GreaterSeverity( SEVERITY_INCOMPLETE );
387 #ifdef SC_LOGGING
388 //    *logStream << "DAVE ERR Exiting SDAI_Select::STEPread for " << _type->Name() << endl;
389 #endif
390                 return SEVERITY_INCOMPLETE;
391 
392             case ',':
393             case '\0':
394                 // ERROR  IN INPUT
395                 in.putback( c );
396                 err->AppendToDetailMsg( "No value found for SELECT type.\n" );
397                 err->GreaterSeverity( SEVERITY_WARNING );
398 #ifdef SC_LOGGING
399 //    *logStream << "DAVE ERR Exiting SDAI_Select::STEPread for " << _type->Name() << endl;
400 #endif
401                 return SEVERITY_WARNING;
402 
403             case '.': // assign enum
404                 base_type = ENUM_TYPE;
405                 err->AppendToDetailMsg( "Invalid Enumeration, Logical, or Boolean value in SELECT type.\n" );
406                 err->GreaterSeverity( SEVERITY_WARNING );
407                 break;
408                 // set the underlying type
409                 // call STEPread
410                 // return
411 
412             case '\'': // assign string
413                 base_type = STRING_TYPE;
414                 err->AppendToDetailMsg( "Invalid String value in SELECT type.\n" );
415                 err->GreaterSeverity( SEVERITY_WARNING );
416                 break;
417 
418             case '"': // assign string
419                 base_type = BINARY_TYPE;
420                 err->AppendToDetailMsg( "Invalid Binary value in SELECT type.\n" );
421                 err->GreaterSeverity( SEVERITY_WARNING );
422                 break;
423 
424             case '#':
425                 base_type = ENTITY_TYPE;
426                 break;
427                 // call STEPread_reference
428                 // set the underlying type
429 
430                 // assign entity
431                 // read the reference
432                 // match type to underlying type
433                 // assign the value
434                 // set the underlying type
435 
436             case '(': {
437                 err->AppendToDetailMsg( "Invalid aggregate value in SELECT type.\n" );
438                 err->GreaterSeverity( SEVERITY_WARNING );
439                 char n;
440                 in >> n;
441                 in.putback( n );
442                 if( isalpha( n ) ) {
443                     base_type = SELECT_TYPE;
444                 } else {
445                     base_type = AGGREGATE_TYPE;
446                 }
447                 break;
448             }
449 
450             case '0':
451             case '1':
452             case '2':
453             case '3':
454             case '4':
455             case '5':
456             case '6':
457             case '7':
458             case '8':
459             case '9':
460             case '-':
461                 err->AppendToDetailMsg( "Invalid Integer or Real value in SELECT type.\n" );
462                 err->GreaterSeverity( SEVERITY_WARNING );
463                 if( CanBe( REAL_TYPE ) ) {
464                     base_type = REAL_TYPE;
465                 } else {
466                     base_type = INTEGER_TYPE;
467                 }
468                 break;
469 
470             default:
471                 // ambiguous - ERROR:  underlying type should have been set
472                 err->AppendToDetailMsg(
473                     "type for SELECT could not be determined from value.\n" );
474                 nullify();
475                 in.putback( c );
476                 err->GreaterSeverity( SEVERITY_WARNING );
477 #ifdef SC_LOGGING
478 //    *logStream << "DAVE ERR Exiting SDAI_Select::STEPread for " << _type->Name() << endl;
479 #endif
480                 return SEVERITY_WARNING;
481         }
482 
483         in.putback( c );
484 
485         // now the type descriptor should be derivable from the base_type
486 
487         // if it's not issue a warning
488         if( _type && !( IsUnique( base_type ) ) )  {
489             err->AppendToDetailMsg( "Value for SELECT will be assigned to first possible choice.\n" );
490             err->GreaterSeverity( SEVERITY_USERMSG );
491         }
492 
493         if( base_type == ENTITY_TYPE ) {
494             // you don't know if this is an ENTITY or a SELECT
495             // have to do this here - not in STEPread_content
496             STEPentity * temp =
497                 ReadEntityRef( in, err, ",)", instances, addFileId );
498             if( temp && ( temp != ENTITY_NULL ) && AssignEntity( temp ) ) {
499 #ifdef SC_LOGGING
500 //    *logStream << "DAVE ERR Exiting SDAI_Select::STEPread for " << _type->Name() << endl;
501 #endif
502                 return SEVERITY_NULL;
503             } else {
504                 err->AppendToDetailMsg(
505                     "Reference to entity that is not a valid type for SELECT.\n" );
506                 nullify();
507                 err->GreaterSeverity( SEVERITY_WARNING );
508 #ifdef SC_LOGGING
509 //    *logStream << "DAVE ERR Exiting SDAI_Select::STEPread for " << _type->Name() << endl;
510 #endif
511                 return SEVERITY_WARNING;
512             }
513         } else if( SetUnderlyingType( CanBe( base_type ) ) ) {
514             STEPread_content( in, instances, 0, addFileId );
515         }
516 
517         else { // ERROR  -- the type wasn\'t one of the choices
518             err->AppendToDetailMsg(
519                 "The type of the SELECT type is not valid.\n" );
520             err->GreaterSeverity( SEVERITY_WARNING );
521 #ifdef SC_LOGGING
522 //    *logStream << "DAVE ERR Exiting SDAI_Select::STEPread for " << _type->Name() << endl;
523 #endif
524             return SEVERITY_WARNING;
525         }
526     }
527 //    if (!in.good())   severity (SEVERITY_INPUT_ERROR);
528 #ifdef SC_LOGGING
529 //    *logStream << "DAVE ERR Exiting SDAI_Select::STEPread for " << _type->Name() << endl;
530 #endif
531     return err->severity();
532 }
533 
534 
535 /// updated to Technical Corrigendum DAS Feb 4, 1997
STEPwrite(ostream & out,const char * currSch) const536 void SDAI_Select::STEPwrite( ostream & out, const char * currSch )  const {
537     if( !exists() ) {
538         out << "$";
539         return;
540     }
541     switch( underlying_type->NonRefType() ) {
542         case sdaiINSTANCE: {
543             STEPwrite_content( out );
544             break;
545         }
546         case sdaiSELECT: { // The name of a select is never written DAS 1/31/97
547             if( underlying_type->Type() == REFERENCE_TYPE ) {
548                 std::string s;
549                 out << StrToUpper( underlying_type->Name( currSch ), s ) << "(";
550                 STEPwrite_content( out, currSch );
551                 out << ")";
552             } else {
553                 STEPwrite_content( out, currSch );
554             }
555             break;
556         }
557         case sdaiNUMBER:
558         case sdaiREAL:
559         case sdaiINTEGER:
560         case sdaiSTRING:
561         case sdaiBOOLEAN:
562         case sdaiLOGICAL:
563         case sdaiBINARY:
564         case sdaiENUMERATION:
565         case sdaiAGGR:
566         case ARRAY_TYPE:
567         case BAG_TYPE:
568         case SET_TYPE:
569         case LIST_TYPE: {
570             STEPwrite_verbose( out, currSch );
571             break;
572         }
573         case REFERENCE_TYPE: // this should never happen? DAS
574         default:
575             out << "ERROR Should not have gone here in SDAI_Select::STEPwrite()"
576                 << endl;
577     }
578 }
579 
STEPwrite_verbose(ostream & out,const char * currSch) const580 void SDAI_Select::STEPwrite_verbose( ostream & out, const char * currSch ) const {
581     std::string tmp;
582     out << StrToUpper( CurrentUnderlyingType()->Name( currSch ), tmp ) << "(";
583     STEPwrite_content( out );
584     out << ")";
585 }
586 
STEPwrite(std::string & s,const char * currSch) const587 const char * SDAI_Select::STEPwrite( std::string & s, const char * currSch )  const {
588     ostringstream buf;
589     STEPwrite( buf, currSch );
590     buf << ends;  // add the terminating \0 char
591     s = buf.str();
592     return const_cast<char *>( s.c_str() );
593 }
594 
set_null()595 int SDAI_Select::set_null() {
596     nullify();
597     return 1;
598 }
599 
is_null()600 int SDAI_Select::is_null() {
601     return ( !exists() );
602 }
603