

//



//
// $Id: STEPselect.cc,v 1.3 1999/11/17 08:34:42 gcosmo Exp $
// GEANT4 tag $Name: geant4-01-00 $
//

/*
* NIST STEP Core Class Library
* clstepcore/STEPselect.cc
* May 1995
* Dave Helfrick
* KC Morris

* Development of this software was funded by the United States Government,
* and is not subject to copyright.
*/

/*  */

#include <STEPselect.h>
#include <stdio.h> // to get the BUFSIZ #define
#include <ExpDict.h>
#ifdef WIN32
#  include <Strstrea.h>
#else
#  include <strstream.h>
#endif
#include <STEPentity.h>

/**********
	(member) functions for the select class SdaiSelect
**********/
/*
SdaiSelect::SdaiSelect()
{
}
SdaiSelect::SdaiSelect( const SdaiSelect& s )
{
}
*/

Severity SdaiSelect::severity() const
{		/**  fn Severity  **/
   return _error.severity();
}		/**  end function  **/

Severity  SdaiSelect::severity( Severity s )
{		/**  fn Severity  **/
   return _error.severity( s );
}		/**  end function  **/

const char * SdaiSelect::Error()
{		/**  fn Error  **/
   return _error.DetailMsg();
}		/**  end function  **/

void SdaiSelect::Error( char * e )
{		/**  fn Error  **/
   _error.DetailMsg( e );
}		/**  end function  **/

void  SdaiSelect::ClearError()
{		/**  end function  **/
   _error.ClearErrorMsg();
}		/**  end function  **/

const TypeDescriptor *
SdaiSelect::CanBe (const char * n) const  
{
    const TypeDescLinkNode * tdn = 
		(const TypeDescLinkNode *) _type -> GetElements ().GetHead ();
    const TypeDescriptor *td = tdn -> TypeDesc ();

    SCLstring tmp1;
    SCLstring tmp2;
  
    while (tdn)  {
	td = tdn -> TypeDesc ();
	if (strcmp( (char *)StrToUpper( n, tmp1 ), 
		    (char *)StrToUpper( td->Name(), tmp2 ) ))
	    ;
	else 
	    return td;  // they are the same 
	tdn = (TypeDescLinkNode *) (tdn -> NextNode ());
    }
    return 0;
}


const TypeDescriptor *
SdaiSelect::CanBe (BASE_TYPE bt) const  
{
  const TypeDescLinkNode * tdn = (const TypeDescLinkNode *) _type -> GetElements ().GetHead ();
  const TypeDescriptor *td = tdn -> TypeDesc ();

  while (tdn)  {
    td = tdn -> TypeDesc ();    
    if (td -> NonRefType () == bt)
      return td;  // they are the same 
    tdn = (TypeDescLinkNode *) (tdn -> NextNode ());
  }
  return 0;
}

const TypeDescriptor *
SdaiSelect::CanBe (const TypeDescriptor * td) const  
{
  return _type -> CanBe (td);
}

SdaiString SdaiSelect::UnderlyingTypeName () const  
{
  return underlying_type-> Name ();
}

const TypeDescriptor *  SdaiSelect::CurrentUnderlyingType() const 
{
  return underlying_type;
}

const TypeDescriptor * 
SdaiSelect::SetUnderlyingType (const TypeDescriptor * td)
{
  //  don\'t do anything if the descriptor is bad
  if (!td || !(_type -> CanBe (td))) return 0;

  base_type = td -> NonRefType ();
  return underlying_type = td;
}

int SdaiSelect::exists() const 
{
  return (int) underlying_type;
}

void SdaiSelect::nullify() 
{
  underlying_type = 0;
}

Severity 
SdaiSelect::SelectValidLevel(const char *attrValue, ErrorDescriptor *err, 
			     InstMgr *im, int clearError)
{
  SdaiSelect * tmp = NewSelect();
  Severity s = SEVERITY_NULL;

//  COMMENTED OUT TO ALLOW FOR TYPE RESOLUTION WHEN TYPE CHANGES
//  if (CurrentUnderlyingType ())
//    s = tmp -> StrToVal 
//      (attrValue, CurrentUnderlyingType () -> Name (), err, im);
//  else
  istrstream paska((char *)attrValue);
  s = tmp -> STEPread (paska, err, im);
  delete tmp;
  return s;
}

