/******************************************************************************
 * Copyright (c) 2000-2021 Ericsson Telecom AB
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
 *
 * Contributors:
 *   Balasko, Jeno
 *   Baranyi, Botond
 *   Beres, Szabolcs
 *   Feher, Csaba
 *   Forstner, Matyas
 *   Gecse, Roland
 *   Kovacs, Ferenc
 *   Raduly, Csaba
 *   Szabados, Kristof
 *   Szabo, Janos Zoltan – initial implementation
 *
 ******************************************************************************/
#include "Valuestuff.hh"
#include "Identifier.hh"
#include "Value.hh"
#include "asn1/Object0.hh"
#include "asn1/Block.hh"
#include "asn1/TokenBuf.hh"
#include <limits.h>

#include "ustring.hh"

namespace Common {

  // =================================
  // ===== Values
  // =================================

  Values::Values(bool p_indexed) : Node(), indexed(p_indexed)
  {
    if (!p_indexed) vs = new vector<Value>();
    else ivs = new vector<IndexedValue>();
  }
  
  Values::Values(const Values& p) : Node(p), indexed(p.indexed)
  {
    if (indexed) {
      ivs = new vector<IndexedValue>();
      for (size_t i = 0; i < p.ivs->size(); ++i) {
        ivs->add((*p.ivs)[i]->clone());
      }
    }
    else {
      vs = new vector<Value>();
      for (size_t i = 0; i < p.vs->size(); ++i) {
        vs->add((*p.vs)[i]->clone());
      }
    }
  }

  Values::~Values()
  {
    if (!indexed) {
      for (size_t i = 0; i < vs->size(); i++) delete (*vs)[i];
      vs->clear();
      delete vs;
    } else {
      for (size_t i = 0; i < ivs->size(); i++) delete (*ivs)[i];
      ivs->clear();
      delete ivs;
    }
  }

  Values *Values::clone() const
  {
    return new Values(*this);
  }

