Skip to content
Snippets Groups Projects
enum.c 63.9 KiB
Newer Older
Elemer Lelik's avatar
Elemer Lelik committed
/******************************************************************************
 * Copyright (c) 2000-2021 Ericsson Telecom AB
Elemer Lelik's avatar
Elemer Lelik committed
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
Elemer Lelik's avatar
Elemer Lelik committed
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
Elemer Lelik's avatar
Elemer Lelik committed
 *
 * Contributors:
 *   Baji, Laszlo
 *   Balasko, Jeno
 *   Baranyi, Botond
 *   Beres, Szabolcs
 *   Cserveni, Akos
 *   Delic, Adam
 *   Feher, Csaba
 *   Forstner, Matyas
 *   Kovacs, Ferenc
 *   Kremer, Peter
 *   Raduly, Csaba
 *   Szabados, Kristof
 *   Szabo, Bence Janos
 *   Szabo, Janos Zoltan – initial implementation
 *   Szalai, Gabor
 *
 ******************************************************************************/
#include "../common/memory.h"
#include "datatypes.h"
#include "enum.h"
#include "encdec.h"

#include <stdlib.h>

#include "main.hh"
#include "ttcn3/compiler.h"

void defEnumClass(const enum_def *edef, output_struct *output)
{
  size_t i;
  char *def = NULL, *src = NULL;
  const char *name = edef->name, *dispname = edef->dispname;
  boolean ber_needed = edef->isASN1 && enable_ber();
  boolean raw_needed = edef->hasRaw && enable_raw();
  boolean text_needed= edef->hasText && enable_text();
  boolean xer_needed = edef->hasXer && enable_xer();
  boolean json_needed = edef->hasJson && enable_json();
  boolean oer_needed = edef->hasOer && enable_oer();

  char *enum_type, *qualified_enum_type, *unknown_value, *unbound_value;
  enum_type = mcopystr("enum_type");
  qualified_enum_type = mprintf("%s::enum_type", name);
  unknown_value = mcopystr("UNKNOWN_VALUE");
  unbound_value = mcopystr("UNBOUND_VALUE");

  /* Class declaration */
  output->header.class_decls = mputprintf(output->header.class_decls,
    "class %s;\n", name);

  /* Class definition */
  def = mputprintf(def,
#ifndef NDEBUG
      "// written by %s in " __FILE__ " at %d\n"
#endif
    "class %s : public %s { // enum\n"
    "friend class %s_template;\n"
#ifndef NDEBUG
      , __FUNCTION__, __LINE__
#endif
      , name, (use_runtime_2) ? "Enum_Type" : "Base_Type", name);
  def = mputstr(def, "public:\n"
    "enum enum_type { ");
  for (i = 0; i < edef->nElements; i++) {
    def = mputprintf(def, "%s = %d, ", edef->elements[i].name,
    edef->elements[i].value);
  }
  def = mputprintf(def, "UNKNOWN_VALUE = %d, UNBOUND_VALUE = %d };\n"
    "private:\n", edef->firstUnused, edef->secondUnused);
  def = mputprintf(def, "%s enum_value;\n\n"
    "public:\n", enum_type);

  /* constructors */
  def = mputprintf(def, "%s();\n", name);
  src = mputprintf(src, "%s::%s()\n"
    "{\n"
    "enum_value = %s;\n"
    "}\n\n", name, name, unbound_value);

  def = mputprintf(def, "%s(int other_value);\n", name);
  src = mputprintf(src,
    "%s::%s(int other_value)\n"
    "{\n"
    "if (!is_valid_enum(other_value)) "
    "TTCN_error(\"Initializing a variable of enumerated type %s with "
    "invalid numeric value %%d.\", other_value);\n"
    "enum_value = (%s)other_value;\n"
    "}\n\n",
    name, name, dispname, enum_type);

  def = mputprintf(def, "%s(%s other_value);\n", name, enum_type);
  src = mputprintf(src,
    "%s::%s(%s other_value)\n"
    "{\n"
    "enum_value = other_value;\n"
    "}\n\n", name, name, enum_type);

  def = mputprintf(def, "%s(const %s& other_value);\n\n", name, name);
  src = mputprintf
    (src,
     "%s::%s(const %s& other_value)\n"
     ": %s()\n" /* Base class DEFAULT constructor*/
     "{\n"
     "if (other_value.enum_value == %s) "
     "TTCN_error(\"Copying an unbound value of enumerated type %s.\");\n"
     "enum_value = other_value.enum_value;\n"
     "}\n\n", name, name, name, (use_runtime_2) ? "Enum_Type" : "Base_Type",
     unbound_value, dispname);

  /* assignment operators */
  def = mputprintf(def, "%s& operator=(int other_value);\n", name);
  src = mputprintf(src,
    "%s& %s::operator=(int other_value)\n"
    "{\n"
    "if (!is_valid_enum(other_value)) "
    "TTCN_error(\"Assigning unknown numeric value %%d to a variable "
    "of enumerated type %s.\", other_value);\n"
    "enum_value = (%s)other_value;\n"
    "return *this;\n"
    "}\n\n", name, name, dispname, enum_type);

  def = mputprintf(def, "%s& operator=(%s other_value);\n", name, enum_type);
  src = mputprintf(src,
    "%s& %s::operator=(%s other_value)\n"
    "{\n"
    "enum_value = other_value;\n"
    "return *this;\n"
    "}\n\n", name, name, enum_type);

  def = mputprintf(def, "%s& operator=(const %s& other_value);\n\n", name,
                   name);
  src = mputprintf(src,
    "%s& %s::operator=(const %s& other_value)\n"
    "{\n"
    "if (other_value.enum_value == %s) "
    "TTCN_error(\"Assignment of an unbound value of enumerated type %s.\");\n"
    "enum_value = other_value.enum_value;\n"
    "return *this;\n"
    "}\n\n", name, name, name, unbound_value, dispname);

  /* Comparison operators */
  def = mputprintf(def, "boolean operator==(%s other_value) const;\n",
                   enum_type);
  src = mputprintf(src,
    "boolean %s::operator==(%s other_value) const\n"
    "{\n"
    "if (enum_value == %s) "
    "TTCN_error(\"The left operand of comparison is an unbound value of "
    "enumerated type %s.\");\n"
    "return enum_value == other_value;\n"
    "}\n\n", name, enum_type, unbound_value, dispname);

  def = mputprintf(def, "boolean operator==(const %s& other_value) const;\n",
                   name);
  src = mputprintf(src,
    "boolean %s::operator==(const %s& other_value) const\n"
    "{\n"
    "if (enum_value == %s) "
    "TTCN_error(\"The left operand of comparison is an unbound value of "
    "enumerated type %s.\");\n"
    "if (other_value.enum_value == %s) "
    "TTCN_error(\"The right operand of comparison is an unbound value of "
    "enumerated type %s.\");\n"
    "return enum_value == other_value.enum_value;\n"
    "}\n\n", name, name, unbound_value, dispname, unbound_value, dispname);

  def = mputprintf(def, "inline boolean operator!=(%s other_value) "
                   "const { return !(*this == other_value); }\n", enum_type);

  def = mputprintf(def, "inline boolean operator!=(const %s& other_value) "
                   "const { return !(*this == other_value); }\n", name);

  def = mputprintf(def, "boolean operator<(%s other_value) const;\n",
                   enum_type);
  src = mputprintf(src,
    "boolean %s::operator<(%s other_value) const\n"
    "{\n"
    "if (enum_value == %s) "
    "TTCN_error(\"The left operand of comparison is an unbound value of "
    "enumerated type %s.\");\n"
    "return enum_value < other_value;\n"
    "}\n\n", name, enum_type, unbound_value, dispname);

  def = mputprintf(def, "boolean operator<(const %s& other_value) const;\n",
                   name);
  src = mputprintf(src,
    "boolean %s::operator<(const %s& other_value) const\n"
    "{\n"
    "if (enum_value == %s) "
    "TTCN_error(\"The left operand of comparison is an unbound value of "
    "enumerated type %s.\");\n"
    "if (other_value.enum_value == %s) "
    "TTCN_error(\"The right operand of comparison is an unbound value of "
    "enumerated type %s.\");\n"
    "return enum_value < other_value.enum_value;\n"
    "}\n\n", name, name, unbound_value, dispname, unbound_value, dispname);

  def = mputprintf(def, "boolean operator>(%s other_value) const;\n",
                   enum_type);
  src = mputprintf(src,
    "boolean %s::operator>(%s other_value) const\n"
    "{\n"
    "if (enum_value == %s) "
    "TTCN_error(\"The left operand of comparison is an unbound value of "
    "enumerated type %s.\");\n"
    "return enum_value > other_value;\n"
    "}\n\n", name, enum_type, unbound_value, dispname);

  def = mputprintf(def, "boolean operator>(const %s& other_value) const;\n",
                   name);
  src = mputprintf(src,
    "boolean %s::operator>(const %s& other_value) const\n"
    "{\n"
    "if (enum_value == %s) "
    "TTCN_error(\"The left operand of comparison is an unbound value of "
    "enumerated type %s.\");\n"
    "if (other_value.enum_value == %s) "
    "TTCN_error(\"The right operand of comparison is an unbound value of "
    "enumerated type %s.\");\n"
    "return enum_value > other_value.enum_value;\n"
    "}\n\n", name, name, unbound_value, dispname, unbound_value, dispname);

  def = mputprintf(def, "inline boolean operator<=(%s other_value) "
                   "const { return !(*this > other_value); }\n", enum_type);

  def = mputprintf(def, "inline boolean operator<=(const %s& other_value) "
                   "const { return !(*this > other_value); }\n", name);

  def = mputprintf(def, "inline boolean operator>=(%s other_value) "
                   "const { return !(*this < other_value); }\n", enum_type);

  def = mputprintf(def, "inline boolean operator>=(const %s& other_value) "
                   "const { return !(*this < other_value); }\n\n", name);

  /* Conversion function: enum_to_str */
  def = mputprintf(def, "static const char *enum_to_str(%s enum_par%s);\n",
    enum_type,
    edef->xerText ? ", boolean txt = FALSE" : "");
  src = mputprintf(src, "const char *%s::enum_to_str(%s enum_par%s)\n"
    "{\n"
    "switch (enum_par) {\n", name, enum_type,
    edef->xerText ? ", boolean txt" : "");
  for (i = 0; i < edef->nElements; i++) {
    if (edef->elements[i].text) {
      src = mputprintf(src,
        "case %s: if (txt) return \"%s\"; else return \"%s\";\n",
        edef->elements[i].name, edef->elements[i].text, edef->elements[i].dispname);
    }
    else {
      src = mputprintf(src, "case %s: return \"%s\";\n",
        edef->elements[i].name, edef->elements[i].dispname);
    }
  }
  src = mputstr(src, "default: return \"<unknown>\";\n"
    "}\n"
    "}\n\n");

  /* Conversion function: str_to_enum */
  def = mputprintf(def, "static %s str_to_enum(const char *str_par);\n",
    enum_type);
  src = mputprintf(src, "%s %s::str_to_enum(const char *str_par)\n"
    "{\n", qualified_enum_type, name);
  for (i = 0; i < edef->nElements; i++) {
      src = mputprintf(src, "if (!strcmp(str_par, \"%s\")) return %s;\n"
        "else ", edef->elements[i].dispname, edef->elements[i].name);
    } else if (!strcmp(edef->elements[i].text, edef->elements[i].descaped_text)) {
      src = mputprintf(src, "if (!strcmp(str_par, \"%s\") || !strcmp(str_par, \"%s\")) return %s;\n"
        "else ", edef->elements[i].text, edef->elements[i].dispname, edef->elements[i].name);
    } else {
      src = mputprintf(src, "if (!strcmp(str_par, \"%s\") || !strcmp(str_par, \"%s\") || !strcmp(str_par, \"%s\")) return %s;\n"
        "else ", edef->elements[i].text, edef->elements[i].descaped_text, edef->elements[i].dispname, edef->elements[i].name);
    }
  }
  src = mputprintf(src, "return %s;\n"
    "}\n\n", unknown_value);

  /* Checking function: is_valid_enum */
  def = mputstr(def, "static boolean is_valid_enum(int int_par);\n\n");
  src = mputprintf(src, "boolean %s::is_valid_enum(int int_par)\n"
    "{\n"
    "switch (int_par) {\n", name);
  for (i = 0; i < edef->nElements; i++) {
    src = mputprintf(src, "case %d:\n", edef->elements[i].value);
  }
  src = mputstr(src, "return TRUE;\n"
    "default:\n"
    "return FALSE;\n"
    "}\n"
    "}\n\n");

  /* TTCN-3 predefined function enum2int() */
  def = mputprintf(def, "static int enum2int(%s enum_par);\n", enum_type);
  src = mputprintf(src, "int %s::enum2int(%s enum_par)\n"
    "{\n"
    "if (enum_par==%s || enum_par==%s) TTCN_error(\"The argument of function "
      "enum2int() is an %%s value of enumerated type %s.\", "
      "enum_par==%s?\"unbound\":\"invalid\");\n"
    "return enum_par;\n"
    "}\n\n",
    name, enum_type, unbound_value, unknown_value, dispname, unbound_value);
  def = mputprintf(def, "static int enum2int(const %s& enum_par);\n", name);
  src = mputprintf(src, "int %s::enum2int(const %s& enum_par)\n"
    "{\n"
    "if (enum_par.enum_value==%s || enum_par.enum_value==%s) "
      "TTCN_error(\"The argument of function "
      "enum2int() is an %%s value of enumerated type %s.\", "
      "enum_par==%s?\"unbound\":\"invalid\");\n"
    "return enum_par.enum_value;\n"
    "}\n\n",
    name, name, unbound_value, unknown_value, dispname, unbound_value);
  def = mputstr(def,
    "int as_int() const { return enum2int(enum_value); }\n"
    "void from_int(int p_val) { *this = p_val; }\n");
