
/*
* NIST STEP Core Class Library
* clstepcore/sdaiSelect.cc
* April 1997
* Dave Helfrick
* KC Morris

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

/* $Id: sdaiSelect.cc,v 1.6 1997/11/05 21:59:16 sauderd DP3.1 $ */

#include <stdio.h> // to get the BUFSIZ #define
#include <ExpDict.h>
#include <sstream>
//#include <STEPentity.h>
#include <string>
#include <sdai.h>
#include <STEPattribute.h>

#ifdef  SCL_LOGGING 
#include <fstream.h>
    extern ofstream *logStream;
#endif 

/**********
	(member) functions for the select class SCLP23(Select)
**********/
SCLP23(Select)::SCLP23_NAME(Select) (const SelectTypeDescriptor *s, 
			const TypeDescriptor *td) 
	: _type (s), underlying_type (td)
{
#ifdef SCL_LOGGING
    *logStream << "Exiting SCLP23(Select) constructor." << endl;
#endif
}

SCLP23(Select)::~SCLP23_NAME(Select) ()
{
}

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

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

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

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

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

const TypeDescriptor *
SCLP23(Select)::CanBe (const char * n) const  
{
  return _type -> CanBe (n);
}


const TypeDescriptor *
SCLP23(Select)::CanBe (BASE_TYPE bt) const  
{
  const TypeDescLinkNode * tdn =
    (const TypeDescLinkNode *) _type -> GetElements ().GetHead ();
  const TypeDescriptor *td = tdn -> TypeDesc ();
  BASE_TYPE bt_thisnode;
  
  while (tdn)  {
    td = tdn -> TypeDesc ();
    if (((bt_thisnode = td -> NonRefType ()) == bt) ||
        (bt == AGGREGATE_TYPE && ((bt_thisnode == ARRAY_TYPE) ||
                                  (bt_thisnode == LIST_TYPE) ||
                                  (bt_thisnode == SET_TYPE) ||
                                  (bt_thisnode == BAG_TYPE))))
      return td;  // they are the same 
    tdn = (TypeDescLinkNode *) (tdn -> NextNode ());
  }
  return 0;
}

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

const TypeDescriptor *
SCLP23(Select)::CanBeSet (const char * n, const char *schnm) const  
{
  return _type -> CanBeSet (n, schnm);
}

const int
SCLP23(Select)::IsUnique (const BASE_TYPE bt) const
{
  if (bt == ARRAY_TYPE ||
      bt == LIST_TYPE ||
      bt == BAG_TYPE ||
      bt == SET_TYPE)
      return ((_type->UniqueElements()) & AGGREGATE_TYPE);
  else
      return ((_type->UniqueElements()) & bt);
}


SCLP23(String) SCLP23(Select)::UnderlyingTypeName () const  
{
  return underlying_type -> Name ();
}

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

const TypeDescriptor * 
SCLP23(Select)::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;
}

bool SCLP23(Select)::exists() const 
{
  // instead of returning the address as 'true' or NULL as false, lets reduce
  // this down to a bool.
  return underlying_type != NULL;
}

void SCLP23(Select)::nullify() 
{
  underlying_type = 0;
}

Severity 
SCLP23(Select)::SelectValidLevel(const char *attrValue, ErrorDescriptor *err, 
			     InstMgr *im, int clearError)
{
  SCLP23(Select) * 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 

  istringstream strtmp (attrValue);
  s = tmp -> STEPread (strtmp, err, im);
  delete tmp;
  return s;
}

Severity 
SCLP23(Select)::StrToVal(const char *Val, const char *selectType, 
		     ErrorDescriptor *err, InstMgr * instances)
{
    severity(SEVERITY_NULL);
    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();  
	    }
	    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:
	  {
	      istringstream strtmp (Val);
	      err->GreaterSeverity( STEPread_content (strtmp) );
	      if(_error.severity() != SEVERITY_NULL)
		  err->AppendFromErrorArg(&_error);
	      return err->severity();
	  }
	}
    return SEVERITY_INPUT_ERROR;
}