  void Values::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    if (!indexed) {
      for (size_t i = 0; i < vs->size(); i++) {
        Value *v = (*vs)[i];
        if (v) v->set_fullname(p_fullname + "[" + Int2string(i) + "]");
      }
    } else {
      for (size_t i = 0; i < ivs->size(); i++) {
        IndexedValue *iv = (*ivs)[i];
        // The order is defined by the user.  The index used, doesn't really
        // matter here.
        if (iv) iv->set_fullname(p_fullname + "[" + Int2string(i) + "]");
      }
    }
  }

  void Values::set_my_scope(Scope *p_scope)
  {
    if (!indexed) {
      for (size_t i = 0; i < vs->size(); i++) {
        Value *v = (*vs)[i];
        if (v) v->set_my_scope(p_scope);
      }
    } else {
      for (size_t i = 0; i < ivs->size(); i++) {
        IndexedValue *iv = (*ivs)[i];
        if (iv) iv->set_my_scope(p_scope);
      }
    }
  }

  size_t Values::get_nof_vs() const
  {
    if (indexed) FATAL_ERROR("Values::get_nof_vs()");
    return vs->size();
  }

  size_t Values::get_nof_ivs() const
  {
    if (!indexed) FATAL_ERROR("Values::get_nof_ivs()");
    return ivs->size();
  }

  Value *Values::get_v_byIndex(size_t p_i) const
  {
    if (indexed) FATAL_ERROR("Values::get_v_byIndex");
    return (*vs)[p_i];
  }

  IndexedValue *Values::get_iv_byIndex(size_t p_i) const
  {
    if (!indexed) FATAL_ERROR("Values::get_iv_byIndex()");
    return (*ivs)[p_i];
  }

  void Values::add_v(Value *p_v)
  {
    if (!p_v) FATAL_ERROR("Values::add_v(): NULL parameter");
    if (indexed) FATAL_ERROR("Values::add_v()");
    vs->add(p_v);
  }

  void Values::add_iv(IndexedValue *p_iv)
  {
    if (!p_iv) FATAL_ERROR("Values::add_iv(): NULL parameter");
    if (!indexed) FATAL_ERROR("Values::add_iv()");
    ivs->add(p_iv);
  }

  Value *Values::steal_v_byIndex(size_t p_i)
  {
    if (indexed) FATAL_ERROR("Values::steal_v_byIndex()");
    Value *ret_val = (*vs)[p_i];
    if (!ret_val) FATAL_ERROR("Values::steal_v_byIndex()");
    (*vs)[p_i] = NULL;
    return ret_val;
  }

  void Values::dump(unsigned level) const
  {
    if (!indexed) {
      for (size_t i = 0; i < vs->size(); i++) {
        Value *v = (*vs)[i];
        if (v) v->dump(level);
        else DEBUG(level, "Value: stolen");
      }
    } else {
      for (size_t i = 0; i < ivs->size(); i++) {
        IndexedValue *iv = (*ivs)[i];
        if (iv) iv->dump(level);
        else DEBUG(level, "Value: stolen");
      }
    }
  }

  // =================================
  // ===== IndexedValue
  // =================================

  IndexedValue::IndexedValue(Ttcn::FieldOrArrayRef *p_index, Value *p_value)
    : Node(), Location(), index(p_index), value(p_value)
  {
    if (!p_index || !p_value)
      FATAL_ERROR("NULL parameter: IndexedValue::IndexedValue()");
  }
  
  IndexedValue::IndexedValue(const IndexedValue& p) : Node(p), Location(p)
  {
    index = p.index->clone();
    value = p.value->clone();
  }

  IndexedValue::~IndexedValue()
  {
    delete index;
    delete value;
  }

  IndexedValue *IndexedValue::clone() const
  {
    return new IndexedValue(*this);
  }

  void IndexedValue::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    if (value) value->set_fullname(p_fullname);
    if (index) index->set_fullname(p_fullname);
  }

  void IndexedValue::set_my_scope(Scope *p_scope)
  {
    if (value) value->set_my_scope(p_scope);
    if (index) index->set_my_scope(p_scope);
  }

  void IndexedValue::set_code_section
    (GovernedSimple::code_section_t p_code_section)
  {
    // Follow the style and add ifs everywhere...
    if (index) index->get_val()->set_code_section(p_code_section);
    if (value) value->set_code_section(p_code_section);
  }

  Value *IndexedValue::steal_value()
  {
    if (!value) FATAL_ERROR("IndexedValue::steal_value()");
    Value *tmp = value;
    value = 0;
    return tmp;
  }

  void IndexedValue::dump(unsigned level) const
  {
    index->dump(level);
    if (value) value->dump(level + 1);
    else DEBUG(level + 1, "Value: stolen");
  }

  bool IndexedValue::is_unfoldable(ReferenceChain *refch,
  		Type::expected_value_t exp_val)
  {
	  return (value->is_unfoldable(refch, exp_val) ||
			  	  get_index()->is_unfoldable(refch, exp_val));
  }

  // =================================
  // ===== NamedValue
  // =================================

  NamedValue::NamedValue(Identifier *p_name, Value *p_value)
    : Node(), Location(), name(p_name), value(p_value)
  {
    if (!p_name || !p_value)
      FATAL_ERROR("NULL parameter: NamedValue::NamedValue()");
  }

  NamedValue::NamedValue(const NamedValue& p)
    : Node(p), Location(p)
  {
    if (!p.value) FATAL_ERROR("NamedValue::NamedValue(): value is stolen");
    name = p.name->clone();
    value = p.value->clone();
  }

  NamedValue::~NamedValue()
  {
    delete name;
    delete value;
  }

  NamedValue *NamedValue::clone() const
  {
    return new NamedValue(*this);
  }

  void NamedValue::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    if (value) value->set_fullname(p_fullname);
  }

  void NamedValue::set_my_scope(Scope *p_scope)
  {
    if (value) value->set_my_scope(p_scope);
  }

  Value *NamedValue::steal_value()
  {
    if (!value) FATAL_ERROR("NamedValue::steal_value()");
    Value *tmp = value;
    value = 0;
    return tmp;
  }

  void NamedValue::dump(unsigned level) const
  {
    name->dump(level);
    if (value) value->dump(level + 1);
    else DEBUG(level + 1, "Value: stolen");
  }

  // =================================
  // ===== NamedValues
  // =================================

  NamedValues::NamedValues(const NamedValues& p)
    : Node(p), checked(false)
  {
    for (size_t i = 0; i < p.nvs_v.size(); i++)
      nvs_v.add(p.nvs_v[i]->clone());
  }

  NamedValues::~NamedValues()
  {
    for(size_t i = 0; i < nvs_v.size(); i++) delete nvs_v[i];
    nvs_v.clear();
    nvs_m.clear();
  }

  NamedValues *NamedValues::clone() const
  {
    return new NamedValues(*this);
  }

  void NamedValues::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    for(size_t i = 0; i < nvs_v.size(); i++) {
      NamedValue *nv = nvs_v[i];
      nv->set_fullname(p_fullname + "." + nv->get_name().get_dispname());
    }
  }

  void NamedValues::set_my_scope(Scope *p_scope)
  {
    for (size_t i = 0; i < nvs_v.size(); i++)
      nvs_v[i]->set_my_scope(p_scope);
  }

  void NamedValues::add_nv(NamedValue *p_nv)
  {
    if (!p_nv) FATAL_ERROR("NamedValues::add_nv(): NULL parameter");
    nvs_v.add(p_nv);
    if (checked) {
      const string& name = p_nv->get_name().get_name();
      if (nvs_m.has_key(name)) FATAL_ERROR("NamedValues::add_nv()");
      nvs_m.add(name, p_nv);
    }
  }

  bool NamedValues::has_nv_withName(const Identifier& p_name)
  {
    if (!checked) chk_dupl_id(false);
    return nvs_m.has_key(p_name.get_name());
  }

  NamedValue* NamedValues::get_nv_byName(const Identifier& p_name)
  {
    if (!checked) chk_dupl_id(false);
    return nvs_m[p_name.get_name()];
  }

  void NamedValues::chk_dupl_id(bool report_errors)
  {
    if (checked) nvs_m.clear();
    for (size_t i = 0; i < nvs_v.size(); i++) {
      NamedValue *nv = nvs_v[i];
      const Identifier& id = nv->get_name();
      const string& name = id.get_name();
      if (!nvs_m.has_key(name)) nvs_m.add(name, nv);
      else if (report_errors) {
	const char *dispname_str = id.get_dispname().c_str();
	nv->error("Duplicate identifier: `%s'", dispname_str);
	nvs_m[name]->note("Previous definition of `%s' is here", dispname_str);
      }
    }
    checked = true;
  }

  void NamedValues::dump(unsigned level) const
  {
    for (size_t i = 0; i < nvs_v.size(); i++)
      nvs_v[i]->dump(level);
  }

  // =================================
  // ===== OID_comp
  // =================================

  const OID_comp::nameform_t OID_comp::names_root[] = {
    { "itu__t", 0 },
    { "ccitt", 0 },
    { "itu__r", 0 },
    { "iso", 1 },
    { "joint__iso__itu__t", 2 },
    { "joint__iso__ccitt", 2 },
    { NULL, 0 }
  };

  const OID_comp::nameform_t OID_comp::names_itu[] = {
    { "recommendation", 0 },
    { "question", 1 },
    { "administration", 2 },
    { "network__operator", 3 },
    { "identified__organization", 4 },
    { "r__recommendation", 5 },
    { NULL, 0 }
  };

  const OID_comp::nameform_t OID_comp::names_iso[] = {
    { "standard", 0 },
    { "registration__authority", 1 },
    { "member__body", 2 },
    { "identified__organization", 3 },
    { NULL, 0 }
  };

  // taken from OID repository: http://asn1.elibel.tm.fr/oid/
  const OID_comp::nameform_t OID_comp::names_joint[] = {
    { "presentation", 0 },
    { "asn1", 1 },
    { "association__control", 2 },
    { "reliable__transfer", 3 },
    { "remote__operations", 4 },
    { "ds", 5 },
    { "directory", 5 },
    { "mhs", 6 },
    { "mhs__motis", 6 },
    { "ccr", 7 },
    { "oda", 8 },
    { "ms", 9 },
    { "osi__management", 9 },
    { "transaction__processing", 10 },
    { "dor", 11 },
    { "distinguished__object__reference", 11 },
    { "reference__data__transfer", 12 },
    { "network__layer", 13 },
    { "network__layer__management", 13 },
    { "transport__layer", 14 },
    { "transport__layer__management", 14 },
    { "datalink__layer", 15 },
    { "datalink__layer__management", 15 },
    { "datalink__layer__management__information", 15 },
    { "country", 16 },
    { "registration__procedures", 17 },
    { "registration__procedure", 17 },
    { "physical__layer", 18 },
    { "physical__layer__management", 18 },
    { "mheg", 19 },
    { "genericULS", 20 },
    { "generic__upper__layers__security", 20 },
    { "guls", 20 },
    { "transport__layer__security__protocol", 21 },
    { "network__layer__security__protocol", 22 },
    { "international__organizations", 23 },
    { "internationalRA", 23 },
    { "sios", 24 },
    { "uuid", 25 },
    { "odp", 26 },
    { "upu", 40 },
    { NULL, 0 }
  };

  OID_comp::OID_comp(Identifier *p_name, Value *p_number)
    : Node(), Location(), defdval(0), name(p_name), number(p_number), var(0)
  {
    if (p_name) {
      if (p_number) formtype = NAMEANDNUMBERFORM;
      else formtype = NAMEFORM;
    } else {
      if (p_number) formtype = NUMBERFORM;
      else FATAL_ERROR("NULL parameter: Common::OID_comp::OID_comp()");
    }
  }

  OID_comp::OID_comp(Value *p_defdval)
    : Node(), Location(), formtype(DEFDVALUE), defdval(p_defdval), name(0),
      number(0), var(0)
  {
    if(!p_defdval)
      FATAL_ERROR("NULL parameter: Common::OID_comp::OID_comp()");
  }

  OID_comp::~OID_comp()
  {
    delete defdval;
    delete name;
    delete number;
    delete var;
  }

  OID_comp *OID_comp::clone() const
  {
    FATAL_ERROR("OID_comp::clone");
  }

  void OID_comp::chk_OID(ReferenceChain& refch, Value *parent, size_t index,
                     oidstate_t& state)
  {
    Error_Context ec(this, "In component #%lu of OBJECT IDENTIFIER value",
                     static_cast<unsigned long>(index + 1));
    switch (formtype) {
    case NAMEFORM: {
      Int v_Int = detect_nameform(state);
      if (v_Int >= 0) {
	// we have recognized the NameForm
	number = new Value(Value::V_INT, v_Int);
	number->set_fullname(get_fullname());
	number->set_my_scope(parent->get_my_scope());
	number->set_location(*this);
	// the component is finished, jump out from switch
	break;
      } else {
	// otherwise treat as a defined value and continue
	Common::Reference *ref;
	if (parent->is_asn1()) ref = new Asn::Ref_defd_simple(0, name);
	else ref = new Ttcn::Reference(0, name);
	name = 0;
	defdval = new Value(Value::V_REFD, ref);
	defdval->set_fullname(get_fullname());
	defdval->set_my_scope(parent->get_my_scope());
	defdval->set_location(*this);
	formtype = DEFDVALUE;
      }
      }
    // no break
    case DEFDVALUE:
      chk_defdvalue_OID(refch, state);
      break;
    case NUMBERFORM:
      chk_numberform_OID(state);
      break;
    case NAMEANDNUMBERFORM:
      chk_nameandnumberform(state);
      break;
    default:
      FATAL_ERROR("OID_comp::chk_OID()");
    } // switch
  }

  void OID_comp::chk_ROID(ReferenceChain& refch, size_t index)
  {
    Error_Context ec(this, "In component #%lu of RELATIVE-OID value",
      static_cast<unsigned long> (index + 1));
    switch (formtype) {
    case DEFDVALUE:
      chk_defdvalue_ROID(refch);
      break;
    case NUMBERFORM:
    case NAMEANDNUMBERFORM:
      chk_numberform_ROID();
      break;
    default:
      FATAL_ERROR("OID_comp::chk_ROID()");
    }
  }

  bool OID_comp::has_error()
  {
    if (formtype == DEFDVALUE) return defdval->has_oid_error();
    else return number->get_value_refd_last()->get_valuetype() != Value::V_INT;
  }

  void OID_comp::get_comps(vector<string>& comps)
  {
    if (formtype == DEFDVALUE) defdval->get_oid_comps(comps);
    else if (formtype == VARIABLE) {
      comps.add(new string("OBJID::from_INTEGER(" + var->get_stringRepr() + ")"));
    }
    else comps.add(new string(number->get_stringRepr() + "u"));
  }
  
  bool OID_comp::is_variable()
  {
    return (formtype == VARIABLE);
  }

  Int OID_comp::detect_nameform(oidstate_t& state)
  {
    const string& name_str = name->get_name();
    Int ret_val = -1;
    switch (state) {
    case START:
      if (name_str == "itu__t" || name_str == "ccitt") {
	state = ITU;
	ret_val = 0;
      } else if (name_str == "itu__r") {
	warning("Identifier `%s' should not be used as NameForm",
	  name->get_dispname().c_str());
        state = ITU;
	ret_val = 0;
      } else if (name_str == "iso") {
	state = ISO;
	ret_val = 1;
      } else if (name_str == "joint__iso__itu__t"
        || name_str == "joint__iso__ccitt") {
	state = JOINT;
	ret_val = 2;
      }
      break;
    case ITU:
      for (const nameform_t *nf = names_itu; nf->name != NULL; nf++) {
	if (name_str == nf->name) {
	  ret_val = nf->value;
	  switch (nf->value) {
	  case 0:
	    state = ITU_REC;
	    break;
	  case 5:
	    warning("Identifier `%s' should not be used as NameForm",
	      name->get_dispname().c_str());
	    // no break
	  default:
	    state = LATER;
	  }
	  break;
	}
      }
      break;
    case ISO:
      for (const nameform_t *nf = names_iso; nf->name != NULL; nf++) {
	if (name_str == nf->name) {
	  ret_val = nf->value;
	  state = LATER;
	  break;
	}
      }
      break;
    case JOINT:
      for (const nameform_t *nf = names_joint; nf->name != NULL; nf++) {
	if (name_str == nf->name) {
	  ret_val = nf->value;
	  state = LATER;
	  warning("Identifier `%s' should not be used as NameForm",
	    name->get_dispname().c_str());
	  break;
	}
      }
      break;
    case ITU_REC:
      if (name_str.size() == 1) {
	char c = name_str[0];
	if (c >= 'a' && c <= 'z') {
	  ret_val = c - 'a' + 1;
	  state = LATER;
	}
      }
      break;
    default:
      break;
    }
    return ret_val;
  }

  void OID_comp::chk_defdvalue_OID(ReferenceChain& refch, oidstate_t& state)
  {
    Value *v = defdval->get_value_refd_last();
    switch (v->get_valuetype()) {
    case Value::V_ERROR:
      state = LATER;
      break;
    case Value::V_INT:
      // if the component is a reference to an integer value
      // it shall be set to number form
      formtype = NUMBERFORM;
      number = defdval;
      defdval = 0;
      chk_numberform_OID(state);
      break;
    case Value::V_OID:
      if (state != START) defdval->error("INTEGER or RELATIVE-OID "
	"value was expected");
      state = LATER;
      v->chk_OID(refch);
      break;
    case Value::V_ROID:
      switch (state) {
      case ITU_REC:
	state = LATER;
	// no break
      case LATER:
	break;
      default:
	defdval->error("RELATIVE-OID value cannot be used as the "
	  "%s component of an OBJECT IDENTIFIER value",
	  state == START ? "first" : "second");
	state = LATER;
	break;
      }
      break;
    case Value::V_REFD: {
      Common::Reference* ref = v->get_reference();
      Common::Assignment* as = ref->get_refd_assignment();
      Type* t = as->get_Type()->get_type_refd_last();
      if (t->get_typetype() == Type::T_INT) {
        formtype = VARIABLE;
        var = defdval;
        defdval = 0;
      } else {
        defdval->error("INTEGER variable was expected");
      }
      state = LATER;
      break; }      
    default:
      if (state == START) defdval->error("INTEGER or OBJECT IDENTIFIER "
	"value was expected for the first component");
      else defdval->error("INTEGER or RELATIVE-OID value was expected");
      state = LATER;
      break;
    }
  }

  void OID_comp::chk_numberform_OID(oidstate_t& state)
  {
    Value *v = number->get_value_refd_last();
    switch (v->get_valuetype()) {
    case Value::V_INT:
      // everything is OK, continue the checking
      break;
    case Value::V_ERROR:
      state = LATER;
      return;
    default:
      number->error("INTEGER value was expected in the number form");
      state = LATER;
      return;
    }
    const int_val_t *v_Num = v->get_val_Int();
    if (v_Num->get_val() > static_cast<Int>(UINT_MAX)) {
      number->error("An integer value less than `%u' was expected in the "
        "number form instead of `%s'", UINT_MAX, (v_Num->t_str()).c_str());
      state = LATER;
      return;
    }
    Int v_Int = v_Num->get_val();
    switch (state) {
    case START:
      switch (v_Int) {
      case 0:
	state = ITU;
	break;
      case 1:
	state = ISO;
	break;
      case 2:
	state = JOINT;
	break;
      default:
        number->error("The value of first OBJECT IDENTIFIER component must "
          "be between 0 and 2 instead of %s", Int2string(v_Int).c_str());
	state = LATER;
        break;
      }
      break;
    case ITU:
    case ISO:
      if (v_Int < 0 || v_Int > 39) number->error("The value of "
	  "second OBJECT IDENTIFIER component must be between 0 and 39 "
	  "instead of %s", Int2string(v_Int).c_str());
      if (state == ITU && v_Int == 0) state = ITU_REC;
      else state = LATER;
      break;
    case JOINT:
    default:
      if (v_Int < 0) number->error("A non-negative integer value was "
        "expected instead of %s", Int2string(v_Int).c_str());
      state = LATER;
      break;
    }
  }

  void OID_comp::chk_nameandnumberform(oidstate_t& state)
  {
    // make a backup of state
    oidstate_t oldstate = state;
    chk_numberform_OID(state);
    Value *v = number->get_value_refd_last();
    if (v->get_valuetype() != Value::V_INT || !v->get_val_Int()->is_native())
      return;
    Int v_Int = v->get_val_Int()->get_val();
    const string& name_str = name->get_name();
    switch (oldstate) {
    case START:
      if (!is_valid_name_for_number(name_str, v_Int, names_root)) {
	number->warning("Identifier %s was expected instead of `%s' for "
	  "number %s in the NameAndNumberForm as the first OBJECT IDENTIFIER "
	  "component", get_expected_name_for_number(v_Int, number->is_asn1(),
	  names_root).c_str(), name->get_dispname().c_str(),
	  Int2string(v_Int).c_str());
      }
      break;
    case ITU:
      if (!is_valid_name_for_number(name_str, v_Int, names_itu)) {
	number->warning("Identifier %s was expected instead of `%s' for "
	  "number %s in the NameAndNumberForm as the second OBJECT IDENTIFIER "
	  "component", get_expected_name_for_number(v_Int, number->is_asn1(),
	  names_itu).c_str(), name->get_dispname().c_str(),
	  Int2string(v_Int).c_str());
      }
      break;
    case ISO:
      if (!is_valid_name_for_number(name_str, v_Int, names_iso)) {
	number->warning("Identifier %s was expected instead of `%s' for "
	  "number %s in the NameAndNumberForm as the second OBJECT IDENTIFIER "
	  "component", get_expected_name_for_number(v_Int, number->is_asn1(),
	  names_iso).c_str(), name->get_dispname().c_str(),
	  Int2string(v_Int).c_str());
      }
      break;
    case JOINT:
      if (!is_valid_name_for_number(name_str, v_Int, names_joint)) {
	number->warning("Identifier %s was expected instead of `%s' for "
	  "number %s in the NameAndNumberForm as the second OBJECT IDENTIFIER "
	  "component", get_expected_name_for_number(v_Int, number->is_asn1(),
	  names_joint).c_str(), name->get_dispname().c_str(),
	  Int2string(v_Int).c_str());
      }
      break;
    case ITU_REC:
      if (v_Int >= 1 && v_Int <= 26 &&
	(name_str.size() != 1 || name_str[0] != 'a' + v_Int - 1))
	number->warning("Identifier `%c' was expected instead of `%s' for "
	  "number %s in the NameAndNumberForm as the third OBJECT IDENTIFIER "
	  "component", static_cast<char>('a' + v_Int - 1), name->get_dispname().c_str(),
	  Int2string(v_Int).c_str());
      break;
    default:
      // no warning can be displayed in later states
      break;
    }
  }

  bool OID_comp::is_valid_name_for_number(const string& p_name,
    const Int& p_number, const nameform_t *p_names)
  {
    bool ret_val = true;
    for (const nameform_t *nf = p_names; nf->name != NULL; nf++) {
      if (static_cast<unsigned int>(p_number) == nf->value) {
	if (p_name == nf->name) return true;
	else ret_val = false;
      }
    }
    return ret_val;
  }

  string OID_comp::get_expected_name_for_number(const Int& p_number,
    bool p_asn1, const nameform_t *p_names)
  {
    size_t nof_expected_names = 0;
    for (const nameform_t *nf = p_names; nf->name != NULL; nf++) {
      if (static_cast<unsigned int>(p_number) == nf->value) nof_expected_names++;
    }
    if (nof_expected_names <= 0)
      FATAL_ERROR("OID_comp::get_expected_name_for_number()");
    string ret_val;
    size_t i = 0;
    for (const nameform_t *nf = p_names; nf->name != NULL; nf++) {
      if (static_cast<unsigned int>(p_number) == nf->value) {
	if (i > 0) {
	  if (i < nof_expected_names - 1) ret_val += ", ";
	  else ret_val += " or ";
	}
	ret_val += "`";
	Identifier id(Identifier::ID_NAME, string(nf->name));
	if (p_asn1) ret_val += id.get_asnname();
	else ret_val += id.get_ttcnname();
	ret_val += "'";
	i++;
      }
    }
    return ret_val;
  }

  void OID_comp::chk_defdvalue_ROID(ReferenceChain& refch)
  {
    Value *v = defdval->get_value_refd_last();
    switch (v->get_valuetype()) {
    case Value::V_ERROR:
      break;
    case Value::V_INT:
      // if the component is a reference to an integer value
      // it shall be set to number form
      formtype = NUMBERFORM;
      number = defdval;
      defdval = 0;
      chk_numberform_ROID();
      break;
    case Value::V_ROID:
      v->chk_ROID(refch);
      break;
    default:
      defdval->error("INTEGER or RELATIVE-OID value was expected");
      break;
    }
  }

  void OID_comp::chk_numberform_ROID()
  {
    Value *v = number->get_value_refd_last();
    switch (v->get_valuetype()) {
    case Value::V_INT: {
      const int_val_t *v_Num = v->get_val_Int();
      if (*v_Num > INT_MAX) {
        number->error("An integer value less than `%d' was expected instead "
          "of `%s'", INT_MAX, (v_Num->t_str()).c_str());
        return;
      }
      Int v_Int = v_Num->get_val();
      if (v_Int < 0)
        number->error("A non-negative integer value was expected instead "
          "of %s", Int2string(v_Int).c_str());
      break; }
    case Value::V_ERROR:
      break;
    default:
      number->error("INTEGER value was expected in the number form");
      break;
    }
  }

  void OID_comp::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    if (defdval) defdval->set_fullname(p_fullname);
    if (number) number->set_fullname(p_fullname);
    if (var) var->set_fullname(p_fullname);
  }

  void OID_comp::set_my_scope(Scope *p_scope)
  {
    if(defdval) defdval->set_my_scope(p_scope);
    if(number) number->set_my_scope(p_scope);
    if(var) var->set_my_scope(p_scope);
  }

  void OID_comp::append_stringRepr(string& str) const
  {
    switch (formtype) {
    case NAMEFORM:
      str += name->get_dispname();
      break;
    case NUMBERFORM:
    case NAMEANDNUMBERFORM:
      str += number->get_stringRepr();
      break;
    case DEFDVALUE:
      str += defdval->get_stringRepr();
      break;
    case VARIABLE:
      str += var->get_stringRepr();
      break;
    default:
      str += "<unknown OID component>";
      break;
    }
  }

  // =================================
  // ===== CharsDefn
  // =================================

  CharsDefn::CharsDefn(string *p_str)
    : Node(), Location(), selector(CD_CSTRING), checked(false)
  {
    if(!p_str) FATAL_ERROR("CharsDefn::CharsDefn()");
    u.str=p_str;
  }

  CharsDefn::CharsDefn(Value *p_val)
    : Node(), Location(), selector(CD_VALUE), checked(false)
  {
    if(!p_val) FATAL_ERROR("CharsDefn::CharsDefn()");
    u.val=p_val;
  }

  CharsDefn::CharsDefn(Int p_g, Int p_p, Int p_r, Int p_c)
    : Node(), Location(), selector(CD_QUADRUPLE), checked(false)
  {
    u.quadruple.g=p_g;
    u.quadruple.p=p_p;
    u.quadruple.r=p_r;
    u.quadruple.c=p_c;
  }

  CharsDefn::CharsDefn(Int p_c, Int p_r)
    : Node(), Location(), selector(CD_TUPLE), checked(false)
  {
    u.tuple.c=p_c;
    u.tuple.r=p_r;
  }

  CharsDefn::CharsDefn(Block *p_block)
    : Node(), Location(), selector(CD_BLOCK), checked(false)
  {
    if(!p_block) FATAL_ERROR("CharsDefn::CharsDefn()");
    u.block=p_block;
  }

  CharsDefn::~CharsDefn()
  {
    switch(selector) {
    case CD_CSTRING:
      delete u.str;
      break;
    case CD_VALUE:
      delete u.val;
      break;
    case CD_BLOCK:
      delete u.block;
      break;
    default:
      break;
    } // switch
  }

  CharsDefn *CharsDefn::clone() const
  {
    FATAL_ERROR("CharsDefn::clone");
  }

  void CharsDefn::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    switch (selector) {
    case CD_VALUE:
      u.val->set_fullname(p_fullname);
      break;
    case CD_BLOCK:
      u.block->set_fullname(p_fullname);
      break;
    default:
      break;
    }
  }

  void CharsDefn::set_my_scope(Scope *p_scope)
  {
    if(selector==CD_VALUE)
      u.val->set_my_scope(p_scope);
  }

  void CharsDefn::parse_block()
  {
    if(selector!=CD_BLOCK) return;
    Block *t_block=u.block;
    Node *node=t_block->parse(KW_Block_QuadrupleOrTuple);
    CharsDefn *cd=dynamic_cast<CharsDefn*>(node);
    if(!cd) cd=new CharsDefn(0, 0, 0, 0);
    delete t_block;
    if(cd->selector==CD_QUADRUPLE) {
      u.quadruple=cd->u.quadruple;
      selector=CD_QUADRUPLE;
    }
    else {
      u.tuple=cd->u.tuple;
      selector=CD_TUPLE;
    }
    delete cd;
  }

  void CharsDefn::chk()
  {
    if(checked) return;
    checked=true;
    if(selector==CD_BLOCK) parse_block();
    switch(selector) {
    case CD_QUADRUPLE:
      if(u.quadruple.g>127) {
        error("In quadruple: Group value must be in range 0..127");
        u.quadruple.g=0;
      }
      if(u.quadruple.p>255) {
        error("In quadruple: Plane value must be in range 0..255");
        u.quadruple.p=0;
      }
      if(u.quadruple.r>255) {
        error("In quadruple: Row value must be in range 0..255");
        u.quadruple.r=0;
      }
      if(u.quadruple.c>255) {
        error("In quadruple: Cell value must be in range 0..255");
        u.quadruple.c=0;
      }
      break;
    case CD_TUPLE:
      if(u.tuple.c>7) {
        error("In tuple: Column value must be in range 0..7");
        u.tuple.c=0;
      }
      if(u.tuple.r>15) {
        error("In tuple: Row value must be in range 0..15");
        u.tuple.r=0;
      }
      break;
    default:
      break;
    } // switch
  }

  string CharsDefn::get_string(ReferenceChain *refch)
  {
    chk();
    switch(selector) {
    case CD_CSTRING:
      return *u.str;
    case CD_QUADRUPLE:
      error("Quadruple form is not allowed here.");
      return string("\0");
    case CD_TUPLE:{
      error("Tuple form is not allowed here.");
      char c=u.tuple.c*16+u.tuple.r;
      return string(1, &c); }
    case CD_VALUE:
      switch(u.val->get_value_refd_last(refch)->get_valuetype()) {
      case Value::V_ERROR:
        return string();
      case Value::V_CHARSYMS:
      case Value::V_CSTR:
        return u.val->get_val_str();
      case Value::V_ISO2022STR:
        error("Reference to string value was expected"
              " (instead of ISO-2022 string).");
        return string();
      case Value::V_USTR:
        error("Reference to string value was expected"
              " (instead of ISO-10646 string).");
        return string();
      default:
        error("Reference to character string value was expected.");
        return string();
      } // switch valuetype
      break;
    default:
      FATAL_ERROR("CharsDefn::get_string()");
      // to eliminate warning
      return string();
    }
  }

  ustring CharsDefn::get_ustring(ReferenceChain *refch)
  {
    chk();
    switch(selector) {
    case CD_CSTRING:
      return ustring(*u.str);
      break;
    case CD_QUADRUPLE:
      return ustring(u.quadruple.g, u.quadruple.p,
                     u.quadruple.r, u.quadruple.c);
      break;
    case CD_TUPLE:
      error("Tuple form is not allowed here.");
      return ustring(0, 0, 0, u.tuple.c*16+u.tuple.r);
      break;
    case CD_VALUE:
      switch(u.val->get_value_refd_last(refch)->get_valuetype()) {
      case Value::V_ERROR:
        return ustring();
      case Value::V_CSTR:
      case Value::V_USTR:
      case Value::V_CHARSYMS:
        return u.val->get_val_ustr();
      case Value::V_ISO2022STR:
        error("Reference to ISO-10646 string value was expected"
              " (instead of ISO-2022 string).");
        return ustring();
      default:
        error("Reference to string value was expected.");
        return ustring();
      } // switch valuetype
      break;
    default:
      FATAL_ERROR("CharsDefn::get_ustring()");
      // to eliminate warning
      return ustring();
    }
  }

  string CharsDefn::get_iso2022string(ReferenceChain *refch)
  {
    chk();
    switch(selector) {
    case CD_CSTRING:
      return *u.str;
    case CD_QUADRUPLE:
      error("Quadruple form is not allowed here");
      return string("\0");
    case CD_TUPLE:{
      char c=u.tuple.c*16+u.tuple.r;
      return string(1, &c); }
    case CD_VALUE:
      switch(u.val->get_value_refd_last(refch)->get_valuetype()) {
      case Value::V_ERROR:
        return string();
      case Value::V_CSTR:
      case Value::V_ISO2022STR:
      case Value::V_CHARSYMS:
        return u.val->get_val_iso2022str();
      case Value::V_USTR:
        error("Reference to ISO-2022 string value was expected"
              " (instead of ISO-10646 string).");
        return string();
      default:
        error("Reference to string value was expected.");
        return string();
      } // switch valuetype
      break;
    default:
      FATAL_ERROR("CharsDefn::get_iso2022string()");
      // to eliminate warning
      return string();
    }
  }

  size_t CharsDefn::get_len(ReferenceChain *refch)
  {
    chk();
    switch(selector) {
    case CD_CSTRING:
      return u.str->size();
    case CD_QUADRUPLE:
    case CD_TUPLE:
      return 1;
    case CD_VALUE:
      switch(u.val->get_value_refd_last(refch)->get_valuetype()) {
      case Value::V_ERROR:
        return 0;
      case Value::V_CSTR:
      case Value::V_USTR:
      case Value::V_ISO2022STR:
      case Value::V_CHARSYMS:
        return u.val->get_val_strlen();
      default:
        error("Reference to string value was expected.");
        return 0;
      } // switch valuetype
      break;
    default:
      FATAL_ERROR("CharsDefn::get_LEN()");
      // to eliminate warning
      return 0;
    }
  }

  void CharsDefn::dump(unsigned level) const
  {
    switch (selector) {
    case CD_CSTRING:
      DEBUG(level, "\"%s\"", u.str->c_str());
      break;
    case CD_QUADRUPLE:
      DEBUG(level, "{%s, %s, %s, %s}",
	    Int2string(u.quadruple.g).c_str(),
	    Int2string(u.quadruple.p).c_str(),
	    Int2string(u.quadruple.r).c_str(),
	    Int2string(u.quadruple.c).c_str());
      break;
    case CD_TUPLE:
      DEBUG(level, "{%s, %s}", Int2string(u.tuple.c).c_str(),
	    Int2string(u.tuple.r).c_str());
      break;
    case CD_VALUE:
      u.val->dump(level);
      break;
    case CD_BLOCK:
      u.block->dump(level);
      break;
    default:
      break;
    }
  }

  // =================================
  // ===== CharSyms
  // =================================

  CharSyms::CharSyms() : Node(), Location(), selector(CS_UNDEF)
  {
  }

  CharSyms::~CharSyms()
  {
    for(size_t i=0; i<cds.size(); i++) delete cds[i];
    cds.clear();
    switch(selector) {
    case CS_UNDEF:
      break;
    case CS_CSTRING:
    case CS_ISO2022STRING:
      delete u.str;
      break;
    case CS_USTRING:
      delete u.ustr;
      break;
    default:
      FATAL_ERROR("CharSyms::~CharSyms()");
    } // switch
  }

  CharSyms *CharSyms::clone() const
  {
    FATAL_ERROR("CharSyms::clone");
  }

  void CharSyms::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    for (size_t i = 0; i < cds.size(); i++)
      cds[i]->set_fullname(p_fullname + "." + Int2string(i));
  }

  void CharSyms::set_my_scope(Scope *p_scope)
  {
    for(size_t i=0; i<cds.size(); i++)
      cds[i]->set_my_scope(p_scope);
  }

  void CharSyms::add_cd(CharsDefn *p_cd)
  {
    if(!p_cd) return;
    cds.add(p_cd);
  }

  string CharSyms::get_string(ReferenceChain *refch)
  {
    switch(selector) {
    case CS_UNDEF:{
      bool destroy_refch=refch==0;
      if(refch) refch->mark_state();
      else refch=new ReferenceChain(this, "While checking"
                                    " charstring value");
      u.str=new string();
      selector=CS_CHECKING;
      if(refch->add(get_fullname()))
        for(size_t i=0; i<cds.size(); i++)
          *u.str+=cds[i]->get_string(refch);
      if(destroy_refch) delete refch;
      else refch->prev_state();
      selector=CS_CSTRING;
    }
    // no break
    case CS_CSTRING:
      return *u.str;
    case CS_USTRING:
      error("ISO-10646 charstring cannot be used in charstring context");
      return string();
    case CS_ISO2022STRING:
      error("ISO-2022 charstring cannot be used in charstring context");
      return string();
    case CS_CHECKING:
      error("Circular reference in `%s'", get_fullname().c_str());
      return string();
    default:
      FATAL_ERROR("CharSyms::get_string()");
      return string();
    } // switch
  }

  ustring CharSyms::get_ustring(ReferenceChain *refch)
  {
    switch(selector) {
    case CS_UNDEF:{
      bool destroy_refch=refch==0;
      if(refch) refch->mark_state();
      else refch=new ReferenceChain(this, "While checking"
                                    " charstring value");
      u.ustr=new ustring();
      selector=CS_CHECKING;
      if(refch->add(get_fullname()))
        for(size_t i=0; i<cds.size(); i++)
          *u.ustr+=cds[i]->get_ustring(refch);
      if(destroy_refch) delete refch;
      else refch->prev_state();
      selector=CS_USTRING;
    }
    // no break
    case CS_USTRING:
      return *u.ustr;
    case CS_CSTRING:
      return ustring(*u.str);
    case CS_ISO2022STRING:
      error("ISO-2022 charstring cannot be used in ISO-10646 context");
      return ustring();
    case CS_CHECKING:
      error("Circular reference in `%s'", get_fullname().c_str());
      return ustring();
    default:
      FATAL_ERROR("CharSyms::get_ustring()");
      return ustring();
    } // switch
  }

  string CharSyms::get_iso2022string(ReferenceChain *refch)
  {
    switch(selector) {
    case CS_UNDEF:{
      bool destroy_refch=refch==0;
      if(refch) refch->mark_state();
      else refch=new ReferenceChain(this, "While checking"
                                    " charstring value");
      u.str=new string();
      selector=CS_CHECKING;
      if(refch->add(get_fullname()))
        for(size_t i=0; i<cds.size(); i++)
          *u.str+=cds[i]->get_iso2022string(refch);
      if(destroy_refch) delete refch;
      else refch->prev_state();
      selector=CS_CSTRING;
    }
    // no break
    case CS_CSTRING:
    case CS_ISO2022STRING:
      return *u.str;
    case CS_USTRING:
      error("ISO-10646 charstring cannot be used in ISO-2022 context");
      return string();
    case CS_CHECKING:
      error("Circular reference in `%s'", get_fullname().c_str());
      return string();
    default:
      FATAL_ERROR("CharSyms::get_iso2022string()");
      return string();
    } // switch
  }

  size_t CharSyms::get_len(ReferenceChain *refch)
  {
    switch(selector) {
    case CS_UNDEF:{
      bool destroy_refch=refch==0;
      if(refch) refch->mark_state();
      else refch=new ReferenceChain(this, "While checking"
                                    " charstring value");
      size_t len=0;
      selector=CS_CHECKING;
      if(refch->add(get_fullname()))
        for(size_t i=0; i<cds.size(); i++)
          len+=cds[i]->get_len(refch);
      if(destroy_refch) delete refch;
      else refch->prev_state();
      selector=CS_UNDEF;
    }
    // no break
    case CS_CSTRING:
    case CS_ISO2022STRING:
      return u.str->size();
    case CS_USTRING:
      return u.ustr->size();
    case CS_CHECKING:
      error("Circular reference in `%s'", get_fullname().c_str());
      return 0;
    default:
      FATAL_ERROR("CharSyms::get_len()");
      return 0;
    } // switch
  }

  void CharSyms::dump(unsigned level) const
  {
    DEBUG(level, "CharSyms:");
    level++;
    for (size_t i = 0; i < cds.size(); i++)
      cds[i]->dump(level);
  }

} // namespace Common