Elemer Lelik's avatar
Elemer Lelik committed
  
  /* TTCN-3 predefined function int2enum() */
  def = mputstr(def, "void int2enum(int int_val);\n");
  src = mputprintf(src, "void %s::int2enum(int int_val)\n"
    "{\n"
    "if (!is_valid_enum(int_val)) "
    "TTCN_error(\"Assigning invalid numeric value %%d to a variable of "
    "enumerated type %s.\", int_val);\n"
    "enum_value = (%s)int_val;\n"
    "}\n\n", name, dispname, enum_type);

  /* miscellaneous members */
  def = mputprintf(def, "operator %s() const;\n", enum_type);
  src = mputprintf(src,
    "%s::operator %s() const\n"
    "{\n"
    "if (enum_value == %s) "
    "TTCN_error(\"Using the value of an unbound variable of enumerated "
    "type %s.\");\n"
    "return enum_value;\n"
    "}\n\n", name, qualified_enum_type, unbound_value, dispname);

  def = mputprintf(def, "inline boolean is_bound() const "
    "{ return enum_value != %s; }\n", unbound_value);
  def = mputprintf(def, "inline boolean is_value() const "
    "{ return enum_value != %s; }\n", unbound_value);
  def = mputprintf(def, "inline void clean_up()"
    "{ enum_value = %s; }\n", unbound_value);

  if (use_runtime_2) {
    def = mputstr(def,
      "boolean is_equal(const Base_Type* other_value) const;\n"
      "void set_value(const Base_Type* other_value);\n"
      "Base_Type* clone() const;\n"
      "const TTCN_Typedescriptor_t* get_descriptor() const;\n");
    src = mputprintf(src,
      "boolean %s::is_equal(const Base_Type* other_value) const "
        "{ return *this == *(static_cast<const %s*>(other_value)); }\n"
      "void %s::set_value(const Base_Type* other_value) "
        "{ *this = *(static_cast<const %s*>(other_value)); }\n"
      "Base_Type* %s::clone() const { return new %s(*this); }\n"
      "const TTCN_Typedescriptor_t* %s::get_descriptor() const "
        "{ return &%s_descr_; }\n",
      name, name,
      name, name,
      name, name,
      name, name);
  } else {
    def = mputstr(def,
      "inline boolean is_present() const { return is_bound(); }\n");
  }

  def = mputstr(def, "void log() const;\n");
  src = mputprintf(src,
    "void %s::log() const\n"
    "{\n"
    "if (enum_value != %s) TTCN_Logger::log_event_enum(enum_to_str(enum_value), enum_value);\n"
    "else TTCN_Logger::log_event_unbound();\n"
    "}\n\n", name, unbound_value);

  def = mputstr(def, "void set_param(Module_Param& param);\n");
  src = mputprintf
    (src,
     "void %s::set_param(Module_Param& param)\n"
     "{\n"
     "  param.basic_check(Module_Param::BC_VALUE, \"enumerated value\");\n", name);
  if (use_runtime_2) {
    src = mputprintf(src,
Elemer Lelik's avatar
Elemer Lelik committed
     "  Module_Param_Ptr m_p = &param;\n"
Elemer Lelik's avatar
Elemer Lelik committed
     "  if (param.get_type() == Module_Param::MP_Reference) {\n"
     /* enumerated values are also treated as references (containing only 1 name) by the parser;
        first check if the reference name is a valid enumerated value */
     "    char* enum_name = param.get_enumerated();\n"
     /* get_enumerated() returns NULL if the reference contained more than one name */
     "    enum_value = (enum_name != NULL) ? str_to_enum(enum_name) : %s;\n"
     "    if (is_valid_enum(enum_value)) {\n"
     "      return;\n"
     "    }\n"
     /* it's not a valid enum value => dereference it! */
Elemer Lelik's avatar
Elemer Lelik committed
     "    m_p = param.get_referenced_param();\n"
     "  }\n", unknown_value);
  }
  src = mputprintf(src,
     "  if (%sget_type()!=Module_Param::MP_Enumerated) param.type_error(\"enumerated value\", \"%s\");\n"
     "  enum_value = str_to_enum(%sget_enumerated());\n"
Elemer Lelik's avatar
Elemer Lelik committed
     "  if (!is_valid_enum(enum_value)) {\n"
     "    param.error(\"Invalid enumerated value for type %s.\");\n"
     "  }\n"
     "}\n\n", use_runtime_2 ? "m_p->" : "param.", dispname,
     use_runtime_2 ? "m_p->" : "param.", dispname);