// updated to Technical Corrigendum. DAS 2/4/97
Severity
SCLP23(Select)::STEPread (istream& in, ErrorDescriptor *err,
			  InstMgr *instances, const char* utype,
			  int addFileId, const char *currSch)
{
    char c ='\0';
    std::string tmp;

#ifdef SCL_LOGGING
//    *logStream << "DAVE ERR Entering SCLP23(Select)::STEPread." << endl;
#endif
    // find out what case we have
    //  NOTE case C falls out of recursive calls in cases A and B

    // This section of code is used to read a value belonging to a select 
    // contained in another select. If you have read the text part of the
    // TYPED_PARAMETER and it needs to fall down thru some levels of contained
    // select types, then the text is passed down to each select in the utype 
    // parameter as STEPread is called on each contained select type.DAS 2/4/97
    if(utype)
    {
	if( SetUnderlyingType( CanBeSet( utype, currSch ) ) )
	{  //  assign the value to the underlying type
	    in >> ws; // skip white space
	    if( (underlying_type->Type() == REFERENCE_TYPE) &&
	        (underlying_type->NonRefType() == sdaiSELECT) )
	    {   // See comments below for a similar code segment.
		STEPread_content (in, instances, 0, addFileId, currSch);
	    }
	    else
	      STEPread_content (in, instances, utype, addFileId, currSch);
	    err->AppendToDetailMsg (Error ());
	    err->GreaterSeverity( severity () );
	}
	return err->severity();
    }
    in >> ws;
    in >> c;
//  c = in.peek();

    // the if part of this code reads a value according to the Technical 
    // Corrigendum for Part 21 (except for Entity instance refs which are read
    // in the else part - everything else in the else stmt is invalid). The
    // idea here is to read text and find out if it is specifying the final
    // type or is a simple defined type which is typed to be a select some 
    // number of levels down. The first case will cause the value to be read
    // directly by STEPread_content() or will cause STEPread_content() to pass
    // the text naming the type to the underlying Select containing it. When
    // the latter is the case, STEPread_content calls STEPread for the 
    // contained select and passes in the text. The convoluted case is when
    // the text describes a simple defined type that translates directly into
    // a select some number of levels down. In this case there will be more 
    // text inside the containing parens to indicate the type. Since the text
    // specifying the type still needs to be read utype is passed as null which
    // will cause the contained Select STEPread function to read it. DAS 2/4/97

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

	//  check for valid type and set the underlying type
	if( SetUnderlyingType( CanBeSet( tmp.c_str(), currSch ) ) )
	{   // Assign the value to the underlying type.  CanBeSet() is a
	    // slightly modified CanBe().  It ensures that a renamed select
	    // type (see big comment below) "CantBe" one of its member items.
	    // This is because such a select type requires its own name to
	    // appear first ("selX("), so even if we read a valid element of
	    // selX, selX can't be the underlying type.  That can only be the
	    // case if "selX" appears first and is what we just read.
	    in >> ws; // skip white space
	    if( (underlying_type->Type() == REFERENCE_TYPE) &&
	        (underlying_type->NonRefType() == sdaiSELECT) )
	    {
		// This means (1) that the underlying type is itself a select
		// (cond 2), and (2) it's not defined in the EXPRESS as a
		// select, but as a renamed select.  (E.g., TYPE sel2 = sel1.)
		// In such a case, according to the TC, "sel2(" must appear in
		// the text.  (We must have found one for SetUnderlyingType()
		// above to set underlying_type to sel2.)  If all of the above
		// is true, we read "sel2" but not the value of sel2.  We
		// therefore do not pass down what we read to STEPread_content
		// below, so that we can still read the value of sel2.  If,
		// however, under_type was a non-renamed select, no "sel1" 
		// would appear (according to TC) and we already read the value
		// of sel1.  If so, we pass the already-read value down.
		STEPread_content (in, instances, 0, addFileId, currSch);
	    }
	    else
	    {
		// In most cases (see above note), we've already read the value
		// we're interested in.
		// This also handles all other cases? other than the if part
		// above and elements of type entity ref?
		STEPread_content (in, instances, tmp.c_str(), addFileId,
				  currSch);
		// STEPread_content uses the ErrorDesc data member from the
		// SCLP23(Select) 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);
#ifdef SCL_LOGGING
//    *logStream << "DAVE ERR Exiting SCLP23(Select)::STEPread for " << _type->Name() << endl;
#endif
		return SEVERITY_WARNING;
	    }
#ifdef SCL_LOGGING
//    *logStream << "DAVE ERR Exiting SCLP23(Select)::STEPread for " << _type->Name() << endl;
#endif
	    return err->severity();
	}
	else // ERROR  -- the type wasn't one of the choices
	{
	    if( !in.good() )
	    {
		err->GreaterSeverity( SEVERITY_INPUT_ERROR );
#ifdef SCL_LOGGING
//    *logStream << "DAVE ERR Exiting SCLP23(Select)::STEPread for " << _type->Name() << endl;
#endif
		return SEVERITY_INPUT_ERROR;
	    }
	    else
	    {
		err->AppendToDetailMsg (
			"The type name for the SELECT type is not valid.\n");
		err->GreaterSeverity (SEVERITY_WARNING);
#ifdef SCL_LOGGING
//    *logStream << "DAVE ERR Exiting SCLP23(Select)::STEPread for " << _type->Name() << endl;
#endif
		return SEVERITY_WARNING;
	    }
	}
/*	if (!in.good())
	{
	    err->GreaterSeverity( SEVERITY_INPUT_ERROR );
	    return SEVERITY_INPUT_ERROR;
	}
*/
    }