Severity 
SdaiSelect::StrToVal(const char *Val, const char *selectType, 
		     ErrorDescriptor *err, InstMgr * instances)
{
    if (SetUnderlyingType (CanBe (selectType)))  

	//  the underlying type is set to a valid type
	// call read on underlying type in subclass

	switch (base_type)  {
	  case ENTITY_TYPE:
	  {  
	    STEPentity * tmp = 
	      ReadEntityRef (Val, err, ",)", instances, 0);
	    if( tmp && ( tmp != ENTITY_NULL ) && AssignEntity(tmp) ) 
	      return SEVERITY_NULL;  
	    else
	    {
		err->AppendToDetailMsg( 
		"Reference to entity that is not a valid type for SELECT.\n" );
		nullify ();	   
		err->GreaterSeverity( SEVERITY_WARNING );
		return SEVERITY_WARNING;
	    }	
	  }

	    // call StrToVal on the contents
	  case STRING_TYPE:
	  case AGGREGATE_TYPE:
	  case ARRAY_TYPE:		// DAS
	  case BAG_TYPE:		// DAS
	  case SET_TYPE:		// DAS
	  case LIST_TYPE:		// DAS
	  case ENUM_TYPE:
	  case SELECT_TYPE:
	  case BOOLEAN_TYPE:
	  case LOGICAL_TYPE:
	  {
	      err->GreaterSeverity( StrToVal_content (Val, instances) );
	      if(_error.severity() != SEVERITY_NULL)
		  err->AppendFromErrorArg(&_error);
	      return err->severity();
	  }

	    // do the same as STEPread_content
	  case BINARY_TYPE:
	  case NUMBER_TYPE:
	  case REAL_TYPE:
	  case INTEGER_TYPE:
	  default:
	  {
	    istrstream paska((char *)Val);
	      err->GreaterSeverity( STEPread_content( paska ) );
	      if(_error.severity() != SEVERITY_NULL)
		  err->AppendFromErrorArg(&_error);
	      return err->severity();
	  }
	}
    return SEVERITY_INPUT_ERROR;
}

