/* fieldValue.C */

#include "xrmlP.H"
#include "ExternProto.H"
#include "List.H"
#include "Assoc.H"
#include "file.H"

namespace xrml {

PoolImpl(fieldValue)

PoolImpl(SFBool)
PoolImpl(SFString)
PoolImpl(SFInt32)
PoolImpl(SFFloat)
PoolImpl(SFTime)
PoolImpl(SFVec2f)
PoolImpl(SFVec3f)
PoolImpl(SFColor)
PoolImpl(SFRotation)

PoolImpl(MFBool)
PoolImpl(MFString)
PoolImpl(MFInt32)
PoolImpl(MFFloat)
PoolImpl(MFTime)
PoolImpl(MFVec2f)
PoolImpl(MFVec3f)
PoolImpl(MFColor)
PoolImpl(MFRotation)

const char *fieldTypeName(const fieldType& type)
{
  return (type == tdummy ? "dummy" :

	 (type == tSFBool ? "SFBool" :
	  (type == tSFString ? "SFString" :
	   (type == tSFInt32 ? "SFInt32" :
	    (type == tSFFloat ? "SFFloat" :
	     (type == tSFTime ? "SFTime" :
	      (type == tSFVec2f ? "SFVec2f" :
	       (type == tSFVec3f ? "SFVec3f" :
		(type == tSFColor ? "SFColor" :
		 (type == tSFRotation ? "SFRotation" :
		  (type == tSFImage ? "SFImage" :
		   (type == tSFNode ? "SFNode" :

	   (type == tMFBool ? "MFBool" :
	    (type == tMFString ? "MFString" :
	     (type == tMFInt32 ? "MFInt32" :
	      (type == tMFFloat ? "MFFloat" :
	       (type == tMFTime ? "MFTime" :
		(type == tMFVec2f ? "MFVec2f" :
		 (type == tMFVec3f ? "MFVec3f" :
		  (type == tMFColor ? "MFColor" :
		   (type == tMFRotation ? "MFRotation" :
		    (type == tMFImage ? "MFImage" :
		     (type == tMFNode ? "MFNode" :

		      "invalid" )))))))))))))))))))))));
}

fieldValue::fieldValue(fieldType type)
{
  fieldValue::type = type;
  switch (type) {
  case tSFBool:		value.sfbool = new SFBool; break;
  case tSFString:	value.sfstring = new SFString; break;
  case tSFInt32:	value.sfint32 = new SFInt32; break;
  case tSFFloat:	value.sffloat = new SFFloat; break;
  case tSFTime:		value.sftime = new SFTime; break;
  case tSFVec2f:	value.sfvec2f = new SFVec2f; break;
  case tSFVec3f:	value.sfvec3f = new SFVec3f; break;
  case tSFColor:	value.sfcolor = new SFColor; break;
  case tSFRotation:	value.sfrotation = new SFRotation; break;
  case tSFImage:	value.sfimage = new SFImage; break;
  case tSFNode:		value.sfnode = new SFNode* (0); break;
  case tMFBool:		value.mfbool = new MFBool; break;
  case tMFString:	value.mfstring = new MFString; break;
  case tMFInt32:	value.mfint32 = new MFInt32; break;
  case tMFFloat:	value.mffloat = new MFFloat; break;
  case tMFTime:		value.mftime = new MFTime; break;
  case tMFVec2f:	value.mfvec2f = new MFVec2f; break;
  case tMFVec3f:	value.mfvec3f = new MFVec3f; break;
  case tMFColor:	value.mfcolor = new MFColor; break;
  case tMFRotation:	value.mfrotation = new MFRotation; break;
  case tMFImage:	value.mfimage = new MFImage; break;
  case tMFNode:		value.mfnode = new MFNode; break;
  case tdummy:		value.any = 0; break;;
  default:		Fatal(-1, "fieldValue::fieldValue", "invalid type '%d'", type);
  }
}

void fieldValue::destroy(void)
{
  switch (type) {
  case tSFBool:		delete value.sfbool; break;
  case tSFString:	delete value.sfstring; break;
  case tSFInt32:	delete value.sfint32; break;
  case tSFFloat:	delete value.sffloat; break;
  case tSFTime:		delete value.sftime; break;
  case tSFVec2f:	delete value.sfvec2f; break;
  case tSFVec3f:	delete value.sfvec3f; break;
  case tSFColor:	delete value.sfcolor; break;
  case tSFRotation:	delete value.sfrotation; break;
  case tSFImage:	delete value.sfimage; break;
  case tSFNode:		delete value.sfnode; break;
  case tMFBool:		delete value.mfbool; break;
  case tMFString:	delete value.mfstring; break;
  case tMFInt32:	delete value.mfint32; break;
  case tMFFloat:	delete value.mffloat; break;
  case tMFTime:		delete value.mftime; break;
  case tMFVec2f:	delete value.mfvec2f; break;
  case tMFVec3f:	delete value.mfvec3f; break;
  case tMFColor:	delete value.mfcolor; break;
  case tMFRotation:	delete value.mfrotation; break;
  case tMFImage:	delete value.mfimage; break;
  case tMFNode:		delete value.mfnode; break;
  case tdummy:		delete (char*)value.any; break;
  default:		Fatal(-1, "fieldValue::destroy", "invalid type '%d'", type);
  }
}

ostream& operator<<(ostream& s, const fieldValueBase& v)
{
  //  s << "(addr " << v.value.any << ") ";
  if (v.value.any == 0)  return s << "nil";

  switch (v.type) {
  case tSFBool:		return s << *v.value.sfbool; 
  case tSFString:	return s << *v.value.sfstring; 
  case tSFInt32:	return s << *v.value.sfint32; 
  case tSFFloat:	return s << *v.value.sffloat; 
  case tSFTime:		return s << *v.value.sftime; 
  case tSFVec2f:	return s << *v.value.sfvec2f; 
  case tSFVec3f:	return s << *v.value.sfvec3f; 
  case tSFColor:	return s << *v.value.sfcolor; 
  case tSFRotation:	return s << *v.value.sfrotation; 
  case tSFImage:	return s << *v.value.sfimage; 
  case tSFNode:		return s << *v.value.sfnode; 
  case tMFBool:		return s << *v.value.mfbool; 
  case tMFString:	return s << *v.value.mfstring; 
  case tMFInt32:	return s << *v.value.mfint32; 
  case tMFFloat:	return s << *v.value.mffloat; 
  case tMFTime:		return s << *v.value.mftime; 
  case tMFVec2f:	return s << *v.value.mfvec2f; 
  case tMFVec3f:	return s << *v.value.mfvec3f; 
  case tMFColor:	return s << *v.value.mfcolor; 
  case tMFRotation:	return s << *v.value.mfrotation; 
  case tMFImage:	return s << *v.value.mfimage; 
  case tMFNode:		return s << *v.value.mfnode; 
  default:		Fatal(-1, "operator<<(ostream&, fieldValueBase&)", "invalid type '%d'", v.type); return s << "BOEM!\n";
  }
}

void fieldValue::duplicate(const fieldValue& v)
{
  assert(v.value.any);
  check(v.type);

  switch (type) {	// type == v.type, we checked that.
  case tSFBool:		value.sfbool = new SFBool(*v.value.sfbool); break;
  case tSFString:	value.sfstring = new SFString(*v.value.sfstring); break;
  case tSFInt32:	value.sfint32 = new SFInt32(*v.value.sfint32); break;
  case tSFFloat:	value.sffloat = new SFFloat(*v.value.sffloat); break;
  case tSFTime:		value.sftime = new SFTime(*v.value.sftime); break;
  case tSFVec2f:	value.sfvec2f = new SFVec2f(*v.value.sfvec2f); break;
  case tSFVec3f:	value.sfvec3f = new SFVec3f(*v.value.sfvec3f); break;
  case tSFColor:	value.sfcolor = new SFColor(*v.value.sfcolor); break;
  case tSFRotation:	value.sfrotation = new SFRotation(*v.value.sfrotation); break;
  case tSFImage:	value.sfimage = new SFImage(*v.value.sfimage); break;
  case tSFNode:		value.sfnode = new SFNode*(*v.value.sfnode); break;
  case tMFBool:		value.mfbool = new MFBool(*v.value.mfbool); break;
  case tMFString:	value.mfstring = new MFString(*v.value.mfstring); break;
  case tMFInt32:	value.mfint32 = new MFInt32(*v.value.mfint32); break;
  case tMFFloat:	value.mffloat = new MFFloat(*v.value.mffloat); break;
  case tMFTime:		value.mftime = new MFTime(*v.value.mftime); break;
  case tMFVec2f:	value.mfvec2f = new MFVec2f(*v.value.mfvec2f); break;
  case tMFVec3f:	value.mfvec3f = new MFVec3f(*v.value.mfvec3f); break;
  case tMFColor:	value.mfcolor = new MFColor(*v.value.mfcolor); break;
  case tMFRotation:	value.mfrotation = new MFRotation(*v.value.mfrotation); break;
  case tMFImage:	value.mfimage = new MFImage(*v.value.mfimage); break;
  case tMFNode:		value.mfnode = new MFNode(*v.value.mfnode); break;
  default:		Fatal(-1, "fieldValue::duplicate", "invalid type '%d'", type);
  }
}

void fieldValue::instantiate(Proto *master)
{
  assert(value.any);

  switch (type) {
  case tSFNode:		
    if (*value.sfnode) *value.sfnode = (*value.sfnode)->instantiate(master); 
    break;
  case tMFNode:		
    *value.mfnode = *value.mfnode->instantiate(master); 
    break;
  default:
    break;
  }
}

void fieldValue::assign(const fieldValue& v)
{
  assert(v.value.any && value.any);
  check(v.type);

  switch (type) {
  case tSFBool:		*value.sfbool = *v.value.sfbool; break;
  case tSFString:	*value.sfstring = *v.value.sfstring; break;
  case tSFInt32:	*value.sfint32 = *v.value.sfint32; break;
  case tSFFloat:	*value.sffloat = *v.value.sffloat; break;
  case tSFTime:		*value.sftime = *v.value.sftime; break;
  case tSFVec2f:	*value.sfvec2f = *v.value.sfvec2f; break;
  case tSFVec3f:	*value.sfvec3f = *v.value.sfvec3f; break;
  case tSFColor:	*value.sfcolor = *v.value.sfcolor; break;
  case tSFRotation:	*value.sfrotation = *v.value.sfrotation; break;
  case tSFImage:	*value.sfimage = *v.value.sfimage; break;
  case tSFNode:		*value.sfnode = *v.value.sfnode; break;
  case tMFBool:		*value.mfbool = *v.value.mfbool; break;
  case tMFString:	*value.mfstring = *v.value.mfstring; break;
  case tMFInt32:	*value.mfint32 = *v.value.mfint32; break;
  case tMFFloat:	*value.mffloat = *v.value.mffloat; break;
  case tMFTime:		*value.mftime = *v.value.mftime; break;
  case tMFVec2f:	*value.mfvec2f = *v.value.mfvec2f; break;
  case tMFVec3f:	*value.mfvec3f = *v.value.mfvec3f; break;
  case tMFColor:	*value.mfcolor = *v.value.mfcolor; break;
  case tMFRotation:	*value.mfrotation = *v.value.mfrotation; break;
  case tMFImage:	*value.mfimage = *v.value.mfimage; break;
  case tMFNode:		*value.mfnode = *v.value.mfnode; break;
  default:		Fatal(-1, "fieldValue::assign", "invalid type '%d'", type);
  }
}

#ifdef NEVER
void SFBool::save(class file* fh)
{
  fh->printf("%s", val ? "TRUE" : "FALSE");
}

void SFString::save(class file* fh)
{
  fh->printf("\"%s\"", val);
}

void SFInt32::save(class file* fh)
{
  fh->printf("%d", val);
}

void SFFloat::save(class file* fh)
{
  fh->printf("%g", val);
}

void SFTime::save(class file* fh)
{
  fh->printf("%lg", val);
}

void SFVec2f::save(class file* fh)
{
  fh->printf("%g %g", s, t);
}

void SFVec3f::save(class file* fh)
{
  fh->printf("%g %g %g", x, y, z);
}

void SFColor::save(class file* fh)
{
  fh->printf("%g %g %g", r, g, b);
}

void SFRotation::save(class file* fh)
{
  fh->printf("%g %g %g %g", x, y, z, radians);
}
#endif

void fieldValue::save(class file* fh)
{
  assert(value.any);
#ifdef NEVER
  switch (type) {
  case tSFBool:		value.sfbool->save(fh); break;
  case tSFString:	value.sfstring->save(fh); break;
  case tSFInt32:	value.sfint32->save(fh); break;
  case tSFFloat:	value.sffloat->save(fh); break;
  case tSFTime:		value.sftime->save(fh); break;
  case tSFVec2f:	value.sfvec2f->save(fh); break;
  case tSFVec3f:	value.sfvec3f->save(fh); break;
  case tSFColor:	value.sfcolor->save(fh); break;
  case tSFRotation:	value.sfrotation->save(fh); break;
  case tSFImage:	value.sfimage->save(fh); break;
  case tSFNode:		if (*value.sfnode != 0) { (*value.sfnode)->save(fh); } else { fh->printf("NULL"); } break;
  case tMFBool:		value.mfbool->save(fh); break;
  case tMFString:	value.mfstring->save(fh); break;
  case tMFInt32:	value.mfint32->save(fh); break;
  case tMFFloat:	value.mffloat->save(fh); break;
  case tMFTime:		value.mftime->save(fh); break;
  case tMFVec2f:	value.mfvec2f->save(fh); break;
  case tMFVec3f:	value.mfvec3f->save(fh); break;
  case tMFColor:	value.mfcolor->save(fh); break;
  case tMFRotation:	value.mfrotation->save(fh); break;
  case tMFImage:	value.mfimage->save(fh); break;
  case tMFNode:		value.mfnode->save(fh); break;
  default:		Fatal(-1, "fieldValue::assign", "invalid type '%d'", type);
  }
#endif
}

///////////////////////////////////////////////////////////////////////////////
// [S|M]FNode value checking

bool checkValue(MFNode& nodes,
		bool (*checkType)(SFNode*),
		const char *fieldname, const char *typeclassname)
{
  int i, j, nilcount = 0;
  for (i=0; i<nodes.size; i++) {
    if (!nodes[i]) {
      Error(NULL, "NULL nodes are not allowed in MFNode arrays");
      nilcount++;
    } else if (!IsUnresolvedExternProto(nodes[i]) && !checkType(nodes[i]->firstnode())) {
      Error(NULL, "%s children node %s (type '%s') should be a %s node", 
	    fieldname, nodes[i]->name(), nodes[i]->firstnode()->typeId, typeclassname);
      nodes[i] = (SFNode *)0;
      nilcount++;
    }
  }

  if (nilcount>0) {
    // remove nil pointers
    MFNode newnodes(nodes.size);
    for (i=0, j=0; i<nodes.size; i++)
      if (nodes[i] != 0) newnodes[j++] = nodes[i];
    nodes = newnodes;
  }

  //  for (i=0; i<nodes.size; i++) {
  //  if (IsUnresolvedExternProto(nodes[i])) {
      // such situations result naturally while resolving EXTERNPROTO's,
      // because EXTERNPROTO's in files being parsed in order to find
      // the implementation of an EXTERNPROTO are not automatically
      // resolved. See ExternProto::resolve().
      // Warning(NULL, "%s implementation is missing", nodes[i]->name());
  //  }
  //}

  return true;
}

bool checkValue(SFNode*& node,
		bool (*checkType)(SFNode*),
		const char *fieldname, const char *typeclassname)
{
  if (!node)
    return true;

  else if (!IsUnresolvedExternProto(node) && !checkType(node->firstnode())) {
    Error(NULL, "%s children node %s (type '%s') should be a %s node",
	  fieldname, node->name(), node->firstnode()->typeId, typeclassname);
    node = (SFNode *)0;
    return false;
  }

  if (IsUnresolvedExternProto(node)) {
    // schedule a task for re-assigning the node after re-instantiation
    //    Warning(NULL, "%s implementation is missing", node->name());
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
// [S|M]FInt32 value checking

bool checkValue(const SFInt32& value, class SFNode* node, const char *fieldId,
		bool (*op)(const SFInt32&, const SFInt32&), const char *opname, const SFInt32& threshold)
{
  if (!op(value, threshold)) {
    Error(NULL, "%s.%s value %d out of range: shall be %s %d",
	  node->name(), fieldId, (int)value, opname, (int)threshold);
    return false;
  }
  return true;
}

bool checkValue(const SFInt32& value, class SFNode* node, const char *fieldId,
		bool (*op1)(const SFInt32&, const SFInt32&), const char *op1name, const SFInt32& threshold1,
		bool (*op2)(const SFInt32&, const SFInt32&), const char *op2name, const SFInt32& threshold2)
{
  if (!op1(value, threshold1) || !op2(value, threshold2)) {
    Error(NULL, "%s.%s value %d out of range: shall be %s %d and %s %d",
	  node->name(), fieldId, (int)value, 
	  op1name, (int)threshold1, op2name, (int)threshold2);
    return false;
  }
  return true;
}

bool checkValue(const MFInt32& value, class SFNode* node, const char *fieldId,
		bool (*op)(const SFInt32&, const SFInt32&), const char *opname, const SFInt32& threshold)
{
  bool good = true;
  for (int i=0; i<value.size; i++)
    good = good && checkValue(value[i], node, fieldId, op, opname, threshold);
  return good;
}

bool checkValue(const MFInt32& value, class SFNode* node, const char *fieldId,
		bool (*op1)(const SFInt32&, const SFInt32&), const char *op1name, const SFInt32& threshold1,
		bool (*op2)(const SFInt32&, const SFInt32&), const char *op2name, const SFInt32& threshold2)
{
  bool good = true;
  for (int i=0; i<value.size; i++)
    good = good && checkValue(value[i], node, fieldId, op1, op1name, threshold1, op2, op2name, threshold2);
  return good;
}

///////////////////////////////////////////////////////////////////////////////
// [S|M]FFloat value checking

bool checkValue(const SFFloat& value, class SFNode* node, const char *fieldId,
		bool (*op)(const SFFloat&, const SFFloat&), const char *opname, const SFFloat& threshold)
{
  if (!op(value, threshold)) {
    Error(NULL, "%s.%s value %g out of range: shall be %s %g",
	  node->name(), fieldId, (float)value, opname, (float)threshold);
    return false;
  }
  return true;
}

bool checkValue(const SFFloat& value, class SFNode* node, const char *fieldId,
		bool (*op1)(const SFFloat&, const SFFloat&), const char *op1name, const SFFloat& threshold1,
		bool (*op2)(const SFFloat&, const SFFloat&), const char *op2name, const SFFloat& threshold2)
{
  if (!op1(value, threshold1) || !op2(value, threshold2)) {
    Error(NULL, "%s.%s value %g out of range: shall be %s %g and %s %g",
	  node->name(), fieldId, (float)value, 
	  op1name, (float)threshold1, op2name, (float)threshold2);
    return false;
  }
  return true;
}

bool checkValue(const MFFloat& value, class SFNode* node, const char *fieldId,
		bool (*op)(const SFFloat&, const SFFloat&), const char *opname, const SFFloat& threshold)
{
  bool good = true;
  for (int i=0; i<value.size; i++)
    good = good && checkValue(value[i], node, fieldId, op, opname, threshold);
  return good;
}

bool checkValue(const MFFloat& value, class SFNode* node, const char *fieldId,
		bool (*op1)(const SFFloat&, const SFFloat&), const char *op1name, const SFFloat& threshold1,
		bool (*op2)(const SFFloat&, const SFFloat&), const char *op2name, const SFFloat& threshold2)
{
  bool good = true;
  for (int i=0; i<value.size; i++)
    good = good && checkValue(value[i], node, fieldId, op1, op1name, threshold1, op2, op2name, threshold2);
  return good;
}

///////////////////////////////////////////////////////////////////////////////
// [S|M]FTime value checking

bool checkValue(const SFTime& value, class SFNode* node, const char *fieldId,
		bool (*op)(const SFTime&, const SFTime&), const char *opname, const SFTime& threshold)
{
  if (!op(value, threshold)) {
    Error(NULL, "%s.%s value %lg out of range: shall be %s %lg",
	  node->name(), fieldId, (double)value, opname, (double)threshold);
    return false;
  }
  return true;
}

bool checkValue(const SFTime& value, class SFNode* node, const char *fieldId,
		bool (*op1)(const SFTime&, const SFTime&), const char *op1name, const SFTime& threshold1,
		bool (*op2)(const SFTime&, const SFTime&), const char *op2name, const SFTime& threshold2)
{
  if (!op1(value, threshold1) || !op2(value, threshold2)) {
    Error(NULL, "%s.%s value %lg out of range: shall be %s %lg and %s %lg",
	  node->name(), fieldId, (double)value, 
	  op1name, (double)threshold1, op2name, (double)threshold2);
    return false;
  }
  return true;
}

bool checkValue(const MFTime& value, class SFNode* node, const char *fieldId,
		bool (*op)(const SFTime&, const SFTime&), const char *opname, const SFTime& threshold)
{
  bool good = true;
  for (int i=0; i<value.size; i++)
    good = good && checkValue(value[i], node, fieldId, op, opname, threshold);
  return good;
}

bool checkValue(const MFTime& value, class SFNode* node, const char *fieldId,
		bool (*op1)(const SFTime&, const SFTime&), const char *op1name, const SFTime& threshold1,
		bool (*op2)(const SFTime&, const SFTime&), const char *op2name, const SFTime& threshold2)
{
  bool good = true;
  for (int i=0; i<value.size; i++)
    good = good && checkValue(value[i], node, fieldId, op1, op1name, threshold1, op2, op2name, threshold2);
  return good;
}

///////////////////////////////////////////////////////////////////////////////
// [S|M]FVec2f value checking

bool checkValue(const SFVec2f& value, class SFNode* node, const char *fieldId,
		bool (*op)(const SFVec2f&, const SFVec2f&), const char *opname, const SFVec2f& threshold)
{
  if (!op(value, threshold)) {
    Error(NULL, "%s.%s value %g %g out of range: shall be %s %g %g",
	  node->name(), fieldId, value.s, value.t, opname, threshold.s, threshold.t);
    return false;
  }
  return true;
}

bool checkValue(const SFVec2f& value, class SFNode* node, const char *fieldId,
		bool (*op1)(const SFVec2f&, const SFVec2f&), const char *op1name, const SFVec2f& threshold1,
		bool (*op2)(const SFVec2f&, const SFVec2f&), const char *op2name, const SFVec2f& threshold2)
{
  if (!op1(value, threshold1) || !op2(value, threshold2)) {
    Error(NULL, "%s.%s value %g %g out of range: shall be %s %g %g and %s %g %g",
	  node->name(), fieldId, 
	  value.s, value.t,
	  op1name, threshold1.s, threshold1.t, 
	  op2name, threshold2.s, threshold2.t);
    return false;
  }
  return true;
}

bool checkValue(const MFVec2f& value, class SFNode* node, const char *fieldId,
		bool (*op)(const SFVec2f&, const SFVec2f&), const char *opname, const SFVec2f& threshold)
{
  bool good = true;
  for (int i=0; i<value.size; i++)
    good = good && checkValue(value[i], node, fieldId, op, opname, threshold);
  return good;
}

bool checkValue(const MFVec2f& value, class SFNode* node, const char *fieldId,
		bool (*op1)(const SFVec2f&, const SFVec2f&), const char *op1name, const SFVec2f& threshold1,
		bool (*op2)(const SFVec2f&, const SFVec2f&), const char *op2name, const SFVec2f& threshold2)
{
  bool good = true;
  for (int i=0; i<value.size; i++)
    good = good && checkValue(value[i], node, fieldId, op1, op1name, threshold1, op2, op2name, threshold2);
  return good;
}

///////////////////////////////////////////////////////////////////////////////
// [S|M]FVec3f value checking

bool checkValue(const SFVec3f& value, class SFNode* node, const char *fieldId,
		bool (*op)(const SFVec3f&, const SFVec3f&), const char *opname, const SFVec3f& threshold)
{
  if (!op(value, threshold)) {
    Error(NULL, "%s.%s value %g %g %g out of range: shall be %s %g %g %g",
	  node->name(), fieldId, 
	  value.x, value.y, value.z, 
	  opname, threshold.x, threshold.y, threshold.z);
    return false;
  }
  return true;
}

bool checkValue(const SFVec3f& value, class SFNode* node, const char *fieldId,
		bool (*op1)(const SFVec3f&, const SFVec3f&), const char *op1name, const SFVec3f& threshold1,
		bool (*op2)(const SFVec3f&, const SFVec3f&), const char *op2name, const SFVec3f& threshold2)
{
  if (!op1(value, threshold1) || !op2(value, threshold2)) {
    Error(NULL, "%s.%s value %g %g %g out of range: shall be %s %g %g %g and %s %g %g %g",
	  node->name(), fieldId, value.x, value.y, value.z, 
	  op1name, threshold1.x, threshold1.y, threshold1.z, op2name, threshold2.x, threshold2.y, threshold2.z);
    return false;
  }
  return true;
}

bool checkValue(const MFVec3f& value, class SFNode* node, const char *fieldId,
		bool (*op)(const SFVec3f&, const SFVec3f&), const char *opname, const SFVec3f& threshold)
{
  bool good = true;
  for (int i=0; i<value.size; i++)
    good = good && checkValue(value[i], node, fieldId, op, opname, threshold);
  return good;
}

bool checkValue(const MFVec3f& value, class SFNode* node, const char *fieldId,
		bool (*op1)(const SFVec3f&, const SFVec3f&), const char *op1name, const SFVec3f& threshold1,
		bool (*op2)(const SFVec3f&, const SFVec3f&), const char *op2name, const SFVec3f& threshold2)
{
  bool good = true;
  for (int i=0; i<value.size; i++)
    good = good && checkValue(value[i], node, fieldId, op1, op1name, threshold1, op2, op2name, threshold2);
  return good;
}

///////////////////////////////////////////////////////////////////////////////
// [S|M]FColor value checking

bool checkValue(const SFColor& value, class SFNode* node, const char *fieldId,
		bool (*op)(const SFColor&, const SFColor&), const char *opname, const SFColor& threshold)
{
  if (!op(value, threshold)) {
    Error(NULL, "%s.%s value %g %g %g out of range: shall be %s %g %g %g",
	  node->name(), fieldId, value.r, value.g, value.b, opname, threshold.r, threshold.g, threshold.b);
    return false;
  }
  return true;
}

bool checkValue(const SFColor& value, class SFNode* node, const char *fieldId,
		bool (*op1)(const SFColor&, const SFColor&), const char *op1name, const SFColor& threshold1,
		bool (*op2)(const SFColor&, const SFColor&), const char *op2name, const SFColor& threshold2)
{
  if (!op1(value, threshold1) || !op2(value, threshold2)) {
    Error(NULL, "%s.%s value %g %g %g out of range: shall be %s %g %g %g and %s %g %g %g",
	  node->name(), fieldId, value.r, value.g, value.b, 
	  op1name, threshold1.r, threshold1.g, threshold1.b, op2name, threshold2.r, threshold2.g, threshold2.b);
    return false;
  }
  return true;
}

bool checkValue(const MFColor& value, class SFNode* node, const char *fieldId,
		bool (*op)(const SFColor&, const SFColor&), const char *opname, const SFColor& threshold)
{
  bool good = true;
  for (int i=0; i<value.size; i++)
    good = good && checkValue(value[i], node, fieldId, op, opname, threshold);
  return good;
}

bool checkValue(const MFColor& value, class SFNode* node, const char *fieldId,
		bool (*op1)(const SFColor&, const SFColor&), const char *op1name, const SFColor& threshold1,
		bool (*op2)(const SFColor&, const SFColor&), const char *op2name, const SFColor& threshold2)
{
  bool good = true;
  for (int i=0; i<value.size; i++)
    good = good && checkValue(value[i], node, fieldId, op1, op1name, threshold1, op2, op2name, threshold2);
  return good;
}

///////////////////////////////////////////////////////////////////////////////
// [S|M]FRotation value checking

bool checkValue(const SFRotation& value, class SFNode* node, const char *fieldId,
		bool (*op)(const SFRotation&, const SFRotation&), const char *opname, const SFRotation& threshold)
{
  if (!op(value, threshold)) {
    Error(NULL, "%s.%s value %g %g %g %g out of range: shall be %s %g %g %g %g",
	  node->name(), fieldId, value.x, value.y, value.z, value.radians, opname, threshold.x, threshold.y, threshold.z, threshold.radians);
    return false;
  }
  return true;
}

bool checkValue(const SFRotation& value, class SFNode* node, const char *fieldId,
		bool (*op1)(const SFRotation&, const SFRotation&), const char *op1name, const SFRotation& threshold1,
		bool (*op2)(const SFRotation&, const SFRotation&), const char *op2name, const SFRotation& threshold2)
{
  if (!op1(value, threshold1) || !op2(value, threshold2)) {
    Error(NULL, "%s.%s value %g %g %g %g out of range: shall be %s %g %g %g %g and %s %g %g %g %g",
	  node->name(), fieldId, value.x, value.y, value.z, value.radians, 
	  op1name, threshold1.x, threshold1.y, threshold1.z, threshold1.radians, op2name, threshold2.x, threshold2.y, threshold2.z, threshold2.radians);
    return false;
  }
  return true;
}

bool checkValue(const MFRotation& value, class SFNode* node, const char *fieldId,
		bool (*op)(const SFRotation&, const SFRotation&), const char *opname, const SFRotation& threshold)
{
  bool good = true;
  for (int i=0; i<value.size; i++)
    good = good && checkValue(value[i], node, fieldId, op, opname, threshold);
  return good;
}

bool checkValue(const MFRotation& value, class SFNode* node, const char *fieldId,
		bool (*op1)(const SFRotation&, const SFRotation&), const char *op1name, const SFRotation& threshold1,
		bool (*op2)(const SFRotation&, const SFRotation&), const char *op2name, const SFRotation& threshold2)
{
  bool good = true;
  for (int i=0; i<value.size; i++)
    good = good && checkValue(value[i], node, fieldId, op1, op1name, threshold1, op2, op2name, threshold2);
  return good;
}


} // namespace xrml