// NOTE **** anything that gets read below here is invalid according to the 
// Technical Corrigendum for Part 21 (except for reading an entity instance 
// where below is the only place they get read). I am leaving all of this
// here since it is valuable to 'read what you can' and flag an error which
// is what it does (for every type but entity instance refs). DAS 2/4/97

    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);
#ifdef SCL_LOGGING
//    *logStream << "DAVE ERR Exiting SCLP23(Select)::STEPread for " << _type->Name() << endl;
#endif
	    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 );
#ifdef SCL_LOGGING
//    *logStream << "DAVE ERR Exiting SCLP23(Select)::STEPread for " << _type->Name() << endl;
#endif
	    return SEVERITY_WARNING;

	  case '.': // assign enum
	    base_type = ENUM_TYPE;
	    err->AppendToDetailMsg( "Invalid Enumeration, Logical, or Boolean value in SELECT type.\n" );
	    err->GreaterSeverity( SEVERITY_WARNING );
	    break;
	    // set the underlying type
	    // call STEPread
	    // return

	  case '\'': // assign string
	    base_type = STRING_TYPE;
	    err->AppendToDetailMsg( "Invalid String value in SELECT type.\n" );
	    err->GreaterSeverity( SEVERITY_WARNING );
	    break;

	  case '"': // assign string
	    base_type = BINARY_TYPE;
	    err->AppendToDetailMsg( "Invalid Binary value in SELECT type.\n" );
	    err->GreaterSeverity( SEVERITY_WARNING );
	    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 '(':
	  {
	    err->AppendToDetailMsg( "Invalid aggregate value in SELECT type.\n" );
	    err->GreaterSeverity( SEVERITY_WARNING );
	    char n;
	    in >> n;
	    in.putback(n);
	    if( isalpha(n) )  
		base_type = SELECT_TYPE;
	    else 
		base_type = AGGREGATE_TYPE;
	    break;
	  }

	  case '0':
	  case '1':
	  case '2':
	  case '3':
	  case '4':
	  case '5':
	  case '6':
	  case '7':
	  case '8':
	  case '9':
	  case '-':
	    err->AppendToDetailMsg( "Invalid Integer or Real value in SELECT type.\n" );
	    err->GreaterSeverity( SEVERITY_WARNING );
	    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 );