Elemer Lelik's avatar
Elemer Lelik committed
  
  if (use_runtime_2) {
    def = mputstr(def, "Module_Param* get_param(Module_Param_Name& param_name) const;\n");
    src = mputprintf
      (src,
      "Module_Param* %s::get_param(Module_Param_Name& /* param_name */) const\n"
      "{\n"
      "  if (!is_bound()) {\n"
      "    return new Module_Param_Unbound();\n"
      "  }\n"
      "  return new Module_Param_Enumerated(mcopystr(enum_to_str(enum_value)));\n"
      "}\n\n", name);
  }

  /* encoders/decoders */
  def = mputstr(def, "void encode_text(Text_Buf& text_buf) const;\n");
  src = mputprintf(src,
    "void %s::encode_text(Text_Buf& text_buf) const\n"
    "{\n"
    "if (enum_value == %s) "
    "TTCN_error(\"Text encoder: Encoding an unbound value of enumerated "
    "type %s.\");\n"
    "text_buf.push_int(enum_value);\n"
    "}\n\n", name, unbound_value, dispname);

  def = mputstr(def, "void decode_text(Text_Buf& text_buf);\n");
  src = mputprintf(src,
    "void %s::decode_text(Text_Buf& text_buf)\n"
    "{\n"
    "enum_value = (%s)text_buf.pull_int().get_val();\n"
    "if (!is_valid_enum(enum_value)) "
    "TTCN_error(\"Text decoder: Unknown numeric value %%d was "
    "received for enumerated type %s.\", enum_value);\n"
    "}\n\n", name, enum_type, dispname);

  /* BER functions */
  if(ber_needed || raw_needed || text_needed || xer_needed || json_needed
    || oer_needed) {
    def_encdec(name, &def, &src, ber_needed,
               raw_needed,text_needed, xer_needed, json_needed, oer_needed, TRUE);
  }
  if(ber_needed) {
    src=mputprintf(src,
       "ASN_BER_TLV_t* %s::BER_encode_TLV(const TTCN_Typedescriptor_t&"
       " p_td, unsigned p_coding) const\n"
       "{\n"
       "  BER_chk_descr(p_td);\n"
       "  ASN_BER_TLV_t *new_tlv=BER_encode_chk_bound(is_bound());\n"
       "  if(!new_tlv) {\n"
       "    BER_encode_chk_enum_valid(p_td, is_valid_enum(enum_value),"
       " enum_value);\n"
       "    new_tlv=BER_encode_TLV_INTEGER(p_coding, enum_value);\n"
       "  }\n"
       "  new_tlv=ASN_BER_V2TLV(new_tlv, p_td, p_coding);\n"
       "  return new_tlv;\n"
       "}\n"
       "\n"
       "boolean %s::BER_decode_TLV(const TTCN_Typedescriptor_t& p_td,"
       " const ASN_BER_TLV_t& p_tlv, unsigned L_form)\n"
       "{\n"
       "  enum_value = %s;\n"
       "  BER_chk_descr(p_td);\n"
       "  ASN_BER_TLV_t stripped_tlv;\n"
       "  BER_decode_strip_tags(*p_td.ber, p_tlv, L_form, stripped_tlv);\n"
       "  TTCN_EncDec_ErrorContext ec(\"While decoding ENUMERATED type %s: "
	"\");\n"
       "  int tmp_mfr;\n"
       "  if (BER_decode_TLV_INTEGER(stripped_tlv, L_form, tmp_mfr)) {\n"
       "    BER_decode_chk_enum_valid(p_td, is_valid_enum(tmp_mfr), tmp_mfr);\n"
       "    enum_value = (%s)tmp_mfr;\n"
       "    return TRUE;\n"
       "  } else return FALSE;\n"
       "}\n\n"
       , name, name, unbound_value, dispname, enum_type);
  } /* if ber_needed */
  /* new TEXT functions */
  if(text_needed){
    src = mputprintf(src,
      "int %s::TEXT_encode(const TTCN_Typedescriptor_t& p_td,"
      "TTCN_Buffer& p_buf) const{\n"
      "int encoded_length=0;\n"
      "if(p_td.text->begin_encode){\n"
      "  p_buf.put_cs(*p_td.text->begin_encode);\n"
      "  encoded_length+=p_td.text->begin_encode->lengthof();\n"
      "}\n"
      "  if (enum_value == %s) {\n"
      "    TTCN_EncDec_ErrorContext::error\n"
      "      (TTCN_EncDec::ET_UNBOUND, \"Encoding an unbound value of "
      "enumerated type %s.\");\n"
      "    if(p_td.text->end_encode){\n"
      "      p_buf.put_cs(*p_td.text->end_encode);\n"
      "      encoded_length+=p_td.text->end_encode->lengthof();\n"
      "    }\n"
      "    return encoded_length;\n"
      "  }\n"
      "if(p_td.text->val.enum_values==NULL){\n"
      "  int len=strlen(enum_to_str(enum_value));\n"
      "  p_buf.put_s(len,(const unsigned char*)enum_to_str(enum_value));\n"
      "  encoded_length+=len;\n"
      "} else {\n"
      "switch(enum_value){\n"
      , name, unbound_value, dispname
      );
    for(i=0;i<edef->nElements;i++){
      src = mputprintf(src,
        "case %s: \n"
        "if(p_td.text->val.enum_values[%lu].encode_token){\n"
        " p_buf.put_cs(*p_td.text->val.enum_values[%lu].encode_token);\n"
        " encoded_length+=p_td.text->val.enum_values[%lu]"
        ".encode_token->lengthof();\n"
        "} else { "
        "  int len=strlen(enum_to_str(enum_value));\n"
        "  p_buf.put_s(len,(const unsigned char*)enum_to_str(enum_value));\n"
        "  encoded_length+=len;\n"
        "}\n"
        "break;\n"
         ,edef->elements[i].name,
         (unsigned long) i,(unsigned long) i,(unsigned long) i
       );
    }

    src = mputstr(src,
      " default:\n"
      "break;\n"
      "}\n"
      "}\n"
      " if(p_td.text->end_encode){\n"
      "   p_buf.put_cs(*p_td.text->end_encode);\n"
      "   encoded_length+=p_td.text->end_encode->lengthof();\n"
      " }\n"
      " return encoded_length;\n"
      "}\n"
      );
    src = mputprintf(src,
      "int %s::TEXT_decode(const TTCN_Typedescriptor_t& p_td,"
Elemer Lelik's avatar
Elemer Lelik committed
      " TTCN_Buffer& p_buf, Limit_Token_List&, boolean no_err, boolean){\n"
      "  int decoded_length=0;\n"
      "  int str_len=0;\n"
      "  if(p_td.text->begin_decode){\n"
      "    int tl;\n"
      "    if((tl=p_td.text->begin_decode->match_begin(p_buf))<0){\n"
      "          if(no_err)return -1;\n"
      "          TTCN_EncDec_ErrorContext::error\n"
      "              (TTCN_EncDec::ET_TOKEN_ERR, \"The specified token '%%s'"
      " not found for '%%s': \",(const char*)*(p_td.text->begin_decode)"
      ", p_td.name);\n"
      "          return 0;\n"
      "        }\n"
      "    decoded_length+=tl;\n"
      "    p_buf.increase_pos(tl);\n"
      "  }\n"
      "  if(p_buf.get_read_len()<1 && no_err) return -1;\n"
      ,name
     );


    for(i=0;i<edef->nElements;i++){
      src = mputprintf(src,
       "if((str_len=p_td.text->val.enum_values[%lu].decode_token->"
       "match_begin(p_buf))!=-1){\n"
       "  enum_value=%s;\n"
       "} else "
       ,(unsigned long) i,edef->elements[i].name
      );
    }

    src = mputstr(src,
      " {\n"
      "    if(no_err)return -1;\n"
      "    TTCN_EncDec_ErrorContext::error"
      "(TTCN_EncDec::ET_TOKEN_ERR, \"No enum token found for '%s': \""
      ",p_td.name);\n"
      "    return decoded_length;\n"
      "}\n"
      "  decoded_length+=str_len;\n"
      "  p_buf.increase_pos(str_len);\n"
      "  if(p_td.text->end_decode){\n"
      "    int tl;\n"
      "    if((tl=p_td.text->end_decode->match_begin(p_buf))<0){\n"
      "          if(no_err)return -1;\n"
      "          TTCN_EncDec_ErrorContext::error"
      "(TTCN_EncDec::ET_TOKEN_ERR, \"The specified token '%s'"
      " not found for '%s': \",(const char*)*(p_td.text->end_decode)"
      ",p_td.name);\n"
      "          return decoded_length;\n"
      "        }\n"
      "    decoded_length+=tl;\n"
      "    p_buf.increase_pos(tl);\n"
      "  }\n"
      "  return decoded_length;\n"
      "}\n"
     );
  }
  /* new RAW functions */
  if (raw_needed) {
    int min_bits = 0;
    int max_val = edef->firstUnused;
    size_t a;
    for (a = 0; a < edef->nElements; a++) {
      int val = edef->elements[a].value;
      if (abs(max_val) < abs(val)) max_val = val;
    }
    if (max_val < 0) {
      min_bits = 1;
      max_val = -max_val;
    }
    while (max_val) {
      min_bits++;
      max_val /= 2;
    }
    src = mputprintf(src,
      "int %s::RAW_decode(const TTCN_Typedescriptor_t& p_td,TTCN_Buffer& p_buf,"
      "int limit, raw_order_t top_bit_ord, boolean no_err, int, boolean, "
      "const RAW_Force_Omit*)\n"
      "{\n"
      "  int decoded_value = 0;\n"
      "  int decoded_length = RAW_decode_enum_type(p_td, p_buf, limit, "
      "top_bit_ord, decoded_value, %d, no_err);\n"
      "  if (decoded_length < 0) return decoded_length;\n"
      "  if (is_valid_enum(decoded_value)) "
      "enum_value = (%s)decoded_value;\n"
      "  else {\n"
      "    if(no_err){\n"
      "     return -1;\n"
      "    } else {\n"
      "    TTCN_EncDec_ErrorContext::error\n"
      "      (TTCN_EncDec::ET_ENC_ENUM, \"Invalid enum value '%%d'"
      " for '%%s': \",decoded_value, p_td.name);\n"
      "    enum_value = %s;\n"
      "    }\n"
      "  }\n"
      "  return decoded_length;\n"
      "}\n\n", name, min_bits, enum_type, unknown_value);
    src = mputprintf(src,
      "int %s::RAW_encode(const TTCN_Typedescriptor_t& p_td, "
      "RAW_enc_tree& myleaf) const\n"
      "{\n"
      "  return RAW_encode_enum_type(p_td, myleaf, (int)enum_value, %d);\n"
      "}\n\n", name, min_bits);
  } /* if raw_needed */

  if (xer_needed) { /* XERSTUFF encoder codegen for enum */
    /* FIXME This is redundant,
     * because the code is identical to BaseType::can_start()
     * and enum types are derived from BaseType or EnumType (which, in turn,
     * is derived from BaseType). However, the declaration of can_start()
     * is written by def_encdec() in encdec.c, which doesn't know
     * that this is an enum type. Maybe we need to pass an is_enum to
     * def_encdec, and then we can omit generating can_start() for enums.
     */
    src = mputprintf(src,
      "boolean %s::can_start(const char *name, const char *uri, "
      "const XERdescriptor_t& xd, unsigned int flavor, unsigned int) {\n"
      "  boolean exer = is_exer(flavor);\n"
      "  return check_name(name, xd, exer) && (!exer || check_namespace(uri, xd));\n"
      "}\n\n"
      , name
      );

    src = mputprintf(src,
      "int %s::XER_encode(const XERdescriptor_t& p_td, TTCN_Buffer& p_buf,"
      " unsigned int p_flavor, unsigned int, int p_indent, embed_values_enc_struct_t*) const\n"
      "{\n"
      "  int encoded_length=(int)p_buf.get_len();\n"
      "  const boolean e_xer = is_exer(p_flavor);\n"
      "  p_flavor |= (SIMPLE_TYPE | BXER_EMPTY_ELEM);\n"
      "  if (begin_xml(p_td, p_buf, p_flavor, p_indent, FALSE) == -1) "
      "--encoded_length;\n"
      "  if (!e_xer) p_buf.put_c('<');\n"
      , name
    );
    if (edef->xerUseNumber) {
      src = mputstr(src,
        "  if (e_xer) {\n" /* compile-time instead of p_td.useNumber */
        "    char sval[24];\n" /* unsigned 64 bits fit in 20 characters */
        "    int slen = snprintf(sval, 24, \"%d\", enum_value);\n"
        "    if (slen > 0) p_buf.put_s((size_t)slen, (const unsigned char*)sval);\n"
        "  }\n"
        "  else" /* no newline, will take over following curly */
      );
    }
    src = mputprintf(src,
      "  {\n"
      "    const char * enumval = enum_to_str(enum_value%s);\n"
      "    p_buf.put_s(strlen(enumval), (const unsigned char*)enumval);\n"
      "  }\n"
      "  if (!e_xer) p_buf.put_s(2, (const unsigned char*)\"/>\");\n"
      "  end_xml(p_td, p_buf, p_flavor, p_indent, FALSE);\n"
      , edef->xerText ? ", e_xer" : ""
    );
    src = mputstr(src,
      "  return (int)p_buf.get_len() - encoded_length;\n"
      "}\n\n");

    src = mputprintf(src, /* XERSTUFF decoder codegen for enum */
#ifndef NDEBUG
      "// written by %s in " __FILE__ " at %d\n"
#endif
      "int %s::XER_decode(const XERdescriptor_t& p_td, XmlReaderWrap& p_reader,"
Elemer Lelik's avatar
Elemer Lelik committed
      " unsigned int p_flavor,  unsigned int /*p_flavor2*/, embed_values_dec_struct_t*)\n"
      "{\n"
      "  int rd_ok = 1, type;\n"

      "  const boolean e_xer = is_exer(p_flavor);\n"
      "  const boolean name_tag = !((!e_xer && is_record_of(p_flavor)) || (e_xer && ((p_td.xer_bits & UNTAGGED) ||(is_record_of(p_flavor) && is_exerlist(p_flavor)))));\n"
      "  if (e_xer && ((p_td.xer_bits & XER_ATTRIBUTE) || is_exerlist(p_flavor))) {\n"
      "    if ((p_td.xer_bits & XER_ATTRIBUTE)) verify_name(p_reader, p_td, e_xer);\n"
      "    const char * value = (const char *)p_reader.Value();\n"
      "    if (value) {\n"
#ifndef NDEBUG
      , __FUNCTION__, __LINE__
#endif
      , name);
    if (edef->xerUseNumber) {
      src = mputprintf(src,
        "        int tempvalue;\n"
        "        sscanf(value, \"%%d\", &tempvalue);\n"
        "        if (is_valid_enum(tempvalue)) enum_value = (%s)tempvalue;\n" /* static_cast would be nice */
        "        else enum_value = %s;\n", enum_type, unknown_value
      );
    }
    else {
      src = mputstr(src, "        enum_value = str_to_enum(value);\n");
    }
    src = mputstr(src,
      "    }\n"
      /* The caller should do AdvanceAttribute() */
      "  }\n"
      "  else {\n"
      "    if (name_tag)"
      /* Go past the opening tag with the type name */
      "      for (; rd_ok == 1; rd_ok = p_reader.Read()) {\n"
      "        type = p_reader.NodeType();\n"
      "        if (XML_READER_TYPE_ELEMENT == type) {\n"
      "          rd_ok = p_reader.Read();\n"
      "          break;\n"
      "        }\n"
      "      }\n"
      /* Go to the element with the actual data (EmptyElementEnumerated) */
      "    for (; rd_ok == 1; rd_ok = p_reader.Read()) {\n"
      "      type = p_reader.NodeType();\n"
      "      if (!e_xer && XML_READER_TYPE_ELEMENT == type) break;\n"
      "      if (XML_READER_TYPE_TEXT == type) break;\n"
      "    }\n"
      "    const char *local_name = e_xer ? (const char *)p_reader.Value() : (const char *)p_reader.Name();\n"
      /*                                       TextEnumerated                EmptyElementEnumerated */
      "    if (!local_name) ; else");
    if (edef->xerUseNumber) {
      src = mputprintf(src,
        "    if (e_xer) {\n"
        "      int tempvalue;\n"
        "      sscanf(local_name, \"%%d\", &tempvalue);\n"
        "      if (is_valid_enum(tempvalue)) enum_value = (%s)tempvalue;\n" /* static_cast would be nice */
        "      else enum_value = %s;\n"
        "    }\n"
        "    else" /* no newline */ , enum_type, unknown_value
        );
    }
    {
      src = mputstr(src,
        "    {\n"
        "      for (; '\\t'==*local_name || '\\n'==*local_name; ++local_name) ;\n" /* crutch while default-for-empty always puts in a newline */
        "      enum_value = str_to_enum(local_name);\n"
        "    }\n");
    }
    src = mputprintf(src,
      "    if (name_tag)\n"
      "      for (rd_ok = p_reader.Read(); rd_ok == 1; rd_ok = p_reader.Read()) {\n"
      "        type = p_reader.NodeType();\n"
      "        if (XML_READER_TYPE_END_ELEMENT == type) {\n"
      "          p_reader.Read();\n"
      "          break;\n"
      "        }\n"
      "      }\n"
      "    else p_reader.Read();\n"
      "  }\n"
      "  if (e_xer && (p_flavor & EXIT_ON_ERROR) && %s == enum_value) clean_up();\n" // set to unbound if decoding failed
      "  int decoded_length = 0;\n"
      "  return decoded_length;\n"
      "}\n\n"
      , unknown_value);
  }
  if (json_needed) {
    // JSON encode
    src = mputprintf(src,
      "int %s::JSON_encode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean) const\n"
      "{\n"
      "  if (enum_value == %s) {\n"
      "    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_UNBOUND,\n"
      "      \"Encoding an unbound value of enumerated type %s.\");\n"
      "    return -1;\n"
      "  }\n\n"
      "  if (p_td.json->use_null) {\n"
      "    return p_tok.put_next_token(JSON_TOKEN_LITERAL_NULL);\n"
      "  }\n"
      "    tmp_str = mprintf(\"%%d\", enum_value);\n"
      "  }\n"
      "  else {\n"
      "    boolean text_found = false;\n"
      "    for (size_t i = 0; i < p_td.json->nof_enum_texts; ++i) {\n"
      "      if (p_td.json->enum_texts[i].index == enum_value) {\n"
      "        tmp_str = mprintf(\"\\\"%%s\\\"\", p_td.json->enum_texts[i].text);\n"
      "        text_found = true;\n"
      "        break;\n"
      "      }\n"
      "    }\n"
      "    if (!text_found) {\n"
      "      tmp_str = mprintf(\"\\\"%%s\\\"\", enum_to_str(enum_value));\n"
      "    }\n"
      "  }\n"
      "  int enc_len = p_tok.put_next_token(p_td.json->as_number ? "
      "JSON_TOKEN_NUMBER : JSON_TOKEN_STRING, tmp_str);\n"
      "  Free(tmp_str);\n"
      "  return enc_len;\n"
      "}\n\n"
      , name, unbound_value, dispname);
    
    // JSON decode
    src = mputprintf(src,
      "int %s::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, boolean, int)\n"
      "{\n"
      "  json_token_t token = JSON_TOKEN_NONE;\n"
      "  char* value = 0;\n"
      "  size_t value_len = 0;\n"
      "  boolean error = FALSE;\n"
      "  boolean use_default = FALSE;\n"
      "  if (p_td.json->default_value.type == JD_STANDARD && 0 == p_tok.get_buffer_length()) {\n"
      "    *this = *static_cast<const %s*>(p_td.json->default_value.val);\n"
      "    return dec_len;\n"
      "  }\n"
      "  if (p_td.json->default_value.type == JD_LEGACY && 0 == p_tok.get_buffer_length()) {\n"
      // No JSON data in the buffer -> use default value
      "    value = const_cast<char*>(p_td.json->default_value.str);\n"
      "    value_len = strlen(value);\n"
      "  } else {\n"
      "    dec_len = p_tok.get_next_token(&token, &value, &value_len);\n"
      "  }\n"
      "  if (JSON_TOKEN_ERROR == token) {\n"
      "    JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_BAD_TOKEN_ERROR, \"\");\n"
      "    return JSON_ERROR_FATAL;\n"
      "  }\n"
      "  else if (JSON_TOKEN_LITERAL_NULL == token && p_td.json->use_null) {\n"
      "    enum_value = %s;\n"
      "  }\n"
      "  else if (!p_td.json->use_null && ((JSON_TOKEN_STRING == token && !p_td.json->as_number) || use_default)) {\n"
      "    if (use_default || (value_len > 2 && value[0] == '\\\"' && value[value_len - 1] == '\\\"')) {\n"
      "      if (!use_default) value[value_len - 1] = 0;\n"
      "      boolean text_found = false;\n"
      "      for (size_t i = 0; i < p_td.json->nof_enum_texts; ++i) {\n"
      "        if (strcmp(p_td.json->enum_texts[i].text, value + (use_default ? 0 : 1)) == 0) {\n"
      "          enum_value = static_cast<%s>(p_td.json->enum_texts[i].index);\n"
      "          text_found = true;\n"
      "          break;\n"
      "        }\n"
      "      }\n"
      "      if (!text_found) {\n"
      "        enum_value = str_to_enum(value + (use_default ? 0 : 1));\n"
      "      }\n"
      "      if (!use_default) value[value_len - 1] = '\\\"';\n"
      "      if (%s == enum_value) {\n"
      "      }\n"
      "    } else {\n"
      "    }\n"
      "  else if (!p_td.json->use_null && JSON_TOKEN_NUMBER == token && p_td.json->as_number) {\n"
      "    char* value_str = mcopystrn(value, value_len);\n"
      "    int number = atoi(value_str);\n"
      "    if (strchr(value_str, '.') != NULL || strchr(value_str, 'e') != NULL "
      "|| strchr(value_str, 'E') != NULL) {\n"
      "      error = TRUE;\n"
      "    }\n"
      "    else if (is_valid_enum(number)) {\n"
      "      enum_value = static_cast<%s>(number);\n"
      "    }\n"
      "    else {\n"
      "      error = TRUE;\n"
      "    }\n"
      "    Free(value_str);\n"
      "  }\n"
      "  else {\n"
      "    enum_value = %s;\n"
      "    return JSON_ERROR_INVALID_TOKEN;\n"
      "  }\n\n"
      "  if (error) {\n"
      "    JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_FORMAT_ERROR, "
      "p_td.json->as_number ? \"number\" : \"string\", \"enumerated\");\n"
      "    enum_value = %s;\n"
      "    return JSON_ERROR_FATAL;\n"
      "  }\n"
      "}\n\n"
      , name, name, edef->elements[0].name, enum_type, unknown_value, enum_type
  
  if (oer_needed) {
    // OER encode
    src = mputprintf(src,
      "int %s::OER_encode(const TTCN_Typedescriptor_t&, TTCN_Buffer& p_buf) const\n"
      "{\n"
      "  if (enum_value == %s) {\n"
      "    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_UNBOUND,\n"
      "      \"Encoding an unbound value of enumerated type %s.\");\n"
      "    return -1;\n"
      "  }\n\n"
      "  if (enum_value >= 0 && enum_value < 128) {\n"
      "    char c = enum_value;\n"
      "    p_buf.put_c(c);\n"
      "  } else {\n"
      // This case is the same as encoding a not restricted integer except
      // the first bit is set to 1 before decoding
      "    INTEGER intval(enum_value);\n"
      "    TTCN_Buffer buf;\n"
      "    intval.OER_encode(INTEGER_descr_, buf);\n"
      "    unsigned char* uc = const_cast<unsigned char*>(buf.get_data());\n"
      "    *uc |= 1 << 7;\n"
      "    p_buf.put_buf(buf);\n"
      "  }\n"
      "  return 0;\n"
      "}\n\n"
      , name, unbound_value, dispname);
    
    // OER decode
    src = mputprintf(src,
      "int %s::OER_decode(const TTCN_Typedescriptor_t&, TTCN_Buffer& p_buf, OER_struct& p_oer)\n"
      "{\n"
      "  const unsigned char* uc = p_buf.get_read_data();\n"
      "  if (!(uc[0] & 0x80)) {\n"
      "    if (is_valid_enum(uc[0])) {\n"
      "      enum_value = static_cast<%s>(uc[0]);\n"
      "    } else {\n"
      "      enum_value = %s;\n"
      "    }\n"
      "  } else {\n"
      // This case is the same as decoding a not restricted integer except
      // the first bit is set to 0
      "    unsigned char* uc2 = const_cast<unsigned char*>(p_buf.get_read_data());\n"
      "    uc2[0] &= ~0x80;\n"
      "    INTEGER intval;\n"
      "    intval.OER_decode(INTEGER_descr_, p_buf, p_oer);\n"
      "    if (is_valid_enum(intval.get_val().get_val())) {\n"
      "      enum_value = static_cast<%s>(intval.get_val().get_val());\n"
      "    } else {\n"
      "      enum_value = %s;\n"
      "    }\n"
      "  }\n"
      "  return 0;\n"
      "}\n\n"
      , name, enum_type, unknown_value, enum_type, unknown_value);
  }
  /* end of class */
  def = mputstr(def, "};\n\n");

  output->header.class_defs = mputstr(output->header.class_defs, def);
  Free(def);
  output->source.methods = mputstr(output->source.methods, src);
  Free(src);


  Free(enum_type);
  Free(qualified_enum_type);
  Free(unknown_value);
  Free(unbound_value);
}

void defEnumTemplate(const enum_def *edef, output_struct *output)
{
  char *def = NULL, *src = NULL;
  const char *name = edef->name, *dispname = edef->dispname;

Elemer Lelik's avatar
Elemer Lelik committed
  char *enum_type, *unbound_value, *unknown_value;
  enum_type = mprintf("%s::enum_type", name);
  unbound_value = mprintf("%s::UNBOUND_VALUE", name);
Elemer Lelik's avatar
Elemer Lelik committed
  unknown_value = mprintf("%s::UNKNOWN_VALUE", name);

  /* Class declaration */
  output->header.class_decls = mputprintf(output->header.class_decls,
    "class %s_template;\n", name);

  /* Class definition */
  def = mputprintf(def, "class %s_template : public Base_Template {\n"