Severity
SdaiSelect::STEPread  (istream& in, ErrorDescriptor *err, InstMgr * instances, 
		       int addFileId)
{
    char c ='\0';
    SCLstring tmp;

    // find out what case we have
    //  NOTE case C falls out of recursive calls in cases A and B

    in >> ws;
    in >> c;
//    c = in.peek();
    if( isalpha(c) ) //  case B 
    {
	//  token is a type name - get the type
      int eot =0;  // end of token flag
	while( (c != '(') && in.good() )
	{
	    if (!eot && ! (eot = isspace (c)) ) 
	      // as long as eot hasn\'t been reached keep appending
		tmp.Append(c);
	    in >> c; 
	}

	//  check for valid type and  set the underlying type 

	if( SetUnderlyingType( CanBe( tmp.chars() ) ) )
	{  //  assign the value to the underlying type
	    in >> ws; // skip white space
	    STEPread_content (in, instances, addFileId);
//  STEPread_content uses the ErrorDesc data member from the STEPselect class
	    err->AppendToDetailMsg (Error ());
	    err->GreaterSeverity( severity () );
	    in >> ws >> c;  
	    if( c != ')' )
	    {
		err->AppendToDetailMsg( 
				"Bad data or missing closing ')' for SELECT type.\n" );
		err->GreaterSeverity( SEVERITY_WARNING );
		in.putback (c);
		return SEVERITY_WARNING;
	    }
	    return err->severity();
	}
	else // ERROR  -- the type wasn't one of the choices
	{
	    if( !in.good() )
	    {
		err->GreaterSeverity( SEVERITY_INPUT_ERROR );
		return SEVERITY_INPUT_ERROR;
	    }
	    else
	    {
		err->AppendToDetailMsg (
			"The type name for the SELECT type is not valid.\n");
		err->GreaterSeverity (SEVERITY_WARNING);
		return SEVERITY_WARNING;
	    }
	}
/*	if (!in.good())
	{
	    err->GreaterSeverity( SEVERITY_INPUT_ERROR );
	    return SEVERITY_INPUT_ERROR;
	}
*/
    }
    else // case A
    {    //  the type can be determined from the value 
//      if (_type && ! _type -> UniqueElements ())  {
//	err->AppendToDetailMsg("Type for value of SELECT is ambiguous.\n" );
//	err->GreaterSeverity( SEVERITY_WARNING );
//      }
	switch (c)
	{
	  case '$':  
	    nullify ();
	    err->GreaterSeverity (SEVERITY_INCOMPLETE);
	    return SEVERITY_INCOMPLETE;

	  case ',':
	  case '\0':
	    // ERROR  IN INPUT
	    in.putback (c);
	    err->AppendToDetailMsg( "No value found for SELECT type.\n" );
	    err->GreaterSeverity( SEVERITY_WARNING );
	    return SEVERITY_WARNING;

	  case '.': // assign enum
	    base_type = ENUM_TYPE;
	    break;
	    // set the underlying type
	    // call STEPread
	    // return

	  case '\'': // assign string
	    base_type = STRING_TYPE;
	    break;

	  case '"': // assign string
	    base_type = BINARY_TYPE;
	    break;

	  case '#':
	    base_type = ENTITY_TYPE;
	    break;
	    // call STEPread_reference
	    // set the underlying type

	    // assign entity
	    // read the reference
	    // match type to underlying type
	    // assign the value
	    // set the underlying type

	  case '(':
	  {
	    char n;
	    in >> n;
	    in.putback(n);
	    if( isalpha(n) )  
		base_type = SELECT_TYPE;
	    else 
		base_type = AGGREGATE_TYPE;
	    in.putback( n );
	    break;
	  }

	  case '0':
	  case '1':
	  case '2':
	  case '3':
	  case '4':
	  case '5':
	  case '6':
	  case '7':
	  case '8':
	  case '9':
	  case '-':
	    if( CanBe( REAL_TYPE ) ) 
		base_type = REAL_TYPE;
	    else 	
		base_type = INTEGER_TYPE;
	    break;

	  default:
	    // ambiguous - ERROR:  underlying type should have been set
	    //	     STEPread_error ();
	    err->AppendToDetailMsg( 
		    "type for SELECT could not be determined from value.\n" );
	    nullify ();
	    in.putback (c);
	    err->GreaterSeverity( SEVERITY_WARNING );
	    return SEVERITY_WARNING;
	}

	in.putback (c);

	// now the type descriptor should be derivable from the base_type
	if( base_type == ENTITY_TYPE ) 
	{  // you don\'t know if this is an ENTITY or a SELECT
	    // have to do this here - not in STEPread_content
	    STEPentity * tmp = 
		ReadEntityRef (in, err, ",)", instances, addFileId);
	    if( tmp && ( tmp != ENTITY_NULL ) && AssignEntity(tmp) ) 
		return SEVERITY_NULL;  
	    else
	    {
		err->AppendToDetailMsg( 
		"Reference to entity that is not a valid type for SELECT.\n" );
		nullify ();	   
		err->GreaterSeverity( SEVERITY_WARNING );
		return SEVERITY_WARNING;
	    }	
	}
	else if ( SetUnderlyingType( CanBe(base_type) ) )
	    STEPread_content( in, instances, addFileId );

	else // ERROR  -- the type wasn\'t one of the choices
	{
	    err->AppendToDetailMsg( 
			    "The type of the SELECT type is not valid.\n");
	    err->GreaterSeverity( SEVERITY_WARNING );
	    return SEVERITY_WARNING;
	}
    }
//    if (!in.good())	severity (SEVERITY_INPUT_ERROR);
    return err->severity ();
}


void
SdaiSelect::STEPwrite(ostream& out)  const
{
    if (!exists ()) {  
	out << "$";
	return;
    }
    if (_type -> UniqueElements ())
	STEPwrite_content( out ); 
    else
    {
	SCLstring tmp;
	out << StrToUpper( CurrentUnderlyingType()->Name(), tmp ) << "(";
	STEPwrite_content (out); 
	out << ")";
    }
}

const char *
SdaiSelect::STEPwrite(SCLstring& s)  const
{
  strstream buf;
  STEPwrite (buf);
  buf << ends;  // have to add the terminating \0 char
  char * tmp;
  tmp = buf.str ();
  s = tmp;
  delete tmp;
  return s;
}


//SdaiSelect& 
//SdaiSelect::operator= (SdaiSelect& x)  
//{
//    return *this;
//}

#ifdef OBSOLETE
char* 
SdaiSelect::asStr()  const
{
  strstream buf;
  STEPwrite (buf);
/*  STEPwrite_content (buf);*/
  buf << ends;  // have to add the terminating \0 char
  return buf.str ();  // need to delete this space
}
#endif

int
SdaiSelect::set_null ()  
{
  nullify ();
    return 1;
}

int
SdaiSelect::is_null ()  
{
  return (!exists ());
}