#ifdef SCL_LOGGING
//    *logStream << "DAVE ERR Exiting SCLP23(Select)::STEPread for " << _type->Name() << endl;
#endif
	    return SEVERITY_WARNING;
	}

	in.putback (c);

	// now the type descriptor should be derivable from the base_type

	// if it\'s not issue a warning
	if (_type && ! (IsUnique(base_type)))  {
	  err->AppendToDetailMsg("Value for SELECT will be assigned to first possible choice.\n" );
//	  err->GreaterSeverity( SEVERITY_INCOMPLETE );
	  err->GreaterSeverity( SEVERITY_USERMSG );
	}

	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) ) 
	    {
#ifdef SCL_LOGGING
//    *logStream << "DAVE ERR Exiting SCLP23(Select)::STEPread for " << _type->Name() << endl;
#endif
		return SEVERITY_NULL;  
            }
	    else
	    {
		err->AppendToDetailMsg( 
		"Reference to entity that is not a valid type for SELECT.\n" );
		nullify ();	   
		err->GreaterSeverity( SEVERITY_WARNING );
#ifdef SCL_LOGGING
//    *logStream << "DAVE ERR Exiting SCLP23(Select)::STEPread for " << _type->Name() << endl;
#endif
		return SEVERITY_WARNING;
	    }	
	}
	else if ( SetUnderlyingType( CanBe(base_type) ) )
	    STEPread_content( in, instances, 0, 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 );
#ifdef SCL_LOGGING
//    *logStream << "DAVE ERR Exiting SCLP23(Select)::STEPread for " << _type->Name() << endl;
#endif
	    return SEVERITY_WARNING;
	}
    }
//    if (!in.good())	severity (SEVERITY_INPUT_ERROR);
#ifdef SCL_LOGGING
//    *logStream << "DAVE ERR Exiting SCLP23(Select)::STEPread for " << _type->Name() << endl;
#endif
    return err->severity ();
}


// updated to Technical Corrigendum DAS Feb 4, 1997
void
SCLP23(Select)::STEPwrite(ostream& out, const char *currSch)  const
{
    if (!exists ()) {
	out << "$";
	return;
    }
//    switch(ValueType()) // This doesn't work DAS 2/4/97
    switch(underlying_type->NonRefType())
    {
	case sdaiINSTANCE:
	{
	    STEPwrite_content( out );
	    break;
	}
	case sdaiSELECT: // The name of a select is never written DAS 1/31/97
	{
	    if(underlying_type->Type() == REFERENCE_TYPE)
	    {
//		if(underlying_type->NonRefType() == sdaiSELECT)
        std::string s;
		    out << StrToUpper(underlying_type->Name(currSch),s) << "(";
		    STEPwrite_content( out, currSch );
		    out << ")";
	    }
	    else
	      STEPwrite_content( out, currSch );
	    break;
	}
	case sdaiNUMBER:
	case sdaiREAL:
	case sdaiINTEGER:
	case sdaiSTRING:
	case sdaiBOOLEAN:
	case sdaiLOGICAL:
	case sdaiBINARY:
	case sdaiENUMERATION:
	case sdaiAGGR:
	case ARRAY_TYPE:
	case BAG_TYPE:
	case SET_TYPE:
	case LIST_TYPE:
	{
	    STEPwrite_verbose( out, currSch );
	    break;
	}
	case REFERENCE_TYPE: // this should never happen? DAS
	default:
	  out << "ERROR Should not have gone here in SCLP23(Select)::STEPwrite()"
	      << endl;
      }
}

void
SCLP23(Select)::STEPwrite_verbose(ostream& out, const char *currSch) const
{
  std::string tmp;
    out << StrToUpper( CurrentUnderlyingType()->Name(currSch), tmp) << "(";
    STEPwrite_content (out);
    out << ")";
}

const char *
SCLP23(Select)::STEPwrite(std::string& s, const char *currSch)  const
{
  ostringstream buf;
  STEPwrite (buf, currSch);
  buf << ends;  // have to add the terminating \0 char
  char * tmp;
  /*** tmp = buf.str (); ***/ tmp = &(buf.str()[0]);
  s = tmp;
  delete tmp;
  return const_cast<char *>(s.c_str());
}


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

#ifdef OBSOLETE
char* 
SCLP23(Select)::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
SCLP23(Select)::set_null ()  
{
  nullify ();
    return 1;
}

int
SCLP23(Select)::is_null ()  
{
  return (!exists ());
}
