/******************************************************************************
 * Copyright (c) 2000-2025 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:
 *   Baji, Laszlo
 *   Balasko, Jeno
 *   Baranyi, Botond
 *   Beres, Szabolcs
 *   Cserveni, Akos
 *   Delic, Adam
 *   Feher, Csaba
 *   Forstner, Matyas
 *   Kovacs, Ferenc
 *   Kremer, Peter
 *   Ormandi, Matyas
 *   Raduly, Csaba
 *   Szabados, Kristof
 *   Szabo, Bence Janos
 *   Szabo, Janos Zoltan – initial implementation
 *   Szalai, Gabor
 *
 ******************************************************************************/
#include <string.h>
#include "datatypes.h"
#include "../common/memory.h"
#include "record.h"
#include "encdec.h"

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

static void defEmptyRecordClass(const struct_def *sdef,
                                output_struct *output);

static void defEmptyRecordTemplate(const char *name, const char *dispname,
  const char* base_class_name, output_struct *output);

/** this is common code for both empty and non-empty cases, called from
 *  both functions for template generation */
static void defCommonRecordTemplate(const char *name,
  char **def, char **src);

/** code generation for original runtime */
static void defRecordClass1(const struct_def *sdef, output_struct *output);
static void defRecordTemplate1(const struct_def *sdef, output_struct *output);
/** code generation for alternative runtime (TITAN_RUNTIME_2) */
static void defRecordClass2(const struct_def *sdef, output_struct *output);
static void defRecordTemplate2(const struct_def *sdef, output_struct *output);

void defRecordClass(const struct_def *sdef, output_struct *output)
{
  if (use_runtime_2 && !sdef->containsClass) defRecordClass2(sdef, output);
  else defRecordClass1(sdef, output);
}

void defRecordTemplate(const struct_def *sdef, output_struct *output)
{
  if (use_runtime_2 && !sdef->containsClass) defRecordTemplate2(sdef, output);
  else defRecordTemplate1(sdef, output);
}

struct raw_option_struct {
  boolean lengthto; /* indicates whether this field contains a length */
  int lengthof; /* how many length indicators this field counted in */
  int *lengthoffield; /* the list of length indicator field indices */
  boolean pointerto;
  int pointerof;
  boolean ptrbase;
  int extbitgroup;
  int tag_type;
  boolean delayed_decode; /* indicates whether the field has to be decoded
			 out of order (later) */
  int nof_dependent_fields; /* list of fields that are to be decoded */
  int *dependent_fields; /* after this field */
};

static char *genRawFieldDecodeLimit(char *src, const struct_def *sdef,
  int i, const struct raw_option_struct *raw_options);

static char *genRawDecodeRecordField(char *src, const struct_def *sdef,
  int i, const struct raw_option_struct *raw_options, boolean delayed_decode,
  int *prev_ext_group);

static void set_raw_options(const struct_def *sdef,
  struct raw_option_struct *raw_options, boolean* haslengthto,
  boolean* haspointer, boolean* hascrosstag, boolean* has_ext_bit);

static char *generate_raw_coding(char *src,
  const struct_def *sdef, struct raw_option_struct *raw_options,
  boolean haspointer, boolean hascrosstag, boolean has_ext_bit);
static char *generate_raw_coding_negtest(char *src,
  const struct_def *sdef, struct raw_option_struct *raw_options);

void set_raw_options(const struct_def *sdef,
  struct raw_option_struct *raw_options, boolean *haslengthto,
  boolean *haspointer, boolean *hascrosstag, boolean *has_ext_bit)
{
    size_t i;
    for (i = 0; i < sdef->nElements; i++) {
      raw_options[i].lengthto = FALSE;
      raw_options[i].lengthof = 0;
      raw_options[i].lengthoffield = NULL;
      raw_options[i].pointerto = FALSE;
      raw_options[i].pointerof = 0;
      raw_options[i].ptrbase = FALSE;
      raw_options[i].extbitgroup = 0;
      raw_options[i].tag_type = 0;
      raw_options[i].delayed_decode = FALSE;
      raw_options[i].nof_dependent_fields = 0;
      raw_options[i].dependent_fields = NULL;
    }
    *haslengthto = FALSE;
    *haspointer = FALSE;
    *hascrosstag = FALSE;
    *has_ext_bit = sdef->hasRaw && sdef->raw.extension_bit!=XDEFNO &&
                                  sdef->raw.extension_bit!=XDEFDEFAULT;
    for(i=0;i<sdef->nElements;i++){
      if(sdef->elements[i].hasRaw &&
                                 sdef->elements[i].raw.crosstaglist.nElements){
        *hascrosstag = TRUE;
        break;
      }
    }
    if(sdef->hasRaw){ /* fill tag_type. 0-No tag, >0 index of the tag + 1 */
      for(i=0;i<sdef->raw.taglist.nElements;i++){
        raw_options[sdef->raw.taglist.list[i].fieldnum].tag_type= i+1;
      }
      for(i=0;i<sdef->raw.ext_bit_goup_num;i++){
        int k;
        for(k=sdef->raw.ext_bit_groups[i].from;
                                    k<=sdef->raw.ext_bit_groups[i].to;k++)
            raw_options[k].extbitgroup=i+1;
      }
    }
    for(i=0;i<sdef->nElements;i++){
      if (sdef->elements[i].hasRaw && sdef->elements[i].raw.lengthto_num > 0) {
        int j;
        *haslengthto = TRUE;
        raw_options[i].lengthto = TRUE;
        for(j = 0; j < sdef->elements[i].raw.lengthto_num; j++) {
	  int field_index = sdef->elements[i].raw.lengthto[j];
	  raw_options[field_index].lengthoffield = (int*)
	    Realloc(raw_options[field_index].lengthoffield,
	      (raw_options[field_index].lengthof + 1) * sizeof(int));
	  raw_options[field_index].lengthoffield[
	    raw_options[field_index].lengthof] = i;
	  raw_options[field_index].lengthof++;
        }
      }
      if(sdef->elements[i].hasRaw && sdef->elements[i].raw.pointerto!=-1){
        raw_options[i].pointerto = TRUE;
        raw_options[sdef->elements[i].raw.pointerto].pointerof=i+1;
        *haspointer = TRUE;
        raw_options[sdef->elements[i].raw.pointerbase].ptrbase = TRUE;
      }
    }
    if (sdef->kind == RECORD && *hascrosstag) {
      /* looking for fields that require delayed decoding because of
	 forward references in CROSSTAG */
      for (i = 0; i < sdef->nElements; i++) {
        int j;
	/* we are looking for a field index that is greater than i */
	size_t max_index = i;
	if (!sdef->elements[i].hasRaw) continue;
	for (j = 0; j < sdef->elements[i].raw.crosstaglist.nElements; j++) {
	  int k;
	  rawAST_coding_taglist *crosstag =
	    sdef->elements[i].raw.crosstaglist.list + j;
	  for (k = 0; k < crosstag->nElements; k++) {
	    rawAST_coding_field_list *keyid = crosstag->fields + k;
	    if (keyid->nElements >= 1) {
	      int field_index = keyid->fields[0].nthfield;
	      if (field_index > max_index) max_index = field_index;
	    }
	  }
	}
	if (max_index > i) {
	  raw_options[i].delayed_decode = TRUE;
	  raw_options[max_index].nof_dependent_fields++;
	  raw_options[max_index].dependent_fields = (int*)
	    Realloc(raw_options[max_index].dependent_fields,
	      raw_options[max_index].nof_dependent_fields *
	      sizeof(*raw_options[max_index].dependent_fields));
	  raw_options[max_index].dependent_fields[
	    raw_options[max_index].nof_dependent_fields - 1] = i;
	}
      }
    }
}

char* generate_raw_coding(char* src,
  const struct_def *sdef, struct raw_option_struct *raw_options,
    boolean haspointer, boolean hascrosstag, boolean has_ext_bit)
{
  int i;
  const char *name = sdef->name;

  if (sdef->kind == SET) { /* set decoder start */
    size_t mand_num = 0;
    for (i = 0; i < sdef->nElements; i++) {
      if (!sdef->elements[i].isOptional) mand_num++;
    }
    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, int, boolean, const RAW_Force_Omit* force_omit)\n"
      "{\n"
        "int prepaddlength = p_buf.increase_pos_padd(p_td.raw->prepadding);\n"
        "limit -= prepaddlength;\n"
        "int decoded_length = 0;\n"
        "int field_map[%lu];\n"
        "memset(field_map, 0, sizeof(field_map));\n",
        name, (unsigned long) sdef->nElements);
    if (mand_num > 0)
      src = mputstr(src, "size_t nof_mand_fields = 0;\n");
    for (i = 0; i < sdef->nElements; i++) {
      if (sdef->elements[i].isOptional)
        src = mputprintf(src, "field_%s = OMIT_VALUE;\n",
                         sdef->elements[i].name);
    }
      src = mputstr(src,
	"raw_order_t local_top_order;\n"
      "if (p_td.raw->top_bit_order == TOP_BIT_INHERITED) "
	"local_top_order = top_bit_ord;\n"
      "else if (p_td.raw->top_bit_order == TOP_BIT_RIGHT) "
	"local_top_order = ORDER_MSB;\n"
      "else local_top_order = ORDER_LSB;\n"
      "while (limit > 0) {\n"
      "size_t fl_start_pos = p_buf.get_pos_bit();\n"
      );
      for (i = 0; i < sdef->nElements; i++) { /* decoding fields with tag */
	if (raw_options[i].tag_type &&
	    sdef->raw.taglist.list[raw_options[i].tag_type - 1].nElements > 0) {
	  rawAST_coding_taglist* cur_choice =
	    sdef->raw.taglist.list + raw_options[i].tag_type - 1;
	  size_t j;
	  boolean has_fixed = FALSE, has_variable = FALSE, flag_needed = FALSE;
	  for (j = 0; j < cur_choice->nElements; j++) {
	    if (cur_choice->fields[j].start_pos >= 0) {
	      if (has_fixed || has_variable) flag_needed = TRUE;
	      has_fixed = TRUE;
	    } else {
	      if (has_fixed) flag_needed = TRUE;
	      has_variable = TRUE;
	    }
	    if (has_fixed && has_variable) break;
	  }
	  src = mputprintf(src, "if (field_map[%lu] == 0",
            (unsigned long) i);
    if (sdef->elements[i].isOptional) {
      src = mputprintf(src, " && (force_omit == NULL || !(*force_omit)(%d))", i);
    }
    src = mputstr(src, ") {\n");
	  if (flag_needed)
	    src = mputstr(src, "boolean already_failed = FALSE;\n");
	  if (has_fixed) {
	    /* first check the fields we can precode
	     * try to decode those key variables whose position we know
	     * this way we might be able to step over bad values faster
	     */
	    boolean first_fixed = TRUE;
	    src = mputstr(src, "raw_order_t temporal_top_order;\n"
	      "int temporal_decoded_length;\n");
	    for (j = 0; j < cur_choice->nElements; j++) {
	      size_t k;
	      rawAST_coding_field_list *cur_field_list = cur_choice->fields + j;
	      if (cur_field_list->start_pos < 0) continue;
	      if (!first_fixed) src = mputstr(src, "if (!already_failed) {\n");
	      for (k = cur_field_list->nElements - 1; k > 0; k--) {
		src = mputprintf(src, "if (%s_descr_.raw->top_bit_order == "
		  "TOP_BIT_RIGHT) temporal_top_order = ORDER_MSB;\n"
		"else if (%s_descr_.raw->top_bit_order == TOP_BIT_LEFT) "
		  "temporal_top_order = ORDER_LSB;\n"
		"else ", cur_field_list->fields[k - 1].typedescr,
		cur_field_list->fields[k - 1].typedescr);
	      }
	      src = mputprintf(src, "temporal_top_order = top_bit_ord;\n"
		"%s temporal_%lu;\n"
		"p_buf.set_pos_bit(fl_start_pos + %d);\n"
		"temporal_decoded_length = temporal_%lu.RAW_decode(%s_descr_, "
		  "p_buf, limit, temporal_top_order, TRUE);\n"
		"p_buf.set_pos_bit(fl_start_pos);\n"
		"if (temporal_decoded_length > 0 && temporal_%lu == %s) {\n"
    "RAW_Force_Omit field_%d_force_omit(%d, force_omit, "
    "%s_descr_.raw->forceomit);\n"
		"int decoded_field_length = field_%s%s.RAW_decode(%s_descr_, "
		  "p_buf, limit, local_top_order, TRUE, -1, TRUE, &field_%d_force_omit);\n"
		"if (decoded_field_length %s 0 && (",
		cur_field_list->fields[cur_field_list->nElements - 1].type,
		(unsigned long) j, cur_field_list->start_pos, (unsigned long) j,
		cur_field_list->fields[cur_field_list->nElements - 1].typedescr,
		(unsigned long) j, cur_field_list->value,
    i, i, sdef->elements[i].typedescrname,
                sdef->elements[i].name,
		sdef->elements[i].isOptional ? "()" : "",
		sdef->elements[i].typedescrname, i,
		sdef->elements[i].isOptional ? ">" : ">=");
	      src = genRawFieldChecker(src, cur_choice, TRUE);
	      src = mputstr(src, ")) {\n"
		"decoded_length += decoded_field_length;\n"
		"limit -= decoded_field_length;\n");
	      if (!sdef->elements[i].isOptional)
		src = mputstr(src, "nof_mand_fields++;\n");
	      src = mputprintf(src, "field_map[%lu] = 1;\n"
		"continue;\n"
		"} else {\n"
		"p_buf.set_pos_bit(fl_start_pos);\n", (unsigned long) i);
	      if (sdef->elements[i].isOptional)
		src = mputprintf(src, "field_%s = OMIT_VALUE;\n",
		  sdef->elements[i].name);
	      if (flag_needed) src = mputstr(src, "already_failed = TRUE;\n");
	      src = mputstr(src, "}\n"
		"}\n");
	      if (first_fixed) first_fixed = FALSE;
	      else src = mputstr(src, "}\n");
	    }
	  }
	  if (has_variable) {
	    /* if there is one tag key whose position we don't know
	     * and we couldn't decide yet if element can be decoded or not
	     * than we have to decode it.
	     */
	    if (flag_needed) src = mputstr(src, "if (!already_failed) {\n");
	    src = mputprintf(src, 
    "RAW_Force_Omit field_%d_force_omit(%d, force_omit, "
    "%s_descr_.raw->forceomit);\n"
    "int decoded_field_length = "
		"field_%s%s.RAW_decode(%s_descr_, p_buf, limit, "
		"local_top_order, TRUE, -1, TRUE, &field_%d_force_omit);\n"
	      "if (decoded_field_length %s 0 && (",
	      i, i, sdef->elements[i].typedescrname,
        sdef->elements[i].name, sdef->elements[i].isOptional ? "()" : "",
	      sdef->elements[i].typedescrname, i,
	      sdef->elements[i].isOptional ? ">" : ">=");
	    src = genRawFieldChecker(src, cur_choice, TRUE);
	    src = mputstr(src, ")) {\n"
	      "decoded_length += decoded_field_length;\n"
	      "limit -= decoded_field_length;\n");
	    if (!sdef->elements[i].isOptional)
	      src = mputstr(src, "nof_mand_fields++;\n");
	    src = mputprintf(src, "field_map[%lu] = 1;\n"
	      "continue;\n"
	      "} else {\n"
	      "p_buf.set_pos_bit(fl_start_pos);\n", (unsigned long) i);
	    if (sdef->elements[i].isOptional)
	      src = mputprintf(src, "field_%s = OMIT_VALUE;\n",
		sdef->elements[i].name);
	    src = mputstr(src, "}\n");
	    if (flag_needed) src = mputstr(src, "}\n");
	  }
	  src = mputstr(src, "}\n");
	}
      }
      for (i = 0; i < sdef->nElements; i++) {
	/* decoding fields without TAG */
	if (!raw_options[i].tag_type) {
	  boolean repeatable;
	  if (sdef->elements[i].of_type && sdef->elements[i].hasRaw &&
	      sdef->elements[i].raw.repeatable == XDEFYES) {
      repeatable = TRUE;
      if (sdef->elements[i].isOptional) {
        src = mputprintf(src, "if (force_omit == NULL || !(*force_omit)(%d)) ", i);
      }
    }
	  else {
	    repeatable = FALSE;
	    src = mputprintf(src, "if (field_map[%lu] == 0",
            (unsigned long) i);
      if (sdef->elements[i].isOptional) {
        src = mputprintf(src, " && (force_omit == NULL || !(*force_omit)(%d))", i);
      }
      src = mputstr(src, ") ");
	  }
	  src = mputprintf(src, "{\n"
      "RAW_Force_Omit field_%d_force_omit(%d, force_omit, "
      "%s_descr_.raw->forceomit);\n"
	    "int decoded_field_length = field_%s%s.RAW_decode(%s_descr_, "
	      "p_buf, limit, local_top_order, TRUE, %s, ",
      i, i, sdef->elements[i].typedescrname,
	    sdef->elements[i].name, sdef->elements[i].isOptional ? "()" : "",
	    sdef->elements[i].typedescrname, repeatable?"1":"-1");
	  if (repeatable)
            src = mputprintf(src, "field_map[%lu] == 0",
              (unsigned long) i);
    else {
      src = mputstr(src, "TRUE");
    }
	  src = mputprintf(src, ", &field_%d_force_omit);\n"
	    "if (decoded_field_length %s 0) {\n"
	    "decoded_length += decoded_field_length;\n"
	    "limit -= decoded_field_length;\n",
	    i, sdef->elements[i].isOptional ? ">" : ">=");
	  if (repeatable) {
	    if (!sdef->elements[i].isOptional) src = mputprintf(src,
	      "if (field_map[%lu] == 0) nof_mand_fields++;\n",
              (unsigned long) i);
	    src = mputprintf(src, "field_map[%lu]++;\n", (unsigned long) i);
	  } else {
	    if (!sdef->elements[i].isOptional)
	      src = mputstr(src, "nof_mand_fields++;\n");
	    src = mputprintf(src, "field_map[%lu] = 1;\n", (unsigned long) i);
	  }
	  src = mputstr(src, "continue;\n"
	    "} else {\n"
	    "p_buf.set_pos_bit(fl_start_pos);\n");
	  if (sdef->elements[i].isOptional) {
	    if (repeatable)
	      src = mputprintf(src, "if (field_map[%lu] == 0) ",
                (unsigned long) i);
	    src = mputprintf(src, "field_%s = OMIT_VALUE;\n",
	      sdef->elements[i].name);
	  }
	  src = mputstr(src, "}\n"
	    "}\n");
	}
      }
      for (i = 0; i < sdef->nElements; i++){
	/* decoding fields with tag OTHERWISE */
	if (raw_options[i].tag_type &&
	    sdef->raw.taglist.list[raw_options[i].tag_type-1].nElements == 0) {
	  src = mputprintf(src, "if (field_map[%lu] == 0", (unsigned long) i);
    if (sdef->elements[i].isOptional) {
      src = mputprintf(src, " && (force_omit == NULL || !(*force_omit)(%d))", i);
    }
    src = mputprintf(src, ") {\n"
      "RAW_Force_Omit field_%d_force_omit(%d, force_omit, "
      "%s_descr_.raw->forceomit);\n"
	    "int decoded_field_length = field_%s%s.RAW_decode(%s_descr_, "
	      "p_buf, limit, local_top_order, TRUE, -1, TRUE, &field_%d_force_omit);\n"
	    "if (decoded_field_length %s 0) {\n"
	    "decoded_length += decoded_field_length;\n"
	    "limit -= decoded_field_length;\n",
      i, i, sdef->elements[i].typedescrname,
	    sdef->elements[i].name, sdef->elements[i].isOptional ? "()" : "",
	    sdef->elements[i].typedescrname, i,
	    sdef->elements[i].isOptional ? ">" : ">=");
	  if (!sdef->elements[i].isOptional)
	    src = mputstr(src, "nof_mand_fields++;\n");
	  src = mputprintf(src, "field_map[%lu] = 1;\n"
	    "continue;\n"
	    "} else {\n"
	    "p_buf.set_pos_bit(fl_start_pos);\n", (unsigned long) i);
	  if (sdef->elements[i].isOptional)
	    src = mputprintf(src, "field_%s = OMIT_VALUE;\n",
	      sdef->elements[i].name);
	  src = mputstr(src, "}\n"
	    "}\n");
	}
      }
      src = mputstr(src, "break;\n"  /* no field decoded successfully, quit */
        "}\n");
      if (mand_num > 0) src = mputprintf(src,
        "if (nof_mand_fields != %lu) return limit ? -1 : -TTCN_EncDec::ET_INCOMPL_MSG;\n",
        (unsigned long) mand_num);
      /* If not all required fields were decoded and there are no bits left,
       * that means that the last field was decoded successfully but used up
       * the buffer. Signal "incomplete". If there were bits left, that means that
       * no field could be decoded from them; signal an error.
       */
      src = mputstr(src, "return decoded_length + prepaddlength + "
	"p_buf.increase_pos_padd(p_td.raw->padding);\n"
      "}\n\n");
  } else {
    /* set decoder end, record decoder start */
    int prev_ext_group = 0;
    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* force_omit)\n"
      "{ (void)no_err;\n"
	"  int prepaddlength=p_buf.increase_pos_padd(p_td.raw->prepadding);\n"
	"  limit-=prepaddlength;\n"
	"  size_t last_decoded_pos = p_buf.get_pos_bit();\n"
	"  int decoded_length = 0;\n"
	"  int decoded_field_length = 0;\n"
	"  raw_order_t local_top_order;\n"
	, name);
      if (hascrosstag) {
	src = mputstr(src, "  int selected_field = -1;\n");
      }
      if (sdef->raw.ext_bit_goup_num) {
        src=mputstr(src, "  int group_limit = 0;\n");
      }
      src=mputstr(src,
      "  if(p_td.raw->top_bit_order==TOP_BIT_INHERITED)"
      "local_top_order=top_bit_ord;\n"
      "  else if(p_td.raw->top_bit_order==TOP_BIT_RIGHT)local_top_order"
      "=ORDER_MSB;\n"
      "  else local_top_order=ORDER_LSB;\n"
      );
      if(has_ext_bit){
        src=mputstr(src,
          "  {\n"
          "  cbyte* data=p_buf.get_read_data();\n"
          "  int count=1;\n"
          "  unsigned mask = 1 << (local_top_order==ORDER_LSB ? 0 : 7);\n"
          "  if(p_td.raw->extension_bit==EXT_BIT_YES){\n"
          "    while((data[count-1] & mask)==0 && count*8<(int)limit) count++;\n"
          "  }\n"
          "  else{\n"
          "    while((data[count-1] & mask)!=0 && count*8<(int)limit) count++;\n"
          "  }\n"
          "  if(limit) limit=count*8;\n"
          "  }\n"
        );
      }
      if(haspointer)
       src=mputstr(src,
         "  int end_of_available_data=last_decoded_pos+limit;\n");
      for(i=0;i<sdef->nElements;i++){
        if(raw_options[i].pointerof)
          src=mputprintf(src,
          "  int start_of_field%lu=-1;\n"
          ,(unsigned long) i
          );
        if(raw_options[i].ptrbase)
          src=mputprintf(src,
          "  int start_pos_of_field%lu=-1;\n"
          ,(unsigned long) i
          );
        if(raw_options[i].lengthto)
          src=mputprintf(src,
          "  int value_of_length_field%lu = 0;\n"
          ,(unsigned long) i
          );
      }
      for(i=0;i<sdef->nElements;i++){ /* decoding fields */
	if (raw_options[i].delayed_decode) {
	  int j;
	  /* checking whether there are enough bits in the buffer to decode
	     the field */
	  src = mputstr(src, "  if (");
	  src = genRawFieldDecodeLimit(src, sdef, i, raw_options);
	  src = mputprintf(src, " < %d) return -TTCN_EncDec::ET_LEN_ERR;\n",
	    sdef->elements[i].raw.length);
	  /* skipping over the field that has to be decoded later because of
	     forward referencing in CROSSTAG */
	  src = mputprintf(src,
	    "  size_t start_of_field%lu = p_buf.get_pos_bit();\n"
	    "  p_buf.set_pos_bit(start_of_field%lu + %d);\n"
	    "  decoded_length += %d;\n"
	    "  last_decoded_pos += %d;\n"
	    "  limit -= %d;\n",
	    (unsigned long) i, (unsigned long) i, sdef->elements[i].raw.length,
            sdef->elements[i].raw.length,
	    sdef->elements[i].raw.length, sdef->elements[i].raw.length);
	  for (j = 0; j < raw_options[i].lengthof; j++) {
	    src = mputprintf(src,
	      "  value_of_length_field%d -= %d;\n",
	      raw_options[i].lengthoffield[j], sdef->elements[i].raw.length);
	  }
	} else {
	  src = genRawDecodeRecordField(src, sdef, i, raw_options, FALSE,
	   &prev_ext_group);
	  if (raw_options[i].nof_dependent_fields > 0) {
	    int j;
	    for (j = 0; j < raw_options[i].nof_dependent_fields; j++) {
	      int dependent_field_index = raw_options[i].dependent_fields[j];
	      /* seek to the beginning of the dependent field */
	      src = mputprintf(src,
		"  p_buf.set_pos_bit(start_of_field%d);\n",
		dependent_field_index);
	      /* decode the dependent field */
	      src = genRawDecodeRecordField(src, sdef, dependent_field_index,
		raw_options, TRUE, &prev_ext_group);
	    }
	    if (i < sdef->nElements - 1) {
	      /* seek back if there are more regular fields to decode */
	      src = mputstr(src,
		"  p_buf.set_pos_bit(last_decoded_pos);\n");
	    }
	  }
	}
      } /* decoding fields*/

      if(sdef->hasRaw && sdef->raw.presence.nElements > 0)
      {
	src = mputstr(src, "  if (");
	src = genRawFieldChecker(src, &sdef->raw.presence, FALSE);
	src = mputstr(src, ") return -1;\n");
      }

      src=mputstr(src,
      "  p_buf.set_pos_bit(last_decoded_pos);\n"
      "  return decoded_length+prepaddlength+"
      "p_buf.increase_pos_padd(p_td.raw->padding);\n}\n\n");
  } /* record decoder end */

  src = mputprintf(src, /* encoder */
    "int %s::RAW_encode(const TTCN_Typedescriptor_t&%s, "
    "RAW_enc_tree& myleaf) const {\n", name,
    use_runtime_2 ? " p_td" : "");
  if (use_runtime_2) {
    src = mputstr(src, "  if (err_descr) return RAW_encode_negtest(err_descr, p_td, myleaf);\n");
  }
  src = mputprintf(src,
    "  if (!is_bound()) TTCN_EncDec_ErrorContext::error"
    "(TTCN_EncDec::ET_UNBOUND, \"Encoding an unbound value.\");\n"
    "  int encoded_length = 0;\n"
    "  myleaf.isleaf = FALSE;\n"
    "  myleaf.body.node.num_of_nodes = %lu;\n"
    "  myleaf.body.node.nodes = init_nodes_of_enc_tree(%lu);\n",
    (unsigned long)sdef->nElements, (unsigned long)sdef->nElements);
  /* init nodes */
  for (i = 0; i < sdef->nElements; i++) {
    if (sdef->elements[i].isOptional) {
      src = mputprintf(src,
        "  if (field_%s.ispresent()) {\n", sdef->elements[i].name);
    }
    src = mputprintf(src,
      "  myleaf.body.node.nodes[%lu] = new RAW_enc_tree(TRUE, &myleaf, "
      "&(myleaf.curr_pos), %lu, %s_descr_.raw);\n",
      (unsigned long)i, (unsigned long)i, sdef->elements[i].typedescrname);
    if (sdef->elements[i].isOptional) {
      src = mputprintf(src,
        "  }\n"
        "  else myleaf.body.node.nodes[%lu] = NULL;\n", (unsigned long)i);
    }
  }
  for (i = 0; i < sdef->raw.ext_bit_goup_num; i++) {
    if (sdef->raw.ext_bit_groups[i].ext_bit != XDEFNO) {
      src = mputprintf(src,
        "  {\n"
        "   int node_idx = %d;\n"
        "   while (node_idx <= %d && myleaf.body.node.nodes[node_idx] == NULL) node_idx++;\n"
        "   if (myleaf.body.node.nodes[node_idx]) {\n"
        "     myleaf.body.node.nodes[node_idx]->ext_bit_handling = 1;\n"
        "     myleaf.body.node.nodes[node_idx]->ext_bit = %s;\n  }\n"
        "   node_idx = %d;\n"
        "   while (node_idx >= %d && myleaf.body.node.nodes[node_idx] == NULL) node_idx--;\n"
        "   if (myleaf.body.node.nodes[node_idx]) myleaf.body.node.nodes[node_idx]"
        "->ext_bit_handling += 2;\n"
        "  }\n",
        sdef->raw.ext_bit_groups[i].from,
        sdef->raw.ext_bit_groups[i].to,
        sdef->raw.ext_bit_groups[i].ext_bit == XDEFYES ? "EXT_BIT_YES" : "EXT_BIT_REVERSE",
        sdef->raw.ext_bit_groups[i].to,
        sdef->raw.ext_bit_groups[i].from);
    }
  }
  for (i = 0; i < sdef->nElements; i++) {
    /* encoding fields */
    if (sdef->elements[i].isOptional) {
      src = mputprintf(src,
        "  if (field_%s.ispresent()) {\n", sdef->elements[i].name);
    }
    if (raw_options[i].lengthto && sdef->elements[i].raw.lengthindex == NULL
        && sdef->elements[i].raw.union_member_num == 0) {
      /* encoding of lenghto fields */
      int a;
      src = mputprintf(src,
        "  encoded_length += %d;\n"
        "  myleaf.body.node.nodes[%lu]->calc = CALC_LENGTH;\n"
        "  myleaf.body.node.nodes[%lu]->coding_descr = &%s_descr_;\n"
        "  myleaf.body.node.nodes[%lu]->calcof.lengthto.num_of_fields = %d;\n"
        "  myleaf.body.node.nodes[%lu]->calcof.lengthto.unit = %d;\n"
        "  myleaf.body.node.nodes[%lu]->calcof.lengthto.offset = %d;\n"
        "  myleaf.body.node.nodes[%lu]->calcof.lengthto.fields = "
        "init_lengthto_fields_list(%d);\n"
        "  myleaf.body.node.nodes[%lu]->length = %d;\n",
        sdef->elements[i].raw.fieldlength, (unsigned long)i,
        (unsigned long)i, sdef->elements[i].typedescrname,
        (unsigned long)i, sdef->elements[i].raw.lengthto_num,
        (unsigned long)i, sdef->elements[i].raw.unit,
        (unsigned long)i, sdef->elements[i].raw.lengthto_offset,
        (unsigned long)i, sdef->elements[i].raw.lengthto_num,
        (unsigned long)i, sdef->elements[i].raw.fieldlength);
      for (a = 0; a < sdef->elements[i].raw.lengthto_num; a++) {
        if (sdef->elements[sdef->elements[i].raw.lengthto[a]].isOptional) {
          src = mputprintf(src,
            "  if (field_%s.ispresent()) {\n",
            sdef->elements[sdef->elements[i].raw.lengthto[a]].name);
        }
        src = mputprintf(src,
          "  myleaf.body.node.nodes[%lu]->calcof.lengthto.fields[%d].level = "
          "myleaf.body.node.nodes[%d]->curr_pos.level;\n"
          "  myleaf.body.node.nodes[%lu]->calcof.lengthto.fields[%d].pos = "
          "myleaf.body.node.nodes[%d]->curr_pos.pos;\n",
          (unsigned long)i, a, sdef->elements[i].raw.lengthto[a],
          (unsigned long)i, a, sdef->elements[i].raw.lengthto[a]);
        if (sdef->elements[sdef->elements[i].raw.lengthto[a]].isOptional) {
          src = mputprintf(src,
            "  } else {\n"
            "  myleaf.body.node.nodes[%lu]->calcof.lengthto.fields[%d].level = 0;\n"
            "  myleaf.body.node.nodes[%lu]->calcof.lengthto.fields[%d].pos = 0;\n"
            "  }\n", (unsigned long)i, a, (unsigned long)i, a);
        }
      }
    } else if (raw_options[i].pointerto) {
      /* encoding of pointerto fields */
      if (sdef->elements[sdef->elements[i].raw.pointerto].isOptional) {
        src = mputprintf(src,
          "  if (field_%s.ispresent()) {\n",
          sdef->elements[sdef->elements[i].raw.pointerto].name);
      }
      src = mputprintf(src,
        "  encoded_length += %d;\n"
        "  myleaf.body.node.nodes[%lu]->calc = CALC_POINTER;\n"
        "  myleaf.body.node.nodes[%lu]->coding_descr = &%s_descr_;\n"
        "  myleaf.body.node.nodes[%lu]->calcof.pointerto.unit = %d;\n"
        "  myleaf.body.node.nodes[%lu]->calcof.pointerto.ptr_offset = %d;\n"
        "  myleaf.body.node.nodes[%lu]->calcof.pointerto.ptr_base = %d;\n"
        "  myleaf.body.node.nodes[%lu]->calcof.pointerto.target.level = "
        "myleaf.body.node.nodes[%d]->curr_pos.level;\n"
        "  myleaf.body.node.nodes[%lu]->calcof.pointerto.target.pos = "
        "myleaf.body.node.nodes[%d]->curr_pos.pos;\n"
        "  myleaf.body.node.nodes[%lu]->length = %d;\n",
        sdef->elements[i].raw.fieldlength,(unsigned long)i,
        (unsigned long)i, sdef->elements[i].typedescrname,
        (unsigned long)i, sdef->elements[i].raw.unit,
        (unsigned long)i, sdef->elements[i].raw.ptroffset,
        (unsigned long)i, sdef->elements[i].raw.pointerbase,
        (unsigned long)i, sdef->elements[i].raw.pointerto,
        (unsigned long)i, sdef->elements[i].raw.pointerto,
        (unsigned long)i, sdef->elements[i].raw.fieldlength);
      if (sdef->elements[sdef->elements[i].raw.pointerto].isOptional) {
        src = mputprintf(src,
          "  } else {\n"
          "    INTEGER atm;\n"
          "    atm = 0;\n"
          "    encoded_length += atm.RAW_encode(%s_descr_"
          ", *myleaf.body.node.nodes[%lu]);\n"
          "  }\n",
          sdef->elements[i].typedescrname, (unsigned long)i);
      }
    } else {
      /* encoding of normal fields */
      src = mputprintf(src,
        "  encoded_length += field_%s%s.RAW_encode(%s_descr_"
        ", *myleaf.body.node.nodes[%lu]);\n",
        sdef->elements[i].name, sdef->elements[i].isOptional ? "()" : "",
        sdef->elements[i].typedescrname, (unsigned long)i);
    }
    if (sdef->elements[i].isOptional) {
      src = mputstr(src, "  }\n");
    }
  }
  for (i = 0; i < sdef->nElements; i++) {
    /* fill presence, tag and crosstag */
    if (raw_options[i].lengthto && sdef->elements[i].raw.lengthindex) {
      /* encoding of lenghto fields */
      int a;
      if (sdef->elements[i].isOptional) {
        src = mputprintf(src,
          "  if (field_%s.ispresent()) {\n",
          sdef->elements[i].name);
      }
      src = mputprintf(src,
        "  if (myleaf.body.node.nodes[%lu]->body.node.nodes[%d]) {\n"
        "  int tmp_length = myleaf.body.node.nodes[%lu]->body.node.nodes[%d]->length;\n"
        "  delete myleaf.body.node.nodes[%lu]->body.node.nodes[%d];\n"
        "  myleaf.body.node.nodes[%lu]->body.node.nodes[%d] = "
        "new RAW_enc_tree(TRUE, myleaf.body.node.nodes[%lu], "
        "&(myleaf.body.node.nodes[%lu]->curr_pos), %d, %s_descr_.raw);\n"
        "  myleaf.body.node.nodes[%lu]->body.node.nodes[%d]->"
        "length = tmp_length;\n"
        "  myleaf.body.node.nodes[%lu]->body.node.nodes[%d]->"
        "calc = CALC_LENGTH;\n"
        "  myleaf.body.node.nodes[%lu]->body.node.nodes[%d]->"
        "coding_descr = &%s_descr_;\n"
        "  myleaf.body.node.nodes[%lu]->body.node.nodes[%d]->"
        "calcof.lengthto.num_of_fields = %d;\n"
        "  myleaf.body.node.nodes[%lu]->body.node.nodes[%d]->"
        "calcof.lengthto.unit = %d;\n"
         "  myleaf.body.node.nodes[%lu]->body.node.nodes[%d]->"
        "calcof.lengthto.offset = %d;\n"
        "  myleaf.body.node.nodes[%lu]->body.node.nodes[%d]->"
        "calcof.lengthto.fields = "
        "init_lengthto_fields_list(%d);\n",
        (unsigned long)i, sdef->elements[i].raw.lengthindex->nthfield,
        (unsigned long)i, sdef->elements[i].raw.lengthindex->nthfield,
        (unsigned long)i, sdef->elements[i].raw.lengthindex->nthfield,
        (unsigned long)i, sdef->elements[i].raw.lengthindex->nthfield,
        (unsigned long)i, (unsigned long)i,
        sdef->elements[i].raw.lengthindex->nthfield, sdef->elements[i].raw.lengthindex->typedescr,
        (unsigned long)i, sdef->elements[i].raw.lengthindex->nthfield,
        (unsigned long)i, sdef->elements[i].raw.lengthindex->nthfield,
        (unsigned long)i, sdef->elements[i].raw.lengthindex->nthfield,
        sdef->elements[i].raw.lengthindex->typedescr,
        (unsigned long)i, sdef->elements[i].raw.lengthindex->nthfield,
        sdef->elements[i].raw.lengthto_num,
        (unsigned long)i, sdef->elements[i].raw.lengthindex->nthfield,
        sdef->elements[i].raw.unit,
        (unsigned long)i, sdef->elements[i].raw.lengthindex->nthfield,
        sdef->elements[i].raw.lengthto_offset,
        (unsigned long)i, sdef->elements[i].raw.lengthindex->nthfield,
        sdef->elements[i].raw.lengthto_num);
      for (a = 0; a < sdef->elements[i].raw.lengthto_num; a++) {
        if (sdef->elements[sdef->elements[i].raw.lengthto[a]].isOptional) {
          src = mputprintf(src,
            "  if (field_%s.ispresent()) {\n",
            sdef->elements[sdef->elements[i].raw.lengthto[a]].name);
        }
        src = mputprintf(src,
          "  myleaf.body.node.nodes[%lu]->body.node.nodes[%d]"
          "->calcof.lengthto.fields[%d].level = "
          "myleaf.body.node.nodes[%d]->curr_pos.level;\n"
          "  myleaf.body.node.nodes[%lu]->body.node.nodes[%d]"
          "->calcof.lengthto.fields[%d].pos = "
          "myleaf.body.node.nodes[%d]->curr_pos.pos;\n",
          (unsigned long)i,sdef->elements[i].raw.lengthindex->nthfield,
          a, sdef->elements[i].raw.lengthto[a],
          (unsigned long)i,sdef->elements[i].raw.lengthindex->nthfield,
          a, sdef->elements[i].raw.lengthto[a]);
        if (sdef->elements[sdef->elements[i].raw.lengthto[a]].isOptional) {
           src = mputprintf(src,
           "  } else {\n"
           "  myleaf.body.node.nodes[%lu]->body.node.nodes[%d]->"
           "calcof.lengthto.fields[%d].level = 0;\n"
           "  myleaf.body.node.nodes[%lu]->body.node.nodes[%d]->"
           "calcof.lengthto.fields[%d].pos = 0;\n"
           "  }\n",
           (unsigned long)i, sdef->elements[i].raw.lengthindex->nthfield, a,
           (unsigned long)i, sdef->elements[i].raw.lengthindex->nthfield, a);
        }
      }
      src = mputstr(src, "  }\n");
      if (sdef->elements[i].isOptional) {
        src = mputstr(src, "  }\n");
      }
    }
    if (raw_options[i].lengthto && sdef->elements[i].raw.union_member_num) {
      /* encoding of lenghto fields */
      int a;
      if (sdef->elements[i].isOptional) {
        src = mputprintf(src,
          "  if (field_%s.ispresent()) ", sdef->elements[i].name);
      }

      src = mputprintf(src,
        "  {\n"
        "  int sel_field = 0;\n"
        "  while (myleaf.body.node.nodes[%lu]->body.node.nodes[sel_field] == NULL) "
        "{ sel_field++; }\n"
        "  const TTCN_Typedescriptor_t* tmpTypeDesc = myleaf.body.node.nodes[%lu]->"
        "body.node.nodes[sel_field]->coding_descr;\n"
        "  int tmp_length = myleaf.body.node.nodes[%lu]->body.node.nodes[sel_field]->length;\n"
        "  delete myleaf.body.node.nodes[%lu]->body.node.nodes[sel_field];\n"
        "  myleaf.body.node.nodes[%lu]->body.node.nodes[sel_field] = "
        "new RAW_enc_tree(TRUE, myleaf.body.node.nodes[%lu], "
        "&(myleaf.body.node.nodes[%lu]->curr_pos), sel_field, tmpTypeDesc->raw);\n"
        "  myleaf.body.node.nodes[%lu]->body.node.nodes[sel_field]->"
        "length = tmp_length;\n"
        "  myleaf.body.node.nodes[%lu]->body.node.nodes[sel_field]->"
        "calc = CALC_LENGTH;\n"
        "  myleaf.body.node.nodes[%lu]->body.node.nodes[sel_field]->"
        "coding_descr = tmpTypeDesc;\n"
        "  myleaf.body.node.nodes[%lu]->body.node.nodes[sel_field]->"
        "calcof.lengthto.num_of_fields = %d;\n"
        "  myleaf.body.node.nodes[%lu]->body.node.nodes[sel_field]->"
        "calcof.lengthto.unit = %d;\n"
        "  myleaf.body.node.nodes[%lu]->body.node.nodes[sel_field]->"
        "calcof.lengthto.offset = %d;\n"
        "  myleaf.body.node.nodes[%lu]->body.node.nodes[sel_field]->"
        "calcof.lengthto.fields = init_lengthto_fields_list(%d);\n",
        (unsigned long)i,(unsigned long)i,(unsigned long)i,(unsigned long)i,
        (unsigned long)i,(unsigned long)i,(unsigned long)i,(unsigned long)i,
        (unsigned long)i,(unsigned long)i,
        (unsigned long)i,sdef->elements[i].raw.lengthto_num,
        (unsigned long)i,sdef->elements[i].raw.unit,
        (unsigned long)i,sdef->elements[i].raw.lengthto_offset,
        (unsigned long)i,sdef->elements[i].raw.lengthto_num);
      for (a = 0; a < sdef->elements[i].raw.lengthto_num; a++) {
        if (sdef->elements[sdef->elements[i].raw.lengthto[a]].isOptional) {
          src = mputprintf(src,
            "  if (field_%s.ispresent()) {\n",
            sdef->elements[sdef->elements[i].raw.lengthto[a]].name);
        }
        src = mputprintf(src,
          "  myleaf.body.node.nodes[%lu]->body.node.nodes[sel_field]"
          "->calcof.lengthto.fields[%d].level = "
          "myleaf.body.node.nodes[%d]->curr_pos.level;\n"
          "  myleaf.body.node.nodes[%lu]->body.node.nodes[sel_field]"
          "->calcof.lengthto.fields[%d].pos = "
          "myleaf.body.node.nodes[%d]->curr_pos.pos;\n",
          (unsigned long)i, a, sdef->elements[i].raw.lengthto[a],
          (unsigned long)i, a, sdef->elements[i].raw.lengthto[a]);
        if (sdef->elements[sdef->elements[i].raw.lengthto[a]].isOptional) {
          src = mputprintf(src,
            "  }else{\n"
            "  myleaf.body.node.nodes[%lu]->body.node.nodes[sel_field]"
            "->calcof.lengthto.fields[%d].level = 0;\n"
            "  myleaf.body.node.nodes[%lu]->body.node.nodes[sel_field]"
            "->calcof.lengthto.fields[%d].pos = 0;\n"
            "  }\n",
            (unsigned long)i, a,
            (unsigned long)i, a);
        }
      }
      src = mputstr(src,
        "  }\n");
    }
    if (raw_options[i].tag_type && sdef->raw.taglist.list[raw_options[i].tag_type - 1].nElements) {
      /* tag */
      rawAST_coding_taglist *cur_choice =
        sdef->raw.taglist.list + raw_options[i].tag_type - 1;
      src = mputstr(src, "  if (");
      if (sdef->elements[i].isOptional) {
        src = mputprintf(src, "field_%s.ispresent() && (", sdef->elements[i].name);
      }
      src = genRawFieldChecker(src, cur_choice, FALSE);
      if (sdef->elements[i].isOptional) src = mputstr(src, ")");
      src = mputstr(src,") {\n");
      src = genRawTagChecker(src, cur_choice);
      src=mputstr(src,"  }\n");
    }
    if (sdef->elements[i].hasRaw && sdef->elements[i].raw.presence.nElements) {
      /* presence */
      src = mputstr(src, "  if (");
      if (sdef->elements[i].isOptional) {
        src = mputprintf(src, "field_%s.ispresent() && (", sdef->elements[i].name);
      }
      src = genRawFieldChecker(src, &sdef->elements[i].raw.presence, FALSE);
      if (sdef->elements[i].isOptional) src = mputstr(src, ")");
      src = mputstr(src, ") {\n");
      src = genRawTagChecker(src, &sdef->elements[i].raw.presence);
      src = mputstr(src,"  }\n");
    }
    if (sdef->elements[i].hasRaw &&
        sdef->elements[i].raw.crosstaglist.nElements) {
      /* crosstag */
      int a;
      if (sdef->elements[i].isOptional) {
        src = mputprintf(src,
          "  if (field_%s.ispresent()) {\n",
          sdef->elements[i].name);
      }
      src = mputprintf(src,
        "  switch (field_%s%s.get_selection()) {\n",
        sdef->elements[i].name,sdef->elements[i].isOptional ? "()" : "");
      for (a = 0; a < sdef->elements[i].raw.crosstaglist.nElements; a++) {
        rawAST_coding_taglist *cur_choice =
	  sdef->elements[i].raw.crosstaglist.list + a;
        if (cur_choice->nElements > 0) {
	    src = mputprintf(src, "  case %s%s%s:\n"
	      "  if (", sdef->elements[i].type,
	      "::ALT_", cur_choice->fieldName);
	    src = genRawFieldChecker(src, cur_choice, FALSE);
	    src = mputstr(src, ") {\n");
	    if (!strcmp(cur_choice->fields[0].value, "OMIT_VALUE")) {
	      if (cur_choice->fields[0].nElements != 1)
		NOTSUPP("omit value with multiple fields in CROSSTAG");
	      /* eliminating the corresponding encoded leaf, which will have
	         the same effect as if the referred field had been omit */
	      src = mputprintf(src,
		"  encoded_length -= myleaf.body.node.nodes[%d]->length;\n"
		"  delete myleaf.body.node.nodes[%d];\n"
		"  myleaf.body.node.nodes[%d] = NULL;\n",
		cur_choice->fields[0].fields[0].nthfield,
		cur_choice->fields[0].fields[0].nthfield,
		cur_choice->fields[0].fields[0].nthfield);
	    } else {
	      int ll;
	      src = mputprintf(src,
		"  RAW_enc_tr_pos pr_pos;\n"
		"  pr_pos.level = myleaf.curr_pos.level + %d;\n"
		"  int new_pos[] = { ",
		cur_choice->fields[0].nElements);
              for (ll = 0; ll < cur_choice->fields[0].nElements; ll++) {
		if (ll > 0) src = mputstr(src, ", ");
		src = mputprintf(src, "%d",
		  cur_choice->fields[0].fields[ll].nthfield);
              }
	      src = mputprintf(src, " };\n"
		"  pr_pos.pos = init_new_tree_pos(myleaf.curr_pos, %d, "
		  "new_pos);\n",
		cur_choice->fields[0].nElements);
              if (cur_choice->fields[0].value[0] == ' ') {
	        /* the value is a string literal (the encoder can be called
		   on that object directly */
		src = mputprintf(src,
		  "  RAW_enc_tree* temp_leaf = myleaf.get_node(pr_pos);\n"
                  "  if (temp_leaf != NULL)\n"
                  "    %s.RAW_encode(%s_descr_,*temp_leaf);\n"
                  "  else\n"
                  "    TTCN_EncDec_ErrorContext::error\n"
                  "      (TTCN_EncDec::ET_OMITTED_TAG, \"Encoding a tagged,"
                  " but omitted value.\");\n",
		  cur_choice->fields[0].value,
		  cur_choice->fields[0].fields[
		    cur_choice->fields[0].nElements - 1].typedescr);
	      } else {
	        /* a temporary object needs to be created for encoding */
		src = mputprintf(src,
		  "  %s new_val(%s);\n"
                  "  RAW_enc_tree* temp_leaf = myleaf.get_node(pr_pos);\n"
                  "  if (temp_leaf != NULL)\n"
                  "    new_val.RAW_encode(%s_descr_,*temp_leaf);\n"
                  "  else\n"
                  "    TTCN_EncDec_ErrorContext::error\n"
                  "      (TTCN_EncDec::ET_OMITTED_TAG, \"Encoding a tagged,"
                  " but omitted value.\");\n",
		  cur_choice->fields[0].fields[
		    cur_choice->fields[0].nElements - 1].type,
		  cur_choice->fields[0].value,
		  cur_choice->fields[0].fields[
		    cur_choice->fields[0].nElements - 1].typedescr);
              }
              src = mputstr(src, "  free_tree_pos(pr_pos.pos);\n");
	    }
	    src = mputstr(src, "  }\n"
	      "  break;\n");
          }
        }
        src = mputstr(src, "  default:;\n"
          "  }\n");
        if (sdef->elements[i].isOptional) src = mputstr(src, "  }\n");
      }
    }
  /* presence */
  if (sdef->hasRaw && sdef->raw.presence.nElements > 0) {
    src = mputstr(src, "  if (");
    src = genRawFieldChecker(src, &sdef->raw.presence, FALSE);
    src = mputstr(src,") {\n");
    src = genRawTagChecker(src, &sdef->raw.presence);
    src = mputstr(src,"  }\n");
  }
  src = mputstr(src, "  return myleaf.length = encoded_length;\n}\n\n");

  if (use_runtime_2)
    src = generate_raw_coding_negtest(src, sdef, raw_options);
  return src;
}

char *generate_raw_coding_negtest(char *src, const struct_def *sdef,
  struct raw_option_struct *raw_options)
{
  int i;
  const char *name = sdef->name;
  src = mputprintf(src,
    /* Count the nodes and discover the new node order only.  No node creation
       or encoding.  */
    "int %s::RAW_encode_negtest(const Erroneous_descriptor_t *p_err_descr, "
    "const TTCN_Typedescriptor_t& /*p_td*/, RAW_enc_tree& myleaf) const\n"
    "{\n"
    "  if (!is_bound()) TTCN_EncDec_ErrorContext::error"
    "(TTCN_EncDec::ET_UNBOUND, \"Encoding an unbound value.\");\n"
    "  int idx_map[%lu];\n"
    "  int idx_map_before[%lu];\n"
    "  int idx_map_after[%lu];\n"
    "  bool altered_[%lu];\n"
    "  for (int idx_map_idx = 0; idx_map_idx < %lu; ++idx_map_idx) {\n"
    "    idx_map[idx_map_idx] = -1;\n"
    "    idx_map_before[idx_map_idx] = -1;\n"
    "    idx_map_after[idx_map_idx] = -1;\n"
    "    altered_[idx_map_idx] = false;\n"
    "  };\n"
    "  (void)idx_map;\n"
    "  (void)idx_map_before;\n"
    "  (void)idx_map_after;\n"
    "  (void)altered_;\n"
    "  int encoded_length = 0;\n"
    "  int num_fields = get_count();\n"
    "  myleaf.isleaf = FALSE;\n"
    "  myleaf.body.node.num_of_nodes = 0;\n"
    "  for (int field_idx = 0; field_idx < num_fields; ++field_idx) {\n"
    "    if ((p_err_descr->omit_before != -1) &&\n"
    "        (field_idx < p_err_descr->omit_before)) {\n"
    "      altered_[field_idx] = true;\n"
    "      continue;\n"
    "    }\n"
    "    const Erroneous_values_t *err_vals = p_err_descr->get_field_err_values(field_idx);\n"
    "    const Erroneous_descriptor_t *emb_descr = p_err_descr->get_field_emb_descr(field_idx);\n"
    "    if (err_vals && err_vals->before) {\n"
    "      idx_map_before[field_idx] = myleaf.body.node.num_of_nodes;\n"
    "      ++myleaf.body.node.num_of_nodes;\n"
    "    }\n"
    "    if (err_vals && err_vals->value) {\n"
    "      if (err_vals->value->errval) {\n"
    "        idx_map[field_idx] = myleaf.body.node.num_of_nodes;\n"
    "        ++myleaf.body.node.num_of_nodes;\n"
    "      }\n"
    "      altered_[field_idx] = true;\n"
    "    } else {\n"
    "      if (emb_descr) altered_[field_idx] = true;\n"
    "      idx_map[field_idx] = myleaf.body.node.num_of_nodes;\n"
    "      ++myleaf.body.node.num_of_nodes;\n"
    "    }\n"
    "    if (err_vals && err_vals->after) {\n"
    "      idx_map_after[field_idx] = myleaf.body.node.num_of_nodes;\n"
    "      ++myleaf.body.node.num_of_nodes;\n"
    "    }\n"
    "    if ((p_err_descr->omit_after != -1) &&\n"
    "        (field_idx >= p_err_descr->omit_after)) {\n"
    "      ++field_idx;\n"
    "      while (field_idx < num_fields) {\n"
    "        altered_[field_idx] = true;\n"
    "        ++field_idx;\n"
    "      }\n"
    "      break;\n"
    "    }\n"
    "  }\n",
    name, (unsigned long)sdef->nElements, (unsigned long)sdef->nElements,
    (unsigned long)sdef->nElements, (unsigned long)sdef->nElements,
    (unsigned long)sdef->nElements);
  /* Init nodes.  */
  src = mputprintf(src,
    "  myleaf.body.node.nodes =\n"
    "    init_nodes_of_enc_tree(myleaf.body.node.num_of_nodes);\n"
    "  TTCN_EncDec_ErrorContext e_c;\n"
    "  int node_pos = 0;\n"
    "  int next_optional_idx = 0;\n"
    "  const int *my_optional_indexes = get_optional_indexes();\n"
    "  for (int field_idx = 0; field_idx < num_fields; ++field_idx) {\n"
    "    boolean is_optional_field = my_optional_indexes &&\n"
    "      (my_optional_indexes[next_optional_idx] == field_idx);\n"
    "    if ((p_err_descr->omit_before != -1) &&\n"
    "        (field_idx < p_err_descr->omit_before)) {\n"
    "      if (is_optional_field) ++next_optional_idx;\n"
    "      continue;\n"
    "    }\n"
    "    const Erroneous_values_t *err_vals =\n"
    "      p_err_descr->get_field_err_values(field_idx);\n"
    "    if (err_vals && err_vals->before) {\n"
    "      if (err_vals->before->errval == NULL)\n"
    "        TTCN_error(\"internal error: erroneous before value missing\");\n"
    "      if (err_vals->before->raw) {\n"
    "        myleaf.body.node.nodes[node_pos] =\n"
    "          new RAW_enc_tree(TRUE, &myleaf, &(myleaf.curr_pos),\n"
    "                           node_pos, err_vals->before->errval"
    "->get_descriptor()->raw);\n"
    "      } else {\n"
    "        if (err_vals->before->type_descr == NULL)\n"
    "          TTCN_error(\"internal error: erroneous before typedescriptor "
    "missing\");\n"
    "        myleaf.body.node.nodes[node_pos] =\n"
    "          new RAW_enc_tree(TRUE, &myleaf, &(myleaf.curr_pos),\n"
    "            node_pos, err_vals->before->type_descr->raw);\n"
    "      }\n"
    "      ++node_pos;\n"
    "    }\n"
    "    if (err_vals && err_vals->value) {\n"
    "      if (err_vals->value->errval) {\n"
    "        e_c.set_msg(\"'%%s'(erroneous value): \", fld_name(field_idx));\n"
    "        if (err_vals->value->raw) {\n"
    "          myleaf.body.node.nodes[node_pos] =\n"
    "            new RAW_enc_tree(TRUE, &myleaf, &(myleaf.curr_pos),\n"
    "                             node_pos, err_vals->value->errval"
    "->get_descriptor()->raw);\n"
    "        } else {\n"
    "          if (err_vals->value->type_descr == NULL)\n"
    "            TTCN_error(\"internal error: erroneous value typedescriptor "
    "missing\");\n"
    "          myleaf.body.node.nodes[node_pos] =\n"
    "            new RAW_enc_tree(TRUE, &myleaf, &(myleaf.curr_pos),\n"
    "              node_pos, err_vals->value->type_descr->raw);\n"
    "        }\n"
    "        ++node_pos;\n"
    "      }\n"
    "    } else {\n"
    "      e_c.set_msg(\"'%%s': \", fld_name(field_idx));\n"
    "      if (!is_optional_field || get_at(field_idx)->ispresent()) {\n"
    "        myleaf.body.node.nodes[node_pos] =\n"
    "          new RAW_enc_tree(TRUE, &myleaf, &(myleaf.curr_pos),\n"
    "                           node_pos, fld_descr(field_idx)->raw);\n"
    "      } else {\n"
    "        // `omitted' field.\n"
    "        myleaf.body.node.nodes[node_pos] = NULL;\n"
    "      }\n"
    "      ++node_pos;\n"
    "    }\n"
    "    if (err_vals && err_vals->after) {\n"
    "      if (err_vals->after->errval == NULL)\n"
    "        TTCN_error(\"internal error: erroneous before value missing\");\n"
    "      if (err_vals->after->raw) {\n"
    "        myleaf.body.node.nodes[node_pos] =\n"
    "          new RAW_enc_tree(TRUE, &myleaf, &(myleaf.curr_pos),\n"
    "                           node_pos, err_vals->after->errval"
    "->get_descriptor()->raw);\n"
    "      } else {\n"
    "        if (err_vals->after->type_descr == NULL)\n"
    "          TTCN_error(\"internal error: erroneous after typedescriptor "
    "missing\");\n"
    "        myleaf.body.node.nodes[node_pos] =\n"
    "          new RAW_enc_tree(TRUE, &myleaf, &(myleaf.curr_pos),\n"
    "            node_pos, err_vals->after->type_descr->raw);\n"
    "      }\n"
    "      ++node_pos;\n"
    "    }\n"
    "    if (is_optional_field) ++next_optional_idx;\n"
    "    if ((p_err_descr->omit_after != -1) &&\n"
    "        (field_idx >= p_err_descr->omit_after))\n"
    "      break;\n"
    "  }\n");
  /* Handling of the tricky attributes.  A simple integer array will be used
     to track changes (field positions) in the record.
     int idx_map[n_fields] = {
       -1 (= field 0. was omitted),
        1 (= field 1. can be found at index 1.),
       -2 (= field 2. is still at index 2., but its value has changed) };  */
  for (i = 0; i < sdef->raw.ext_bit_goup_num; i++) {
    if (sdef->raw.ext_bit_groups[i].ext_bit != XDEFNO) {
      src = mputprintf(src,
        "  {\n"
        "    boolean in_between_modified = FALSE;\n"
        "    for (int idx_map_idx = %d; idx_map_idx <= %d; ++idx_map_idx)\n"
        "      if (idx_map[idx_map_idx] < 0) {\n"
        "        in_between_modified = TRUE;\n"
        "        break;\n"
        "      }\n"
        "    if (altered_[%d] || altered_[%d] ||\n"
        "        %d != idx_map[%d] - idx_map[%d] || in_between_modified) {\n"
        "      e_c.set_msg(\"Field #%d and/or #%d: \");\n"
        "      TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_NEGTEST_CONFL,\n"
        "        \"Conflicting negative testing attributes, extension bit "
        "group #%d will be ignored\");\n"
        "    } else {\n"
        "      int node_idx = idx_map[%d];\n"
        "      while (node_idx <= idx_map[%d] &&\n"
        "             myleaf.body.node.nodes[node_idx] == NULL)\n"
        "        node_idx++;\n"
        "      if (myleaf.body.node.nodes[node_idx]) {\n"
        "        myleaf.body.node.nodes[node_idx]->ext_bit_handling = 1;\n"
        "        myleaf.body.node.nodes[node_idx]->ext_bit = %s;\n"
        "      }\n"
        "      node_idx = idx_map[%d];\n"
        "      while (node_idx >= idx_map[%d] &&\n"
        "             myleaf.body.node.nodes[node_idx] == NULL)\n"
        "        node_idx--;\n"
        "      if (myleaf.body.node.nodes[node_idx])\n"
        "        myleaf.body.node.nodes[node_idx]->ext_bit_handling += 2;\n"
        "    }\n"
        "  }\n",
        sdef->raw.ext_bit_groups[i].from,
        sdef->raw.ext_bit_groups[i].to,
        sdef->raw.ext_bit_groups[i].from,
        sdef->raw.ext_bit_groups[i].to,
        sdef->raw.ext_bit_groups[i].to - sdef->raw.ext_bit_groups[i].from,
        sdef->raw.ext_bit_groups[i].to,
        sdef->raw.ext_bit_groups[i].from,
        sdef->raw.ext_bit_groups[i].from,
        sdef->raw.ext_bit_groups[i].to, i,
        sdef->raw.ext_bit_groups[i].from,
        sdef->raw.ext_bit_groups[i].to,
        sdef->raw.ext_bit_groups[i].ext_bit == XDEFYES ? "EXT_BIT_YES" : "EXT_BIT_REVERSE",
        sdef->raw.ext_bit_groups[i].to,
        sdef->raw.ext_bit_groups[i].from);
    }
  }
  for (i = 0; i < sdef->nElements; i++) {
    if (sdef->elements[i].isOptional) {
      src = mputprintf(src,
        "  if (field_%s.ispresent()) {\n",
        sdef->elements[i].name);
    }
    if (raw_options[i].lengthto && sdef->elements[i].raw.lengthindex == NULL
        && sdef->elements[i].raw.union_member_num == 0) {
      /* Encoding of lenghto fields.  */
      int a;
      src = mputprintf(src,
        "  if (idx_map[%lu] >= 0 && !altered_[%lu]) {\n"
        "    size_t nof_fields = %d",
        (unsigned long)i, (unsigned long)i, sdef->elements[i].raw.lengthto_num);
      for (a = 0; a < sdef->elements[i].raw.lengthto_num; a++) {
        src = mputprintf(src,
          " + (idx_map_before[%lu] >= 0) + (idx_map_after[%lu] >= 0)",
          (unsigned long)sdef->elements[i].raw.lengthto[a],
          (unsigned long)sdef->elements[i].raw.lengthto[a]);
      }
      src = mputprintf(src,
        ";\n"
        "    encoded_length += %d;\n"
        "    myleaf.body.node.nodes[idx_map[%lu]]->calc = CALC_LENGTH;\n"
        "    myleaf.body.node.nodes[idx_map[%lu]]->coding_descr = &%s_descr_;\n"
        "    myleaf.body.node.nodes[idx_map[%lu]]->calcof.lengthto.num_of_fields = nof_fields;\n"
        "    myleaf.body.node.nodes[idx_map[%lu]]->calcof.lengthto.unit = %d;\n"
        "    myleaf.body.node.nodes[idx_map[%lu]]->calcof.lengthto.offset = %d;\n"
        "    myleaf.body.node.nodes[idx_map[%lu]]->calcof.lengthto.fields = "
        "init_lengthto_fields_list(nof_fields);\n"
        "    myleaf.body.node.nodes[idx_map[%lu]]->length = %d;\n"
        "    nof_fields = 0;\n",
        sdef->elements[i].raw.fieldlength, (unsigned long)i,
        (unsigned long)i, sdef->elements[i].typedescrname,
        (unsigned long)i, (unsigned long)i, sdef->elements[i].raw.unit,
        (unsigned long)i, sdef->elements[i].raw.lengthto_offset,
        (unsigned long)i, (unsigned long)i, sdef->elements[i].raw.fieldlength);
      for (a = 0; a < sdef->elements[i].raw.lengthto_num; a++) {
        src = mputprintf(src,
          "    if (idx_map[%lu] >= 0",
          (unsigned long)sdef->elements[i].raw.lengthto[a]);
        if (sdef->elements[sdef->elements[i].raw.lengthto[a]].isOptional) {
          src = mputprintf(src,
            " && field_%s.ispresent()",
            sdef->elements[sdef->elements[i].raw.lengthto[a]].name);
        }
        src = mputprintf(src,
          ") {\n"
          "      myleaf.body.node.nodes[idx_map[%lu]]->calcof.lengthto.fields[nof_fields].level = "
          "myleaf.body.node.nodes[idx_map[%lu]]->curr_pos.level;\n"
          "      myleaf.body.node.nodes[idx_map[%lu]]->calcof.lengthto.fields[nof_fields++].pos = "
          "myleaf.body.node.nodes[idx_map[%lu]]->curr_pos.pos;\n"
          "    } else {\n"
          "      myleaf.body.node.nodes[idx_map[%lu]]->"
          "calcof.lengthto.fields[nof_fields].level = 0;\n"
          "      myleaf.body.node.nodes[idx_map[%lu]]->"
          "calcof.lengthto.fields[nof_fields++].pos = 0;\n"
          "    }\n"
          "    if (idx_map_before[%lu] >= 0) {\n"
          "      myleaf.body.node.nodes[idx_map[%lu]]->calcof.lengthto.fields[nof_fields].level = "
          "myleaf.body.node.nodes[idx_map_before[%lu]]->curr_pos.level;\n"
          "      myleaf.body.node.nodes[idx_map[%lu]]->calcof.lengthto.fields[nof_fields++].pos = "
          "myleaf.body.node.nodes[idx_map_before[%lu]]->curr_pos.pos;\n"
          "    }\n"
          "    if (idx_map_after[%lu] >= 0) {\n"
          "      myleaf.body.node.nodes[idx_map[%lu]]->calcof.lengthto.fields[nof_fields].level = "
          "myleaf.body.node.nodes[idx_map_after[%lu]]->curr_pos.level;\n"
          "      myleaf.body.node.nodes[idx_map[%lu]]->calcof.lengthto.fields[nof_fields++].pos = "
          "myleaf.body.node.nodes[idx_map_after[%lu]]->curr_pos.pos;\n"
          "    }\n",
          (unsigned long)i,
          (unsigned long)sdef->elements[i].raw.lengthto[a],
          (unsigned long)i,
          (unsigned long)sdef->elements[i].raw.lengthto[a],
          (unsigned long)i, (unsigned long)i,
          (unsigned long)sdef->elements[i].raw.lengthto[a],
          (unsigned long)i,
          (unsigned long)sdef->elements[i].raw.lengthto[a],
          (unsigned long)i,
          (unsigned long)sdef->elements[i].raw.lengthto[a],
          (unsigned long)sdef->elements[i].raw.lengthto[a],
          (unsigned long)i,
          (unsigned long)sdef->elements[i].raw.lengthto[a],
          (unsigned long)i,
          (unsigned long)sdef->elements[i].raw.lengthto[a]);
      }
      /* Closing outer index check.  */
      src = mputstr(src,
        "  }\n");
    } else if (raw_options[i].pointerto) {
      /* Encoding of pointerto fields.  */
      if (sdef->elements[sdef->elements[i].raw.pointerto].isOptional) {
        src = mputprintf(src,
          "  if (field_%s.ispresent()) {\n",
          sdef->elements[sdef->elements[i].raw.pointerto].name);
      }
      src = mputprintf(src,
        "  boolean in_between_modified_pointerto_%s = FALSE;\n"
        "  for (int idx_map_idx = %d; idx_map_idx <= %d; ++idx_map_idx) {\n"
        "    if (idx_map[idx_map_idx] < 0) {\n"
        "      in_between_modified_pointerto_%s = TRUE;\n"
        "      break;\n"
        "    }\n"
        "  }\n"
        "  if (altered_[%lu] || altered_[%lu] ||\n"
        "      %lu != idx_map[%lu] - idx_map[%lu] || in_between_modified_pointerto_%s) {\n"
        "    e_c.set_msg(\"Field '%s' and/or '%s': \");\n"
        "    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_NEGTEST_CONFL,\n"
        "      \"Conflicting negative testing attributes, POINTERTO "
        "attribute will be ignored\");\n"
        "  } else {\n"
        "    encoded_length += %d;\n"
        "    myleaf.body.node.nodes[idx_map[%lu]]->calc = CALC_POINTER;\n"
        "    myleaf.body.node.nodes[idx_map[%lu]]->coding_descr = &%s_descr_;\n"
        "    myleaf.body.node.nodes[idx_map[%lu]]->calcof.pointerto.unit = %d;\n"
        "    myleaf.body.node.nodes[idx_map[%lu]]->calcof.pointerto.ptr_offset = %d;\n"
        "    myleaf.body.node.nodes[idx_map[%lu]]->calcof.pointerto.ptr_base = %d;\n"
        "    myleaf.body.node.nodes[idx_map[%lu]]->calcof.pointerto.target.level = "
        "myleaf.body.node.nodes[%d]->curr_pos.level;\n"
        "    myleaf.body.node.nodes[idx_map[%lu]]->calcof.pointerto.target.pos = "
        "myleaf.body.node.nodes[%d]->curr_pos.pos;\n"
        "    myleaf.body.node.nodes[idx_map[%lu]]->length = %d;\n"
        "  }\n",
        sdef->elements[i].name,
        i, sdef->elements[i].raw.pointerto,
        sdef->elements[i].name, (unsigned long)i,
        (unsigned long)sdef->elements[i].raw.pointerto,
        (unsigned long)sdef->elements[i].raw.pointerto - (unsigned long)i,
        (unsigned long)sdef->elements[i].raw.pointerto, (unsigned long)i,
        sdef->elements[i].name, sdef->elements[i].name,
        sdef->elements[sdef->elements[i].raw.pointerto].name,
        sdef->elements[i].raw.fieldlength, (unsigned long)i,
        (unsigned long)i, sdef->elements[i].typedescrname,
        (unsigned long)i, sdef->elements[i].raw.unit,
        (unsigned long)i, sdef->elements[i].raw.ptroffset,
        (unsigned long)i, sdef->elements[i].raw.pointerbase,
        (unsigned long)i, sdef->elements[i].raw.pointerto,
        (unsigned long)i, sdef->elements[i].raw.pointerto,
        (unsigned long)i, sdef->elements[i].raw.fieldlength);
      if (sdef->elements[sdef->elements[i].raw.pointerto].isOptional) {
        src = mputprintf(src,
        "  } else {\n"
        "    INTEGER atm;\n"
        "    atm = 0;\n"
        "    encoded_length += atm.RAW_encode(%s_descr_, "
        "*myleaf.body.node.nodes[idx_map[%lu]]);\n"
        "  }\n",
        sdef->elements[i].typedescrname, (unsigned long)i);
      }
    } else {
      /* Encoding of normal fields with no negative testing.  Needed for the
         later phase.  */
      src = mputprintf(src,
        "  if (!altered_[%lu] && idx_map[%lu] >= 0) "
        "encoded_length += field_%s%s.RAW_encode(%s_descr_, "
        "*myleaf.body.node.nodes[idx_map[%lu]]);\n",
        (unsigned long)i, (unsigned long)i, sdef->elements[i].name,
        sdef->elements[i].isOptional ? "()" : "",
        sdef->elements[i].typedescrname, (unsigned long)i);
    }
    if (sdef->elements[i].isOptional) {
      src = mputstr(src,
        "  }\n");
    }
  }
  for (i = 0; i < sdef->nElements; i++) {
    /* Fill presence, tag and crosstag.  */
    if (raw_options[i].lengthto && sdef->elements[i].raw.lengthindex) {
      /* Encoding of lenghto fields with lengthindex.  */
      int a;
      if (sdef->elements[i].isOptional) {
        src = mputprintf(src,
          "  if (field_%s.ispresent()) {\n",
          sdef->elements[i].name);
      }
      src = mputprintf(src,
        "  if (altered_[%lu]) {\n"
        "    e_c.set_msg(\"Field '%s': \");\n"
        "    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_NEGTEST_CONFL,\n"
        "      \"Conflicting negative testing attributes, LENGTHTO/LENGTHINDEX "
        "attribute will be ignored\");\n"
        "  } else {\n"
        "    if (myleaf.body.node.nodes[idx_map[%lu]]->body.node.nodes[%d]) {\n"
        "      boolean negtest_confl_lengthto = FALSE;\n",
        (unsigned long)i, sdef->elements[i].name, (unsigned long)i,
        sdef->elements[i].raw.lengthindex->nthfield);
      for (a = 0; a < sdef->elements[i].raw.lengthto_num; a++) {
        src = mputprintf(src,
          "      if (altered_[%lu]) {\n"
          "        negtest_confl_lengthto = TRUE;\n"
          "        e_c.set_msg(\"Field '%s': \");\n"
          "        TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_NEGTEST_CONFL,\n"
          "          \"Conflicting negative testing attributes, LENGTHTO/LENGTHINDEX "
          "attribute will be ignored\");\n"
          "      }\n",
          (unsigned long)sdef->elements[i].raw.lengthto[a],
          sdef->elements[sdef->elements[i].raw.lengthto[a]].name);
      }
      src = mputprintf(src,
        "      if (!negtest_confl_lengthto) {\n"
        "        myleaf.body.node.nodes[idx_map[%lu]]->body.node.nodes[%d]->"
        "calc = CALC_LENGTH;\n"
        "        myleaf.body.node.nodes[idx_map[%lu]]->body.node.nodes[%d]->"
        "coding_descr = &%s_descr_;\n"
        "        myleaf.body.node.nodes[idx_map[%lu]]->body.node.nodes[%d]->"
        "calcof.lengthto.num_of_fields = %d;\n"
        "        myleaf.body.node.nodes[idx_map[%lu]]->body.node.nodes[%d]->"
        "calcof.lengthto.unit = %d;\n"
        "        myleaf.body.node.nodes[idx_map[%lu]]->body.node.nodes[%d]->"
        "calcof.lengthto.offset = %d;\n"
        "        myleaf.body.node.nodes[idx_map[%lu]]->body.node.nodes[%d]->"
        "calcof.lengthto.fields = init_lengthto_fields_list(%d);\n",
        (unsigned long)i, sdef->elements[i].raw.lengthindex->nthfield,
        (unsigned long)i, sdef->elements[i].raw.lengthindex->nthfield,
        sdef->elements[i].raw.lengthindex->typedescr,
        (unsigned long)i, sdef->elements[i].raw.lengthindex->nthfield,
        sdef->elements[i].raw.lengthto_num,
        (unsigned long)i, sdef->elements[i].raw.lengthindex->nthfield,
        sdef->elements[i].raw.unit,
        (unsigned long)i, sdef->elements[i].raw.lengthindex->nthfield,
        sdef->elements[i].raw.lengthto_offset,
        (unsigned long)i, sdef->elements[i].raw.lengthindex->nthfield,
        sdef->elements[i].raw.lengthto_num);
      for (a = 0; a < sdef->elements[i].raw.lengthto_num; a++) {
        if (sdef->elements[sdef->elements[i].raw.lengthto[a]].isOptional) {
          src = mputprintf(src,
            "        if (field_%s.ispresent()) {\n",
            sdef->elements[sdef->elements[i].raw.lengthto[a]].name);
        }
        src = mputprintf(src,
          "        myleaf.body.node.nodes[idx_map[%lu]]->body.node.nodes[%d]"
          "->calcof.lengthto.fields[%d].level = "
          "myleaf.body.node.nodes[idx_map[%d]]->curr_pos.level;\n"
          "        myleaf.body.node.nodes[idx_map[%lu]]->body.node.nodes[%d]"
          "->calcof.lengthto.fields[%d].pos = "
          "myleaf.body.node.nodes[idx_map[%d]]->curr_pos.pos;\n",
          (unsigned long)i, sdef->elements[i].raw.lengthindex->nthfield, a,
          sdef->elements[i].raw.lengthto[a], (unsigned long)i,
          sdef->elements[i].raw.lengthindex->nthfield, a,
          sdef->elements[i].raw.lengthto[a]);
        if (sdef->elements[sdef->elements[i].raw.lengthto[a]].isOptional) {
          src = mputprintf(src,
            "        } else {\n"
            "        myleaf.body.node.nodes[idx_map[%lu]]->body.node.nodes[%d]->"
            "calcof.lengthto.fields[%d].level = 0;\n"
            "        myleaf.body.node.nodes[idx_map[%lu]]->body.node.nodes[%d]->"
            "calcof.lengthto.fields[%d].pos = 0;\n"
            "        }\n",
           (unsigned long)i, sdef->elements[i].raw.lengthindex->nthfield, a,
           (unsigned long)i, sdef->elements[i].raw.lengthindex->nthfield, a);
        }
      }
      /* Closing index check.  */
      src = mputstr(src,
        "      }\n");
      src = mputstr(src, "    }\n  }\n");
      if (sdef->elements[i].isOptional) {
        src = mputstr(src, "  }\n");
      }
    }
    if (raw_options[i].lengthto && sdef->elements[i].raw.union_member_num) {
      /* Encoding of lenghto fields.  */
      int a;
      if (sdef->elements[i].isOptional) {
        src = mputprintf(src,
          "  if (field_%s.ispresent()) ", sdef->elements[i].name);
      }
      src = mputprintf(src,
        "  {\n"
        "  if (altered_[%lu]) {\n"
        "    e_c.set_msg(\"Field '%s': \");\n"
        "    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_NEGTEST_CONFL,\n"
        "      \"Conflicting negative testing attributes, LENGTHTO/LENGTHINDEX "
        "attribute will be ignored\");\n"
        "  } else {\n"
        "    boolean negtest_confl_lengthto = FALSE;\n",
        (unsigned long)i, sdef->elements[i].name);
      for (a = 0; a < sdef->elements[i].raw.lengthto_num; a++) {
        src = mputprintf(src,
          "  if (altered_[%lu]) {\n"
          "    negtest_confl_lengthto = TRUE;\n"
          "    e_c.set_msg(\"Field '%s': \");\n"
          "      TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_NEGTEST_CONFL,\n"
          "        \"Conflicting negative testing attributes, LENGTHTO/LENGTHINDEX "
          "attribute will be ignored\");\n"
          "  }\n",
          (unsigned long)sdef->elements[i].raw.lengthto[a],
          sdef->elements[sdef->elements[i].raw.lengthto[a]].name);
      }
      src = mputprintf(src,
        "    if (!negtest_confl_lengthto) {\n"
        "      int sel_field = 0;\n"
        "      while (myleaf.body.node.nodes[idx_map[%lu]]->body.node.nodes[sel_field] == NULL) "
        "{ sel_field++; }\n"
        "      myleaf.body.node.nodes[idx_map[%lu]]->body.node.nodes[sel_field]->"
        "calc = CALC_LENGTH;\n"
        "      myleaf.body.node.nodes[idx_map[%lu]]->body.node.nodes[sel_field]->"
        "calcof.lengthto.num_of_fields = %d;\n"
        "      myleaf.body.node.nodes[idx_map[%lu]]->body.node.nodes[sel_field]->"
        "calcof.lengthto.unit = %d;\n"
        "      myleaf.body.node.nodes[idx_map[%lu]]->body.node.nodes[sel_field]->"
        "calcof.lengthto.offset = %d;\n"
        "      myleaf.body.node.nodes[idx_map[%lu]]->body.node.nodes[sel_field]->"
        "calcof.lengthto.fields = init_lengthto_fields_list(%d);\n",
        (unsigned long)i, (unsigned long)i,
        (unsigned long)i, sdef->elements[i].raw.lengthto_num,
        (unsigned long)i, sdef->elements[i].raw.unit,
        (unsigned long)i, sdef->elements[i].raw.lengthto_offset,
        (unsigned long)i, sdef->elements[i].raw.lengthto_num);
      for (a = 0; a < sdef->elements[i].raw.lengthto_num; a++) {
        if (sdef->elements[sdef->elements[i].raw.lengthto[a]].isOptional) {
          src = mputprintf(src,
            "      if (field_%s.ispresent()) {\n",
            sdef->elements[sdef->elements[i].raw.lengthto[a]].name);
        }
        src = mputprintf(src,
          "      myleaf.body.node.nodes[idx_map[%lu]]->body.node.nodes[sel_field]"
          "->calcof.lengthto.fields[%d].level = "
          "myleaf.body.node.nodes[%d]->curr_pos.level;\n"
          "      myleaf.body.node.nodes[idx_map[%lu]]->body.node.nodes[sel_field]"
          "->calcof.lengthto.fields[%d].pos = "
          "myleaf.body.node.nodes[%d]->curr_pos.pos;\n",
          (unsigned long)i, a, sdef->elements[i].raw.lengthto[a],
          (unsigned long)i, a, sdef->elements[i].raw.lengthto[a]);
        if (sdef->elements[sdef->elements[i].raw.lengthto[a]].isOptional) {
          src = mputprintf(src,
            "      } else {\n"
            "      myleaf.body.node.nodes[idx_map[%lu]]->body.node.nodes[sel_field]"
            "->calcof.lengthto.fields[%d].level = 0;\n"
            "      myleaf.body.node.nodes[idx_map[%lu]]->body.node.nodes[sel_field]"
            "->calcof.lengthto.fields[%d].pos = 0;\n"
            "      }\n",
            (unsigned long)i, a,
            (unsigned long)i, a);
        }
      }
      /* Closing inner index check.  */
      src = mputstr(src,
        "    }\n");
      /* Closing outer index check.  */
      src = mputstr(src,
        "  }\n");
      /* Closing ispresent or local block.  */
      src = mputstr(src,
        "  }\n");
    }
    if (raw_options[i].tag_type &&
        sdef->raw.taglist.list[raw_options[i].tag_type - 1].nElements) {
      /* Plain, old tag.  */
      rawAST_coding_taglist *cur_choice =
        sdef->raw.taglist.list + raw_options[i].tag_type - 1;
      src = mputprintf(src,
        "  boolean negtest_confl_tag_%s = FALSE;\n"
        "  if (altered_[%lu]) {\n"
        "    negtest_confl_tag_%s = TRUE;\n"
        "    e_c.set_msg(\"Field '%s': \");\n"
        "    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_NEGTEST_CONFL,\n"
        "      \"Conflicting negative testing attributes, TAG attribute will "
        "be ignored\");\n"
        "  }\n",
        sdef->elements[i].name, (unsigned long)i, sdef->elements[i].name,
        sdef->elements[i].name);
      src = mputprintf(src, "  if (!negtest_confl_tag_%s && (",
                       sdef->elements[i].name);
      if (sdef->elements[i].isOptional) {
        src = mputprintf(src, "field_%s.ispresent() && (", sdef->elements[i].name);
      }
      src = genRawFieldChecker(src, cur_choice, FALSE);
      if (sdef->elements[i].isOptional) src = mputstr(src, ")");
      src = mputstr(src,")) {\n");
      src = genRawTagChecker(src, cur_choice);
      src = mputstr(src,"  }\n");
    }
    if (sdef->elements[i].hasRaw && sdef->elements[i].raw.presence.nElements) {
      /* Presence attribute for fields.  */
      int taglist_idx, field_idx;
      /* The optional field itself.  */
      src = mputprintf(src,
        "  boolean negtest_confl_presence_%s = FALSE;\n"
        "  if (altered_[%lu]) {\n"
        "    negtest_confl_presence_%s = TRUE;\n"
        "    e_c.set_msg(\"Field '%s': \");\n"
        "    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_NEGTEST_CONFL,\n"
        "      \"Conflicting negative testing attributes, PRESENCE attribute "
        "will be ignored\");\n"
        "  }\n",
        sdef->elements[i].name, (unsigned long)i, sdef->elements[i].name,
        sdef->elements[i].name);
      /* Check the referenced fields.  */
      for (taglist_idx = 0;
           taglist_idx < sdef->elements[i].raw.presence.nElements; ++taglist_idx) {
        rawAST_coding_field_list *fields =
          sdef->elements[i].raw.presence.fields + taglist_idx;
        for (field_idx = 0; field_idx < fields->nElements; ++field_idx) {
          /* The top level field is enough.  >0 index is for subrefs.  */
          if (field_idx == 0) {
            rawAST_coding_fields *field = fields->fields + field_idx;
            src = mputprintf(src,
              "  if (altered_[%d]) {\n"
              "    negtest_confl_presence_%s = TRUE;\n"
              "    e_c.set_msg(\"Field '%s': \");\n"
              "    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_NEGTEST_CONFL,\n"
              "      \"Conflicting negative testing attributes, PRESENCE attribute "
              "will be ignored\");\n"
              "  }\n",
              field->nthfield, sdef->elements[i].name, field->nthfieldname);
          } else {
            /* Subrefs.  */
            break;
          }
        }
      }
      src = mputprintf(src, "  if (!negtest_confl_presence_%s && (",
                       sdef->elements[i].name);
      if (sdef->elements[i].isOptional) {
        src = mputprintf(src, "field_%s.ispresent() && (", sdef->elements[i].name);
      }
      src = genRawFieldChecker(src, &sdef->elements[i].raw.presence, FALSE);
      if (sdef->elements[i].isOptional) src = mputstr(src, ")");
      src = mputstr(src, ")) {\n");
      src = genRawTagChecker(src, &sdef->elements[i].raw.presence);
      src = mputstr(src, "  }\n");
    }
    if (sdef->elements[i].hasRaw &&
        sdef->elements[i].raw.crosstaglist.nElements) {
      /* crosstag */
      int a;
      /* The union field itself.  */
      src = mputprintf(src,
        "  if (altered_[%lu]) {\n"
        "    e_c.set_msg(\"Field '%s': \");\n"
        "    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_NEGTEST_CONFL,\n"
        "      \"Conflicting negative testing attributes, CROSSTAG attribute "
        "will be ignored\");\n"
        "  } else {\n",
        (unsigned long)i, sdef->elements[i].name);
      if (sdef->elements[i].isOptional) {
        src = mputprintf(src,
          "  if (field_%s.ispresent()) {\n",
          sdef->elements[i].name);
      }
      src = mputprintf(src,
        "  switch (field_%s%s.get_selection()) {\n",
        sdef->elements[i].name,sdef->elements[i].isOptional ? "()" : "");
      for (a = 0; a < sdef->elements[i].raw.crosstaglist.nElements; a++) {
        rawAST_coding_taglist *cur_choice =
          sdef->elements[i].raw.crosstaglist.list + a;
        if (cur_choice->nElements > 0) {
          int taglist_idx, field_idx;
          src = mputprintf(src,
            "  case %s%s%s: {\n"
            "  boolean negtest_confl_crosstag = FALSE;\n",
            sdef->elements[i].type, "::ALT_",
            cur_choice->fieldName);
          for (taglist_idx = 0;
               taglist_idx < cur_choice->nElements; ++taglist_idx) {
            rawAST_coding_field_list *fields = cur_choice->fields + taglist_idx;
            for (field_idx = 0; field_idx < fields->nElements; ++field_idx) {
              if (field_idx == 0) {
                rawAST_coding_fields *field = fields->fields + field_idx;
                src = mputprintf(src,
                  "  if (altered_[%d]) {\n"
                  "    negtest_confl_crosstag = TRUE;\n"
                  "    e_c.set_msg(\"Field '%s': \");\n"
                  "    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_NEGTEST_CONFL,\n"
                  "      \"Conflicting negative testing attributes, CROSSTAG attribute "
                  "will be ignored\");\n"
                  "  }\n",
                  field->nthfield, field->nthfieldname);
              } else {
                break;
              }
            }
          }
          src = mputstr(src, "  if (!negtest_confl_crosstag && (");
          src = genRawFieldChecker(src, cur_choice, FALSE);
          src = mputstr(src, ")) {\n");
          if (!strcmp(cur_choice->fields[0].value, "OMIT_VALUE")) {
            if (cur_choice->fields[0].nElements != 1)
              NOTSUPP("omit value with multiple fields in CROSSTAG");
            /* eliminating the corresponding encoded leaf, which will have
               the same effect as if the referred field had been omit */
            src = mputprintf(src,
              "  encoded_length -= myleaf.body.node.nodes[%d]->length;\n"
              "  delete myleaf.body.node.nodes[%d];\n"
              "  myleaf.body.node.nodes[%d] = NULL;\n",
              cur_choice->fields[0].fields[0].nthfield,
              cur_choice->fields[0].fields[0].nthfield,
              cur_choice->fields[0].fields[0].nthfield);
          } else {
            int ll;
            src = mputprintf(src,
              "  RAW_enc_tr_pos pr_pos;\n"
              "  pr_pos.level = myleaf.curr_pos.level + %d;\n"
              "  int new_pos[] = { ",
              cur_choice->fields[0].nElements);
            for (ll = 0; ll < cur_choice->fields[0].nElements; ll++) {
              if (ll > 0) src = mputstr(src, ", ");
              src = mputprintf(src, "%d",
                cur_choice->fields[0].fields[ll].nthfield);
            }
            src = mputprintf(src, " };\n"
              "  pr_pos.pos = init_new_tree_pos(myleaf.curr_pos, %d, "
              "new_pos);\n",
            cur_choice->fields[0].nElements);
            if (cur_choice->fields[0].value[0] == ' ') {
              /* the value is a string literal (the encoder can be called
                 on that object directly */
              src = mputprintf(src,
                "  RAW_enc_tree* temp_leaf = myleaf.get_node(pr_pos);\n"
                "  if (temp_leaf != NULL)\n"
                "    %s.RAW_encode(%s_descr_, *temp_leaf);\n"
                "  else\n"
                "    TTCN_EncDec_ErrorContext::error\n"
                "      (TTCN_EncDec::ET_OMITTED_TAG, \"Encoding a tagged, "
                "but omitted value.\");\n",
                cur_choice->fields[0].value,
                cur_choice->fields[0].fields[
                  cur_choice->fields[0].nElements - 1].typedescr);
            } else {
              /* a temporary object needs to be created for encoding */
              src = mputprintf(src,
                "  %s new_val(%s);\n"
                "  RAW_enc_tree* temp_leaf = myleaf.get_node(pr_pos);\n"
                "  if (temp_leaf != NULL)\n"
                "    new_val.RAW_encode(%s_descr_, *temp_leaf);\n"
                "  else\n"
                "    TTCN_EncDec_ErrorContext::error\n"
                "      (TTCN_EncDec::ET_OMITTED_TAG, \"Encoding a tagged, "
                "but omitted value.\");\n",
                cur_choice->fields[0].fields[
                  cur_choice->fields[0].nElements - 1].type,
                cur_choice->fields[0].value,
                cur_choice->fields[0].fields[
                  cur_choice->fields[0].nElements - 1].typedescr);
            }
            src = mputstr(src, "  free_tree_pos(pr_pos.pos);\n");
          }
          src = mputstr(src, "  }\n"
            "  break; }\n");
        }
      }
      src = mputstr(src, "  default:;\n"
        "  }\n");
      if (sdef->elements[i].isOptional) src = mputstr(src, "  }\n");
      src = mputstr(src, "  }\n");
    }
  }
  src = mputprintf(src,
    "  node_pos = 0;\n"
    "  next_optional_idx = 0;\n"
    "  for (int field_idx = 0; field_idx < num_fields; ++field_idx) {\n"
    "    boolean is_optional_field = my_optional_indexes &&\n"
    "      (my_optional_indexes[next_optional_idx] == field_idx);\n"
    "    if ((p_err_descr->omit_before != -1) &&\n"
    "        (field_idx < p_err_descr->omit_before)) {\n"
    "      if (is_optional_field) ++next_optional_idx;\n"
    "      continue;\n"
    "    }\n"
    "    const Erroneous_values_t *err_vals = p_err_descr->get_field_err_values(field_idx);\n"
    "    const Erroneous_descriptor_t *emb_descr = p_err_descr->get_field_emb_descr(field_idx);\n"
    "    if (err_vals && err_vals->before) {\n"
    "      if (err_vals->before->errval == NULL)\n"
    "        TTCN_error(\"internal error: erroneous before value missing\");\n"
    "      if (err_vals->before->raw) {\n"
    "        encoded_length += err_vals->before->errval->"
    "RAW_encode_negtest_raw(*myleaf.body.node.nodes[node_pos++]);\n"
    "      } else {\n"
    "        if (err_vals->before->type_descr == NULL)\n"
    "          TTCN_error(\"internal error: erroneous before typedescriptor missing\");\n"
    "        encoded_length += err_vals->before->errval->RAW_encode(*(err_vals->before->type_descr),\n"
    "          *myleaf.body.node.nodes[node_pos++]);\n"
    "      }\n"
    "    }\n"
    "    if (err_vals && err_vals->value) {\n"
    "      if (err_vals->value->errval) {\n"
    "        e_c.set_msg(\"'%%s'(erroneous value): \", fld_name(field_idx));\n"
    "        if (err_vals->value->raw) {\n"
    "          encoded_length += err_vals->value->errval->"
    "RAW_encode_negtest_raw(*myleaf.body.node.nodes[node_pos++]);\n"
    "        } else {\n"
    "          if (err_vals->value->type_descr == NULL)\n"
    "            TTCN_error(\"internal error: erroneous value typedescriptor missing\");\n"
    "          encoded_length += err_vals->value->errval->"
    "RAW_encode(*(err_vals->value->type_descr), *myleaf.body.node.nodes[node_pos++]);\n"
    "        }\n"
    "      }\n"
    "    } else {\n"
    "      e_c.set_msg(\"'%%s': \", fld_name(field_idx));\n"
    "      if (!is_optional_field || get_at(field_idx)->ispresent()) {\n"
    "        const Base_Type *curr_field = is_optional_field "
    "? get_at(field_idx)->get_opt_value() : get_at(field_idx);\n"
    "        if (emb_descr) {\n"
    "          encoded_length += curr_field\n"
    "            ->RAW_encode_negtest(emb_descr, *fld_descr(field_idx),\n"
    "                                 *myleaf.body.node.nodes[node_pos]);\n"
    "        }\n"
    "      } else {\n"
    "        // `omitted' field.\n"
    "        myleaf.body.node.nodes[node_pos] = NULL;\n"
    "      }\n"
    "      ++node_pos;\n"
    "    }\n"
    "    if (err_vals && err_vals->after) {\n"
    "      if (err_vals->after->errval == NULL)\n"
    "        TTCN_error(\"internal error: erroneous before value missing\");\n"
    "      if (err_vals->after->raw) {\n"
    "        encoded_length += err_vals->after->errval->"
    "RAW_encode_negtest_raw(*myleaf.body.node.nodes[node_pos++]);\n"
    "      } else {\n"
    "        if (err_vals->after->type_descr == NULL)\n"
    "          TTCN_error(\"internal error: erroneous after typedescriptor missing\");\n"
    "        encoded_length += err_vals->after->errval->"
    "RAW_encode(*(err_vals->after->type_descr), *myleaf.body.node.nodes[node_pos++]);\n"
    "      }\n"
    "    }\n"
    "    if (is_optional_field) ++next_optional_idx;\n"
    "    if ((p_err_descr->omit_after != -1) &&\n"
    "        (field_idx >= p_err_descr->omit_after))\n"
    "      break;\n"
    "  }\n");
  /* Presence for the whole record.  */
  if (sdef->hasRaw && sdef->raw.presence.nElements > 0) {
    /* Check the referenced fields.  */
    int taglist_idx, field_idx;
    src = mputstr(src, "  boolean negtest_confl_presence = FALSE;\n");
    for (taglist_idx = 0; taglist_idx < sdef->raw.presence.nElements; ++taglist_idx) {
      rawAST_coding_field_list *fields =
        sdef->raw.presence.fields + taglist_idx;
      for (field_idx = 0; field_idx < fields->nElements; ++field_idx) {
        if (field_idx == 0) {
          rawAST_coding_fields *field = fields->fields + field_idx;
          src = mputprintf(src,
            "  if (altered_[%d]) {\n"
            "    negtest_confl_presence = TRUE;\n"
            "    e_c.set_msg(\"Field '%s': \");\n"
            "    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_NEGTEST_CONFL,\n"
            "      \"Conflicting negative testing attributes, PRESENCE attribute "
            "will be ignored\");\n"
            "  }\n",
            field->nthfield, field->nthfieldname);
        } else {
          break;
        }
      }
    }
    src = mputstr(src, "  if (!negtest_confl_presence && (");
    src = genRawFieldChecker(src, &sdef->raw.presence, FALSE);
    src = mputstr(src,")) {\n");
    src = genRawTagChecker(src, &sdef->raw.presence);
    src = mputstr(src,"  }\n");
  }
  src = mputstr(src,
    "  return myleaf.length = encoded_length;\n"
    "}\n\n");
  return src;
}

void gen_xer(const struct_def *sdef, char **pdef, char **psrc)
{ /* XERSTUFF codegen for record/SEQUENCE */
  size_t i;
  char *def = *pdef, *src = *psrc;
  const char * const name = sdef->name;

  /*
   * NOTE! Decisions based on the coding instructions of the components
   * (e.g. number of attributes) can be made at compile time.
   * Decisions on coding instructions of the record itself (e.g. D-F-E,
   * EMBED-VALUES, USE-NIL, USE-ORDER) __must__ be postponed to run-time,
   * because these can be applied to type references,
   * which will not be visible when the code is generated.
   */

  /* Number of fields with ATTRIBUTE, includes ANY-ATTRIBUTES */
  size_t num_attributes = 0;

  /* index of USE-ORDER member, which may be bumped into second place by EMBED-VALUES */
  size_t uo = (sdef->xerEmbedValuesPossible !=0);

  /* start_at is the index of the first "real" member of the record */
  size_t start_at = uo + (sdef->xerUseOrderPossible != 0);

  /* Number of optional non-attributes */
  size_t n_opt_elements = 0;

  const boolean want_namespaces = !sdef->isASN1 /* TODO remove this when ASN.1 gets EXER */;

  int aa_index = -1;
  /* Find the number of attributes (compile time) */
  for (i = 0; i < sdef->nElements; ++i) {
    if (sdef->elements[i].xerAttribute) {
      ++num_attributes;
    }
    else if (sdef->elements[i].xerAnyKind & ANY_ATTRIB_BIT) {
      ++num_attributes; /* ANY-ATTRIBUTES also included with attributes */
      aa_index = i;
      /* If the first member is ANY-ATTRIBUTES, then xerEmbedValuesPossible
       * is merely an illusion and USE-ORDER is not possible. */
      if (i==0) {
        start_at = uo = 0;
      }
    }
    else /* not attribute */ if (sdef->elements[i].isOptional) {
      ++n_opt_elements;
    }
  }

  /* Write some helper functions */
  def = mputstr(def,
    "char **collect_ns(const XERdescriptor_t& p_td, size_t& num_ns, bool& def_ns, unsigned int flavor = 0) const;\n");

  src = mputprintf(src,
    "char ** %s::collect_ns(const XERdescriptor_t& p_td, size_t& num_ns, bool& def_ns, unsigned int flavor) const {\n"
    "  size_t num_collected;\n"
    "  char **collected_ns = Base_Type::collect_ns(p_td, num_collected, def_ns, flavor);\n"
    /* The above might throw but then nothing was allocated. */
    , name);

  /* Collect namespaces from the "non-special" members (incl. attributes) */
  if (start_at < sdef->nElements) src = mputstr(src,
    "  try {\n"
    "  char **new_ns;\n"
    "  size_t num_new;\n"
    "  boolean def_ns_1 = FALSE;\n");
  for (i = start_at; i < sdef->nElements; ++i) {
    src = mputprintf(src,
      "  new_ns = field_%s.collect_ns(%s_xer_, num_new, def_ns, flavor);\n"
      "  merge_ns(collected_ns, num_collected, new_ns, num_new);\n"
      "  def_ns = def_ns || def_ns_1;\n" /* alas, no ||= */
      , sdef->elements[i].name, sdef->elements[i].typegen
    );
  }
  if (sdef->xerUseNilPossible) {
    src = mputprintf(src,
      "  if((p_td.xer_bits & USE_NIL) && !field_%s.ispresent()) {\n" /* "nil" attribute to be written*/
      "    collected_ns = (char**)Realloc(collected_ns, sizeof(char*) * ++num_collected);\n"
      "    const namespace_t *c_ns = p_td.my_module->get_controlns();\n"
      "    collected_ns[num_collected-1] = mprintf(\" xmlns:%%s='%%s'\", c_ns->px, c_ns->ns);\n"
      "  }\n"
      , sdef->elements[sdef->nElements-1].name
    );
  }
  if (start_at < sdef->nElements) src = mputstr(src,
    "  }\n"
    "  catch (...) {\n"
    /* Probably a TC_Error thrown from field_%s->collect_ns() if e.g.
     * encoding an unbound value. */
    "    while (num_collected > 0) Free(collected_ns[--num_collected]);\n"
    "    Free(collected_ns);\n"
    "    throw;\n"
    "  }\n"
  );

  src = mputstr(src,
    "  num_ns = num_collected;\n"
    "  return collected_ns;\n"
    "}\n\n"
  );

  src = mputprintf(src,
    "boolean %s::can_start(const char *name, const char *uri, const XERdescriptor_t& xd, unsigned int flavor, unsigned int flavor2) {\n"
    "  boolean e_xer = is_exer(flavor &= ~XER_RECOF);\n"
    "  if (!e_xer || !((xd.xer_bits & UNTAGGED) || (flavor & (USE_NIL|XER_RECOF)))) return check_name(name, xd, e_xer) && (!e_xer || check_namespace(uri, xd));\n"
    , name
  );
  for (i = start_at; i < sdef->nElements; ++i) {
    src = mputprintf(src,
      "  else if (%s::can_start(name, uri, %s_xer_, flavor, flavor2)) return TRUE;\n"
      /* Here we know for sure it's exer */
      , sdef->elements[i].type
      , sdef->elements[i].typegen
    );
    if (!sdef->elements[i].isOptional) break;
    /* The last component with which it can begin is the first non-optional.
     * Does that sentence makes sense to you ? */
  }
  for (i = 0; i < sdef->nElements; i++) {
    if (strcmp(sdef->elements[i].type, "UNIVERSAL_CHARSTRING") == 0 &&
        !sdef->elements[i].xerAttribute) {
      src = mputprintf(src,
        "  else if (%s_xer_.xer_bits & ANY_ELEMENT) return TRUE;\n"
        , sdef->elements[i].typegen);
    }
    if (!sdef->elements[i].isOptional && !sdef->elements[i].xerAttribute) {
      break;
    }
  }
  src = mputstr(src, "  return FALSE;\n}\n\n");

  /* * * * * * * * * * XER_encode * * * * * * * * * * * * * * */
  src = mputprintf(src,
    "int %s::XER_encode(const XERdescriptor_t& p_td, "
    "TTCN_Buffer& p_buf, unsigned int p_flavor, unsigned int p_flavor2, int p_indent, embed_values_enc_struct_t* emb_val_parent) const\n"
    "{\n"
    "  if (!is_bound()) TTCN_EncDec_ErrorContext::error"
    "(TTCN_EncDec::ET_UNBOUND, \"Encoding an unbound value.\");\n"
    "  TTCN_EncDec_ErrorContext ec_0(\"Component '\");\n"
    "  TTCN_EncDec_ErrorContext ec_1;\n"
    "  int encoded_length=(int)p_buf.get_len();\n"
    "  boolean e_xer = is_exer(p_flavor);\n"
    "  const boolean omit_tag = e_xer && p_indent "
    "&& ((p_td.xer_bits & (UNTAGGED|XER_ATTRIBUTE)) || (p_flavor & (USE_NIL|USE_TYPE_ATTR)));\n"
    "  if (e_xer && (p_td.xer_bits & EMBED_VALUES)) p_flavor |= XER_CANONICAL;\n"
    "  int is_indented = !is_canonical(p_flavor);\n"
    , name);

  if (want_namespaces && !(num_attributes|sdef->xerUseQName)) src = mputstr(src,
    "  const boolean need_control_ns = (p_td.xer_bits & (USE_NIL));\n");

  if (want_namespaces && (start_at < sdef->nElements)) { /* there _are_ non-special members */
    src = mputstr(src,
      "  size_t num_collected = 0;\n"
      "  char **collected_ns = NULL;\n"
      "  boolean def_ns = FALSE;\n"
      "  if (e_xer) {\n"
      "    if (p_indent == 0) {\n" /* top-level */
      "      collected_ns = collect_ns(p_td, num_collected, def_ns, p_flavor2);\n" /* our own ns */
      "    }\n"
      "    else if ((p_flavor & DEF_NS_SQUASHED) && p_td.my_module && p_td.ns_index != -1){\n"
      "      const namespace_t * ns = p_td.my_module->get_ns((size_t)p_td.ns_index);\n"
      "      if (*ns->px == '\\0') {\n"
      "        collected_ns = Base_Type::collect_ns(p_td, num_collected, def_ns, p_flavor2);\n"
      "      }\n"
      "    }\n"
      "  }\n"
    );
  }

  if (want_namespaces) {
    src = mputstr(src,
      "  const boolean empty_ns_hack = e_xer && !omit_tag && (p_indent > 0)\n"
      "    && (p_td.xer_bits & FORM_UNQUALIFIED)\n"
      "    && p_td.my_module && p_td.ns_index != -1\n"
      "    && *p_td.my_module->get_ns((size_t)p_td.ns_index)->px == '\\0';\n"
    );

    src = mputstr(src, "  boolean delay_close = e_xer");
    if (!(num_attributes | sdef->xerUseQName)) {
      src = mputprintf(src, " && (need_control_ns%s || empty_ns_hack)",
        (start_at < sdef->nElements) ? " || num_collected" : "");
    }
    src = mputstr(src, ";\n");
  }


  { /* write start tag */
    /* From "name>\n":
     * lose the \n if : not indenting or (single untagged(*) or attributes (*)) AND e_xer
     * lose the > if attributes are present (*) AND e_xer
     */
    src = mputprintf(src,
      "  size_t chopped_chars = 0;\n"
      "  if (!omit_tag) {\n"
      "    if (is_indented) do_indent(p_buf, p_indent);\n"
      "    p_buf.put_c('<');\n"
      "    if (e_xer) write_ns_prefix(p_td, p_buf);\n"
      "    p_buf.put_s((size_t)p_td.namelens[e_xer]%s-(!is_indented%s), "
      "(cbyte*)p_td.names[e_xer]);\n"
      "  }\n"
      "  else if (p_flavor & (USE_NIL|USE_TYPE_ATTR)) {\n"
      "    size_t buf_len = p_buf.get_len();\n"
      "    const unsigned char * const buf_data = p_buf.get_data();\n"
      "    if (buf_data[buf_len-1-chopped_chars] == '\\n') ++chopped_chars;\n"
      "    if (buf_data[buf_len-1-chopped_chars] == '>' ) ++chopped_chars;\n"
      "    if (chopped_chars) {\n"
      "      p_buf.increase_length(-chopped_chars);\n"
      "    }\n"
      "%s"
      "  }\n"
      , (want_namespaces ?   "-(delay_close || (e_xer && (p_td.xer_bits & HAS_1UNTAGGED)))" : "")
      , (want_namespaces ? " || delay_close" : "")
      , (want_namespaces ? "    delay_close = TRUE;\n" : ""));
  }

  src = mputprintf(src,
    "  int sub_len=0%s;\n"
    "  p_flavor &= XER_MASK;\n",
    num_attributes ? ", tmp_len; (void)tmp_len" : "");

  if (sdef->xerUseQName) {
    src = mputprintf(src,
      "  if (e_xer && (p_td.xer_bits & USE_QNAME)) {\n"
      "    if (field_%s.is_value()) {\n"
      "      p_buf.put_s(11, (cbyte*)\" xmlns:b0='\");\n"
      "      field_%s.XER_encode(%s_xer_, p_buf, p_flavor | XER_LIST, p_flavor2, p_indent+1, 0);\n"
      "      p_buf.put_c('\\'');\n"
      "    }\n"
      "    if (p_td.xer_bits & XER_ATTRIBUTE) begin_attribute(p_td, p_buf);\n"
      "    else  p_buf.put_c('>');\n"
      "    if (field_%s.is_value()) {\n"
      "      p_buf.put_s(3, (cbyte*)\"b0:\");\n"
      "      sub_len += 3;\n"
      "    }\n"
      "    sub_len += field_%s.XER_encode(%s_xer_, p_buf, p_flavor | XER_LIST, p_flavor2, p_indent+1, 0);\n"
      "    if (p_td.xer_bits & XER_ATTRIBUTE) p_buf.put_c('\\'');\n"
      "  } else" /* no newline */
      , sdef->elements[0].name
      , sdef->elements[0].name, sdef->elements[0].typegen
      , sdef->elements[0].name
      , sdef->elements[1].name, sdef->elements[1].typegen
    );
  }
  src = mputstr(src, "  { // !QN\n");
 
 /* First, the EMBED-VALUES member as an ordinary member if not doing EXER */
  if (sdef->xerEmbedValuesPossible) {
    src = mputprintf(src,
      "  if (!e_xer && (p_td.xer_bits & EMBED_VALUES)) {\n"
      "    ec_1.set_msg(\"%s': \");\n"
      "    sub_len += field_%s.XER_encode(%s_xer_, p_buf, p_flavor, p_flavor2, p_indent+1, 0);\n"
      "  }\n"
      , sdef->elements[0].dispname
      , sdef->elements[0].name, sdef->elements[0].typegen
    );
  }
  /* Then, the USE-ORDER member as an ordinary member when !EXER */
  if (sdef->xerUseOrderPossible) {
    src = mputprintf(src,
      "  if (!e_xer && (p_td.xer_bits & USE_ORDER)) {\n"
      "    sub_len += field_%s.XER_encode(%s_xer_, p_buf, p_flavor, p_flavor2, p_indent+1, 0);\n"
      "  }\n"
      , sdef->elements[uo].name, sdef->elements[uo].typegen
    );
  }

  if (start_at+num_attributes > sdef->nElements) FATAL_ERROR("defRecordClass1");

  if (want_namespaces && (start_at < sdef->nElements)) {
    src = mputstr(src,
      "  if (e_xer && num_collected) {\n"
      "    size_t num_ns;\n"
      "    for (num_ns = 0; num_ns < num_collected; ++num_ns) {\n"
      "      p_buf.put_s(strlen(collected_ns[num_ns]), (cbyte*)collected_ns[num_ns]);\n"
      "      Free(collected_ns[num_ns]);\n"
      "    }\n"
      "    Free(collected_ns);\n"
      "  }\n\n"
      "  if (def_ns) {\n"
      "    p_flavor &= ~DEF_NS_SQUASHED;\n"
      "    p_flavor |=  DEF_NS_PRESENT;\n"
      "  }\n"
      "  else if (empty_ns_hack) {\n"
      "    p_buf.put_s(9, (cbyte*)\" xmlns=''\");\n"
      "    p_flavor &= ~DEF_NS_PRESENT;\n"
      "    p_flavor |=  DEF_NS_SQUASHED;\n"
      "  }\n");
  }

  /* Then, all the attributes (not added to sub_len) */
  for ( i = start_at; i < start_at + num_attributes; ++i ) {
    /* If the first component is a record-of with ANY-ATTRIBUTES,
     * it has been taken care of because it looked like an EMBED-VALUES */
    if (i==0 && sdef->xerEmbedValuesPossible && (sdef->elements[i].xerAnyKind & ANY_ATTRIB_BIT)) continue ;
    src = mputprintf(src, 
      "  ec_1.set_msg(\"%s': \");\n"
      "  tmp_len = field_%s.XER_encode(%s_xer_, p_buf, p_flavor, p_flavor2, p_indent+1, 0);\n"
      "  %ssub_len += tmp_len;\n" /* do not add if attribute and EXER */
      , sdef->elements[i].dispname
      , sdef->elements[i].name, sdef->elements[i].typegen
      , sdef->elements[i].xerAttribute || (sdef->elements[i].xerAnyKind & ANY_ATTRIB_BIT) ? "if (!e_xer) " : ""
    );
  }

  if (sdef->xerUseNilPossible) {
    src = mputprintf(src,
      "  boolean nil_attribute = e_xer && (p_td.xer_bits & USE_NIL) && !field_%s.ispresent();\n"
      "  if (nil_attribute) {\n"
      "    const namespace_t *control_ns = p_td.my_module->get_controlns();\n"
      "    p_buf.put_c(' ');\n"
      "    p_buf.put_s(strlen(control_ns->px), (cbyte*)control_ns->px);\n"
      "    p_buf.put_c(':');\n"
      "    p_buf.put_s(10, (cbyte*)\"nil='true'\");\n"
      "  }\n"
      , sdef->elements[sdef->nElements-1].name
    );
  }
  
  if (want_namespaces) {
    /* there were some attributes. close the start tag left open */
    src = mputprintf(src,
      "  if (delay_close && (!omit_tag || chopped_chars)) p_buf.put_s(1%s, (cbyte*)\">\\n\");\n", /* close the start tag */
      (sdef->xerUntaggedOne /*|| sdef->xerUseNil*/) ? "" : "+is_indented"
    );
  }

  if (sdef->xerEmbedValuesPossible) {
    src = mputprintf(src,
      "  ec_1.set_msg(\"%s': \");\n"
      "  if (e_xer && (p_td.xer_bits & EMBED_VALUES)) {\n"
    /* write the first string (must come AFTER the attributes) */
      "    if (%s%s%s field_%s%s.size_of() > 0) {\n"
      "      sub_len += field_%s%s[0].XER_encode(UNIVERSAL_CHARSTRING_xer_, p_buf, p_flavor | EMBED_VALUES, p_flavor2, p_indent+1, 0);\n"
      "    }\n"
      "  }\n"
      , sdef->elements[0].dispname
      , sdef->elements[0].isOptional ? "field_" : ""
      , sdef->elements[0].isOptional ? sdef->elements[0].name : ""
      , sdef->elements[0].isOptional ? ".ispresent() &&" : ""
      , sdef->elements[0].name
      , sdef->elements[0].isOptional ? "()" : "", sdef->elements[0].name
      , sdef->elements[0].isOptional ? "()" : "");
    if (want_namespaces) { /* here's another chance */
      src = mputprintf(src,
        "  else if ( !(p_td.xer_bits & EMBED_VALUES)) {\n"
        "    %sfield_%s.XER_encode(%s_xer_, p_buf, p_flavor, p_flavor2, p_indent+1, 0);\n"
        "  }\n"
        , ((sdef->elements[0].xerAnyKind & ANY_ATTRIB_BIT) ? "" : "sub_len += " )
        , sdef->elements[0].name, sdef->elements[0].typegen
      );
    }
  }

  /* Then, all the non-attributes. Structuring the code like this depends on
   * all attributes appearing before all non-attributes (excluding
   * special members for EMBED-VALUES, USE-ORDER, etc.) */
  if (sdef->xerEmbedValuesPossible) {
    src = mputprintf(src,
      "  embed_values_enc_struct_t* emb_val = 0;\n"
      "  if (e_xer && (p_td.xer_bits & EMBED_VALUES) && "
      "    %s%s%s field_%s%s.size_of() > 1) {\n"
      "    emb_val = new embed_values_enc_struct_t;\n"
      /* If the first field is a record of ANY-ELEMENTs, then it won't be a pre-generated
       * record of universal charstring, so it needs a cast to avoid a compilation error */
      "    emb_val->embval_array%s = (const PreGenRecordOf::PREGEN__RECORD__OF__UNIVERSAL__CHARSTRING%s*)&field_%s%s;\n"
      "    emb_val->embval_array%s = NULL;\n"
      "    emb_val->embval_index = 1;\n"
      "  }\n"
      , sdef->elements[0].isOptional ? "field_" : ""
      , sdef->elements[0].isOptional ? sdef->elements[0].name : ""
      , sdef->elements[0].isOptional ? ".ispresent() &&" : ""
      , sdef->elements[0].name
      , sdef->elements[0].isOptional ? "()" : ""
      , sdef->elements[0].optimizedMemAlloc ? "_opt" : "_reg"
      , sdef->elements[0].optimizedMemAlloc ? "__OPTIMIZED" : ""
      , sdef->elements[0].name
      , sdef->elements[0].isOptional ? "()" : ""
      , sdef->elements[0].optimizedMemAlloc ? "_reg" : "_opt");
  }
  
  if (sdef->xerUseOrderPossible) {
    int max_ordered = sdef->nElements - start_at - num_attributes;
    int min_ordered = max_ordered - n_opt_elements;
    size_t offset = 0;
    size_t base   = i;
    size_t limit  = sdef->nElements;
    /* base and limit are indexes into sdef->elements[] */
    src = mputprintf(src,
      "  int dis_order = e_xer && (p_td.xer_bits & USE_ORDER);\n"
      "  int to_send = field_%s.lengthof();\n"
      "  int uo_limit = dis_order ? to_send : %lu;\n"
      , sdef->elements[start_at-1].name
      , (unsigned long)(limit - base)
    );
    if (sdef->xerUseNilPossible) { /* USE-NIL on top of USE-ORDER */
      base  = sdef->nElements;
      limit = sdef->totalElements;
      min_ordered = max_ordered = limit - base;
      src = mputprintf(src,
        "  if (!nil_attribute) {\n"
        "%s"
        "  if (!e_xer) sub_len += field_%s.XER_encode(%s_xer_, p_buf, p_flavor, p_flavor2, p_indent+!omit_tag, 0);\n"
        "  else" /* no newline */
        , (sdef->xerUseNilPossible ? "  if (!(p_td.xer_bits & USE_ORDER)) p_flavor |= (p_td.xer_bits & USE_NIL);\n" : "")
        /* If USE-ORDER is on, the tag-removing effect of USE-NIL has been
         * performed by calling the sub-fields directly. */
        , sdef->elements[sdef->nElements-1].name
        , sdef->elements[sdef->nElements-1].typegen
      );
    }

    /* check incorrect data */
    src = mputprintf(src,
      "  {\n"
      "  if (to_send < %d || to_send > %d) {\n"
      "    ec_1.set_msg(\"%s': \");\n"
      "    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_CONSTRAINT, "
      "\"Wrong number of USE-ORDER, %%d instead of %d..%d\", to_send);\n"
      "    uo_limit = -1;\n" /* squash the loop */
      "  }\n"
      "  else {\n" /* check duplicates */
      "    int *seen = new int [to_send];\n"
      "    int num_seen = 0;\n"
      "    for (int ei = 0; ei < to_send; ++ei) {\n"
      "      int val = field_%s[ei];\n"
      "      for (int x = 0; x < num_seen; ++x) {\n"
      "        if (val == seen[x]) { // complain\n"
      "          ec_1.set_msg(\"%s': \");\n"
      "          TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_CONSTRAINT,\n"
      "            \"Duplicate value for USE-ORDER\");\n"
      "          uo_limit = -1; // don't bother sending anything\n"
      "          goto trouble;\n"
      "        }\n"
      "      }\n"
      "      seen[num_seen++] = val;\n"
      "    }\n"
      "trouble:\n"
      "    delete [] seen;"
      "  }\n"
      "  "
      , min_ordered, max_ordered
      , sdef->elements[start_at-1].dispname
      , min_ordered, max_ordered
      , sdef->elements[start_at-1].name
      , sdef->elements[start_at-1].dispname
    );

    src = mputprintf(src,
      "  for (int i = 0; i < uo_limit; ++i) {\n"
      "    switch (dis_order ? (int)field_%s[i] : i) {\n"
      , sdef->elements[start_at-1].name
    );
    for ( i = base; i < limit; ++i ) {
      src = mputprintf(src,
        "    case %lu:\n"
        "      ec_1.set_msg(\"%s': \");\n"
        "      sub_len += field_%s%s%s%s.XER_encode(%s_xer_, p_buf, p_flavor, p_flavor2, p_indent+!omit_tag, %s);\n"

        , (unsigned long)offset++
        , sdef->elements[i].dispname
        , (sdef->xerUseNilPossible ? sdef->elements[sdef->nElements-1].name : sdef->elements[i].name)
        , (sdef->xerUseNilPossible ? "()." : "")
        , (sdef->xerUseNilPossible ? sdef->elements[i].name : "")
        , (sdef->xerUseNilPossible ? "()" : "")
        , sdef->elements[i].typegen
        , sdef->xerEmbedValuesPossible ? "emb_val" : "0"
      );

      src = mputstr(src, "    break;\n");
    } /* next element */
    src = mputstr(src,
      "    default:\n"
      "      TTCN_error(\"Runaway value while encoding USE-ORDER\");\n"
      "      break;\n" /* cannot happen, complain */
      "    }\n");

    if (sdef->xerEmbedValuesPossible) {
      src = mputprintf(src,
        "    if (e_xer && (p_td.xer_bits & EMBED_VALUES) && 0 != emb_val &&\n"
        "        %s%s%s emb_val->embval_index < field_%s%s.size_of()) { // embed-val\n"
        "      field_%s%s[emb_val->embval_index].XER_encode(\n"
        "        UNIVERSAL_CHARSTRING_xer_, p_buf, p_flavor | EMBED_VALUES, p_flavor2, p_indent+1, 0);\n"
        "      ++emb_val->embval_index;\n"
        "    }\n"
        , sdef->elements[0].isOptional ? "field_" : ""
        , sdef->elements[0].isOptional ? sdef->elements[0].name : ""
        , sdef->elements[0].isOptional ? ".ispresent() &&" : ""
        , sdef->elements[0].name
        , sdef->elements[0].isOptional ? "()" : "", sdef->elements[0].name
        , sdef->elements[0].isOptional ? "()" : "");
    }


    src = mputstr(src,
      "  }\n" /* end of loop */
      "  } // exer\n");
    if (sdef->xerUseNilPossible) {
      src = mputstr(src, "  } // nil_attr\n");
    }
  }
  else /* not USE-ORDER */
    for ( /* continue with i */; i < sdef->nElements; ++i ) {
      src = mputprintf(src,
        "  ec_1.set_msg(\"%s': \");\n"
        , sdef->elements[i].dispname
      );
      if (i > start_at + num_attributes) { // First field's embed value is already processed
        src = mputprintf(src, 
          "if (e_xer && ((p_td.xer_bits & UNTAGGED) && !(p_td.xer_bits & EMBED_VALUES) && 0 != emb_val_parent\n"
          "  %s%s%s)) {\n"
          "  if (0 != emb_val_parent->embval_array_reg) {\n"
          "    if (emb_val_parent->embval_index < emb_val_parent->embval_array_reg->size_of()) {\n"
          "      (*emb_val_parent->embval_array_reg)[emb_val_parent->embval_index].XER_encode(UNIVERSAL_CHARSTRING_xer_"
          "      , p_buf, p_flavor | EMBED_VALUES, p_flavor2, p_indent+1, 0);\n"
          "      ++emb_val_parent->embval_index;\n"
          "    }\n"
          "  } else {\n"
           "   if (emb_val_parent->embval_index < emb_val_parent->embval_array_opt->size_of()) {\n"
          "      (*emb_val_parent->embval_array_opt)[emb_val_parent->embval_index].XER_encode(UNIVERSAL_CHARSTRING_xer_"
          "      , p_buf, p_flavor | EMBED_VALUES, p_flavor2, p_indent+1, 0);\n"
          "      ++emb_val_parent->embval_index;\n"
          "    }\n"
          "  }\n"
          "}\n"
          , sdef->elements[0].isOptional ? "&& field_" : ""
          , sdef->elements[0].isOptional ? sdef->elements[0].name : ""
          , sdef->elements[0].isOptional ? ".ispresent()" : ""
        );
      }
      src = mputprintf(src,
        "  sub_len += field_%s.XER_encode(%s_xer_, p_buf, p_flavor%s, p_flavor2, p_indent+!omit_tag, %s);\n"
        , sdef->elements[i].name, sdef->elements[i].typegen
        , sdef->xerUseNilPossible ? "| (p_td.xer_bits & USE_NIL)" : ""
        , sdef->xerEmbedValuesPossible ? "emb_val" : "0"
      );

      if (sdef->xerEmbedValuesPossible) {
        src = mputprintf(src,
          "  if (e_xer && (p_td.xer_bits & EMBED_VALUES) && 0 != emb_val &&\n"
          "      %s%s%s %s%s%s emb_val->embval_index < field_%s%s.size_of()) {\n"
          "    field_%s%s[emb_val->embval_index].XER_encode(\n"
          "      UNIVERSAL_CHARSTRING_xer_, p_buf, p_flavor | EMBED_VALUES, p_flavor2, p_indent+1, 0);\n"
          "    ++emb_val->embval_index;\n"
          "  }\n"
          , sdef->elements[0].isOptional ? "field_" : ""
          , sdef->elements[0].isOptional ? sdef->elements[0].name : ""
          , sdef->elements[0].isOptional ? ".ispresent() &&" : ""
          , sdef->elements[i].isOptional ? "field_" : ""
          , sdef->elements[i].isOptional ? sdef->elements[i].name : ""
          , sdef->elements[i].isOptional ? ".ispresent() &&" : ""
          , sdef->elements[0].name
          , sdef->elements[0].isOptional ? "()" : "", sdef->elements[0].name
          , sdef->elements[0].isOptional ? "()" : "");
      }
    } /* next field when not USE-ORDER */
  
  if (i <= start_at + num_attributes + 1 || sdef->xerUseOrderPossible) {
    src = mputprintf(src, "  (void)emb_val_parent;\n"); // Silence unused warning
  }
  
  if (sdef->xerEmbedValuesPossible) {
    src = mputprintf(src,
      "  if (0 != emb_val) {\n"
      "    if (%s%s%s emb_val->embval_index < field_%s%s.size_of()) {\n"
      "      ec_1.set_msg(\"%s': \");\n"
      "      TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_CONSTRAINT,\n"
      "        \"Too many EMBED-VALUEs specified: %%d (expected %%d or less)\",\n"
      "        field_%s%s.size_of(), emb_val->embval_index);\n"
      "    }\n"
      "    delete emb_val;\n"
      "  }\n"
      , sdef->elements[0].isOptional ? "field_" : ""
      , sdef->elements[0].isOptional ? sdef->elements[0].name : ""
      , sdef->elements[0].isOptional ? ".ispresent() &&" : ""
      , sdef->elements[0].name, sdef->elements[0].isOptional ? "()" : ""
      , sdef->elements[0].name, sdef->elements[0].name
      , sdef->elements[0].isOptional ? "()" : "");
  }

  src = mputstr(src, "  } // QN?\n");

  {
    src = mputprintf(src,
      "  if (!omit_tag) {\n"
      "  if (sub_len) {\n" /* something was written, now an end tag */
      "    if (is_indented && !(e_xer && (p_td.xer_bits & (HAS_1UNTAGGED | USE_QNAME)))) {\n"
      "      switch ((int)(e_xer && (p_td.xer_bits & USE_NIL))) {\n"
      "      case 1: {\n"
      "        const unsigned char *buf_end = p_buf.get_data() + (p_buf.get_len()-1);\n"
      "        if (buf_end[-1] != '>' || *buf_end != '\\n') break;\n"
      /*       else fall through */
      "      }\n"
      "      case 0:\n"
      "        do_indent(p_buf, p_indent);\n"
      "        break;\n"
      "      }\n"
      "    }\n"
      "    p_buf.put_c('<');\n"
      "    p_buf.put_c('/');\n"
      "    if (e_xer) write_ns_prefix(p_td, p_buf);\n"
      "    p_buf.put_s((size_t)p_td.namelens[e_xer]-!is_indented, (cbyte*)p_td.names[e_xer]);\n"
      "  } else {\n" /* need to generate an empty element tag */
      "    p_buf.increase_length(%s-1);\n" /* decrease length */
      "    p_buf.put_s((size_t)2+is_indented, (cbyte*)\"/>\\n\");\n"
      "  }}\n"
      , (sdef->xerUntaggedOne /*|| sdef->xerUseNil*/) ? "" : "-is_indented"
    );
  }
  src = mputstr(src,
    "  return (int)p_buf.get_len() - encoded_length;\n"
    "}\n\n");

#ifndef NDEBUG
  src = mputprintf(src, "// %s has%s%s%s%s%s%s%s%s%s\n"
    "// written by %s in " __FILE__ " at %d\n"
    , name
    , (sdef->xerUntagged            ? " UNTAGGED" : "")
    , (sdef->xerUntaggedOne         ? "1" : "")
    , (sdef->xerUseNilPossible      ? " USE_NIL?" : "")
    , (sdef->xerUseOrderPossible    ? " USE_ORDER?" : "")
    , (sdef->xerUseQName            ? " USE_QNAME" : "")
    , (sdef->xerUseTypeAttr         ? " USE_TYPE_ATTR" : "")
    , (sdef->xerUseUnion            ? " USE-UNION" : "")
    , (sdef->xerHasNamespaces       ? " namespace" : "")
    , (sdef->xerEmbedValuesPossible ? " EMBED?" : "")
    , __FUNCTION__, __LINE__
  );
#endif

  src = mputprintf(src, /* XERSTUFF decodegen for record/SEQUENCE*/
    "int %s::XER_decode(const XERdescriptor_t& p_td, XmlReaderWrap& p_reader,"
    " unsigned int p_flavor, unsigned int p_flavor2, embed_values_dec_struct_t* emb_val_parent)\n"
    "{\n"
    /* Remove XER_LIST, XER_RECOF from p_flavor. This is not required
     * for is_exer (which tests another bit), but for subsequent code. */
    "  boolean e_xer = is_exer(p_flavor);\n"
    "  unsigned long xerbits = p_td.xer_bits;\n"
    "  if (p_flavor & XER_TOPLEVEL) xerbits &= ~UNTAGGED;\n"
    "  const boolean omit_tag = e_xer && ((xerbits & (UNTAGGED|XER_ATTRIBUTE)) "
    "|| (p_flavor & (USE_NIL|USE_TYPE_ATTR)));\n"
    "%s"
    "  const boolean parent_tag = e_xer && ((p_flavor & USE_TYPE_ATTR)|| (p_flavor2 & USE_NIL_PARENT_TAG));\n"
    "  (void)parent_tag;\n"
    "  p_flavor &= XER_MASK;\n" /* also removes "toplevel" bit */
    "  p_flavor2 = XER_NONE;\n" /* Remove only bit: USE_NIL_PARENT_TAG (for now) */
    "  int rd_ok, xml_depth=-1, type;\n"
    "  {\n" /* scope for the error contexts */
    , name
    , (start_at + num_attributes < sdef->nElements || (sdef->xerEmbedValuesPossible && num_attributes==0)
      ? "  boolean tag_closed = (p_flavor & PARENT_CLOSED) != 0;\n" : "")
    /* tag_closed needed for non-attribute normal members only,
     * or if EMBED-VALUES is possible, but isn't. */
  );

  if (sdef->xerUseNilPossible) { src = mputstr(src,
    "  boolean nil_attribute = FALSE;\n"
    "  boolean already_processed = FALSE;\n");
  }

  src = mputprintf(src,
    "  TTCN_EncDec_ErrorContext ec_0(\"Component '\");\n"
    "  TTCN_EncDec_ErrorContext ec_1;\n"
    "  if (!omit_tag) for (rd_ok=p_reader.Ok(); rd_ok==1; rd_ok=p_reader.Read()) {\n"
    "    type = p_reader.NodeType();\n"
    "    if (type==XML_READER_TYPE_ELEMENT) {\n"
    "      verify_name(p_reader, p_td, e_xer);\n"
    "      xml_depth = p_reader.Depth();\n"
    "%s"
    "      break;\n"
    "    }\n"
    "  }\n"
    , (start_at + num_attributes < sdef->nElements || (sdef->xerEmbedValuesPossible && num_attributes==0)
      ?  "      tag_closed = p_reader.IsEmptyElement();\n": "")
  );


  if (sdef->xerUseQName) {
    src = mputprintf(src,
      "  if (e_xer && (p_td.xer_bits & USE_QNAME)) {\n"
      "    if (p_td.xer_bits & XER_ATTRIBUTE) rd_ok = 1;\n"
      "    else for (rd_ok = p_reader.Read(); rd_ok == 1; rd_ok = p_reader.Read()) {\n"
      "      type = p_reader.NodeType();\n"
      "      if (type == XML_READER_TYPE_TEXT) break;\n"
      "    }\n"
      "    if (rd_ok==1) {\n"
      "      xmlChar *new_val = p_reader.NewValue();\n"
      "      xmlChar *v_npfx = (xmlChar*)strchr((char*)new_val, ':');\n"
      "      xmlChar *v_pfx;\n"
      "      if (v_npfx != NULL) {\n"
      "        *v_npfx++ = '\\0';\n"
      "        v_pfx = new_val;\n"
      "      }\n"
      "      else {\n"
      "        v_npfx = new_val;\n"
      "        v_pfx  = NULL;\n"
      "      }\n"
      "      xmlChar *q_ns = p_reader.LookupNamespace(v_pfx);\n"
      "      if (q_ns) field_%s = (const char*)q_ns;\n"
      "      else      field_%s = OMIT_VALUE;\n"
      "      field_%s = (const char*)v_npfx;\n"
      "      xmlFree(q_ns);\n"
      "      xmlFree(new_val);\n"
      "    }\n"
      "  } else {\n"
      , sdef->elements[0].name
      , sdef->elements[0].name
      , sdef->elements[1].name
    );
  } /* QName */

  if (num_attributes || sdef->xerEmbedValuesPossible || sdef->xerUseOrderPossible) {
    src = mputstr(src, "if (e_xer) {\n");
  }

  /* ********************************************************************
   * ATTRIBUTES
   ***************************/ 
  if (num_attributes || sdef->xerUseNilPossible /* maybe QNAME too ? */) {
    size_t aaa;

     /* Prepare for attributes not present in the XML.
     * Set all attributes with defaultForEmpty to the D-F-E value.
     * Set all optional components with ATTRIBUTE to omit.
     *
     * If EMBED-VALUES is possible, the first component can't be optional.
     * The same is true for the USE-ORDER component. Hence start_at. */
    for (aaa = start_at; aaa < start_at + num_attributes; ++aaa) {
      if (sdef->elements[aaa].xerAttribute) { /* "proper" ATTRIBUTE */
        src = mputprintf(src,
          "  if (%s_xer_.dfeValue) field_%s = "
          "*static_cast<const %s*>(%s_xer_.dfeValue);\n"
          , sdef->elements[aaa].typegen, sdef->elements[aaa].name
          , sdef->elements[aaa].type   , sdef->elements[aaa].typegen);
        if (sdef->elements[aaa].isOptional) src = mputprintf(src,
          "  else field_%s = OMIT_VALUE;\n", sdef->elements[aaa].name);
      }
      else { /* must be the ANY-ATTRIBUTES */
        src = mputprintf(src,
          "  field_%s%s;\n"
        , sdef->elements[aaa].name
        , sdef->elements[aaa].isOptional ? " = OMIT_VALUE" : ".set_size(0)");
      }
    }
    
    src = mputstr(src, " if (!omit_tag || parent_tag) {\n");

    if (num_attributes==0 /* therefore sdef->xerUseNilPossible is true */ ) {
      /* Only the "nil" attribute may be present. If there is no USE-NIL,
       * then there can be no attributes at all. */
      src = mputstr(src,
        "  if (e_xer && (p_td.xer_bits & (USE_NIL|USE_TYPE_ATTR))) {\n");
    }

    if (aa_index > -1) src = mputstr(src, "  size_t num_aa = 0;\n");
    if (sdef->xerUseNilPossible) {
      src = mputstr(src,
        "  static const namespace_t *control_ns = p_td.my_module->get_controlns();\n");
    }

    src = mputstr(src,
      "  if(parent_tag && p_reader.NodeType() == XML_READER_TYPE_ATTRIBUTE) {\n"
      "    rd_ok = p_reader.Ok();\n"
      "  } else {\n"
      "    rd_ok = p_reader.MoveToFirstAttribute();\n"
      "  }\n"
      "  for (; rd_ok==1 && "
      "p_reader.NodeType()==XML_READER_TYPE_ATTRIBUTE; "
      "rd_ok = p_reader.AdvanceAttribute()) {\n"
      "    if (p_reader.IsNamespaceDecl()) continue;\n");
    /* if the only attribute is ANY-ATTRIBUTE, it doesn't need attr_name */
    if (num_attributes==1 && aa_index!=-1 /*&& !sdef->xerUseNilPossible*/) {}
    else {
      src = mputstr(src,
        "    const char *attr_name = (const char*)p_reader.LocalName();\n"
        "    const char *ns_uri    = (const char*)p_reader.NamespaceUri();\n");
    }

    if (sdef->xerUseNilPossible) {
      src = mputprintf(src,
        "    const char *prefix = (const char*)p_reader.Prefix();\n"
        /* prefix may be NULL, control_ns->px is never NULL or empty */
        "    if (prefix && !strcmp(prefix, control_ns->px)\n"
        "      && !strcmp((const char*)p_reader.LocalName(), \"nil\")){\n"
        "      const char *value = (const char*)p_reader.Value();\n"
        "      if (!strcmp(value, \"1\") || !strcmp(value, \"true\")) {\n"
        "        field_%s = OMIT_VALUE;\n"
        "        nil_attribute = TRUE;\n"
        /* found the "nil" attribute */
        "      }\n"
        "    } else"
        , sdef->elements[sdef->nElements-1].name);
    }

    for (i = start_at; i < start_at + num_attributes; ++i) {
      if (i == aa_index) continue; /* ANY_ATTR. is handled below */
      src = mputprintf(src,
        "    if (check_name(attr_name, %s_xer_, 1) && check_namespace(ns_uri, %s_xer_)) {\n"
        "      ec_1.set_msg(\"%s': \");\n"
        "      field_%s.XER_decode(%s_xer_, p_reader, p_flavor | (p_td.xer_bits & USE_NIL), p_flavor2, 0);\n"
        "    } else"
        , sdef->elements[i].typegen, sdef->elements[i].typegen
        , sdef->elements[i].dispname /* set_msg */
        , sdef->elements[i].name, sdef->elements[i].typegen
      );
    }
    
    if(sdef->xerUseNilPossible) {
      src = mputprintf(src,
        "    if(p_td.xer_bits & USE_NIL) {\n"
        "      field_%s.XER_decode(%s_xer_, p_reader, p_flavor | USE_NIL, p_flavor2 | USE_NIL_PARENT_TAG, 0);\n"
        "      already_processed = TRUE;\n"
        "      break;\n"
        "    } else"
        , sdef->elements[sdef->nElements-1].name
        , sdef->elements[sdef->nElements-1].typegen
      );
    }

    if (sdef->control_ns_prefix && !(num_attributes==1 && aa_index!=-1)) {
      src = mputprintf(src,
        "    if (/*parent_tag &&*/ !strcmp(attr_name, \"type\") "
        "&& !strcmp((const char*)p_reader.Prefix(), \"%s\")) {} else\n"
        , sdef->control_ns_prefix);
      /* xsi:type; already processed by parent with USE-UNION or USE-TYPE */
    }


    if (aa_index >= 0) {
      /* we are at a dangling else */
      src = mputprintf(src,
        "    {\n"
        "      TTCN_EncDec_ErrorContext ec_0(\"Attribute %%d: \", (int)num_aa);\n"
        "      UNIVERSAL_CHARSTRING& new_elem = field_%s%s[num_aa++];\n"
        /* Construct the AnyAttributeFormat (X.693amd1, 18.2.6) */
        "      TTCN_Buffer aabuf;\n"
        "      const xmlChar *x_name = p_reader.LocalName();\n"
        "      const xmlChar *x_val  = p_reader.Value();\n"
        "      const xmlChar *x_uri  = p_reader.NamespaceUri();\n"
        "      if (%s_xer_.xer_bits & (ANY_FROM | ANY_EXCEPT)) {\n"
        "        check_namespace_restrictions(%s_xer_, (const char*)x_uri);\n"
        "      }\n"
        /* We don't care about p_reader.Prefix() */
        /* Using strlen to count bytes */
        "      aabuf.put_s(x_uri ? strlen((const char*)x_uri) : 0, x_uri);\n"
        "      if (x_uri && *x_uri) aabuf.put_c(' ');\n"
        "      aabuf.put_s(x_name ? strlen((const char*)x_name) : 0, x_name);\n"
        "      aabuf.put_c('=');\n"
        "      aabuf.put_c('\"');\n"
        "      aabuf.put_s(x_val ? strlen((const char*)x_val) : 0, x_val);\n"
        "      aabuf.put_c('\"');\n"
        "      new_elem.decode_utf8(aabuf.get_len(), aabuf.get_data());\n"
        "    } \n"
        , sdef->elements[aa_index].name
        , sdef->elements[aa_index].isOptional ? "()" : ""
        , sdef->elements[aa_index].typegen, sdef->elements[aa_index].typegen
      );
    }
    else {
      /* we are at a dangling else */
      src = mputstr(src, "    {\n");
      if (sdef->control_ns_prefix) {
        src = mputprintf(src,
          // Lastly check for the xsi:schemaLocation attribute, this does not
          // affect TTCN-3, but it shouldn't cause a DTE
          "      if (!p_reader.LocalName() || strcmp((const char*)p_reader.LocalName(), \"schemaLocation\") ||\n"
          "          !p_reader.Prefix() || strcmp((const char*)p_reader.Prefix(), \"%s\"))\n"
          , sdef->control_ns_prefix);
      }
      src = mputstr(src,
        "      {\n"
        "        ec_0.set_msg(\" \"); ec_1.set_msg(\" \");\n" /* we have no component */
        "        TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_INVAL_MSG, "
        "\"Unexpected attribute '%s', ns '%s'\", attr_name, ns_uri ? ns_uri : \"\");\n"
        "      }\n"
        "    }\n");
    }
    src = mputstr(src, "  }\n"); /* for */

    for (i = start_at; i < start_at + num_attributes; ++i) {
      if (sdef->elements[i].isOptional) continue; /* allowed to be missing */
      src = mputprintf(src, "  if (!field_%s.is_bound()) "
        "TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_INVAL_MSG, "
        "\"Missing attribute '%s'\");\n"
        , sdef->elements[i].name, sdef->elements[i].dispname);
    }

    if (num_attributes==0) src = mputstr(src,  "  }\n"); /* if USE_NIL or USE_TYPE_ATTR */
    src = mputstr(src,  " }\n"); /* process (attributes) in (own or parent) tag */
  } /* * * * * * * * * end if(attributes...) * * * * * * * * * * * * */

  src = mputprintf(src,
    "  if ((!omit_tag%s) && !p_reader.IsEmptyElement()%s) "
    "rd_ok = p_reader.Read();\n"
    , num_attributes > 0 ? " || parent_tag" : ""
    , sdef->xerUseNilPossible ? " && !already_processed" : "");

  if (sdef->xerEmbedValuesPossible && num_attributes==0) {
    /* EMBED-VALUES possible, but isn't: the first component is a non-special
     * record of strings. This means there are no attributes, unless
     * the first component is an ATTRIBUTE or ANY-ATTRIBUTES. */
    src = mputprintf(src,
      "  if (!(p_td.xer_bits & EMBED_VALUES)) {\n"
      "    ec_1.set_msg(\"%s': \");\n"
      "    field_%s.XER_decode(%s_xer_, p_reader, "
      "p_flavor | (p_td.xer_bits & USE_NIL)| (tag_closed ? PARENT_CLOSED : XER_NONE), p_flavor2, 0);\n"
      "  }\n"
      , sdef->elements[0].dispname
      , sdef->elements[0].name, sdef->elements[0].typegen
    );
  }

  if (num_attributes || sdef->xerEmbedValuesPossible || sdef->xerUseOrderPossible) {
    src = mputstr(src,
      "} else {\n" /* now the non-EXER processing of (would-be) attributes */
      "  if (!p_reader.IsEmptyElement()) p_reader.Read();\n"
    );
  }

  if (sdef->xerUseOrderPossible) {
    src = mputstr(src,
      "  if (e_xer && p_td.xer_bits & USE_ORDER) ; else {");
  }

  if (start_at + num_attributes > 0)
  {
    /* No attributes nor USE-NIL:
     * Just decode the specials and would-be attributes. */

    // FIXME: WTF is this?
    //if (sdef->xerEmbedValuesPossible) {
    //  /* Only decode the first member if it really is EMBED-VALUES */
    //  src = mputprintf(src, "if (p_td.xer_bits & EMBED_VALUES) ");
    //}
    for (i = 0; i < start_at + num_attributes; ++i) {
      /* Decode the field */
      src = mputprintf(src,
        "  {\n"
        "    ec_1.set_msg(\"%s': \");\n"
        "    field_%s.XER_decode(%s_xer_, p_reader, p_flavor | (p_td.xer_bits & USE_NIL), p_flavor2, 0);\n"
        "  }\n"
        , sdef->elements[i].dispname
        , sdef->elements[i].name, sdef->elements[i].typegen
      );
    } /* next field */

    if (sdef->xerUseOrderPossible) {
      src = mputstr(src, "  }\n");
    }
  }

  if (num_attributes || sdef->xerEmbedValuesPossible || sdef->xerUseOrderPossible) {
    src = mputstr(src, "}\n");
  }
  
  if (sdef->xerEmbedValuesPossible) {
    src = mputprintf(src,
      "  embed_values_dec_struct_t* emb_val = 0;\n"
      "  if (e_xer && (p_td.xer_bits & EMBED_VALUES)) {\n"
      "    emb_val = new embed_values_dec_struct_t;\n"
      /* If the first field is a record of ANY-ELEMENTs, then it won't be a pre-generated
       * record of universal charstring, so it needs a cast to avoid a compilation error */
      "    emb_val->embval_array%s = (PreGenRecordOf::PREGEN__RECORD__OF__UNIVERSAL__CHARSTRING%s*)&field_%s%s;\n"
      "    emb_val->embval_array%s = NULL;\n"
      "    emb_val->embval_index = 0;\n"
      "    field_%s%s.set_size(0);\n"
      "  }\n"
      , sdef->elements[0].optimizedMemAlloc ? "_opt" : "_reg"
      , sdef->elements[0].optimizedMemAlloc ? "__OPTIMIZED" : ""
      , sdef->elements[0].name
      , sdef->elements[0].isOptional ? "()" : ""
      , sdef->elements[0].optimizedMemAlloc ? "_reg" : "_opt"
      , sdef->elements[0].name
      , sdef->elements[0].isOptional ? "()" : "");
  }

  if (sdef->xerUseOrderPossible) {
    size_t begin = start_at + num_attributes; /* first non-attribute */
    size_t end   = sdef->nElements;
    size_t n_optionals = 0, n_embed;
    int max_ordered = sdef->nElements - start_at - num_attributes;
    int min_ordered = max_ordered - n_opt_elements;
    if (sdef->xerUseNilPossible) { /* USE-NIL on top of USE-ORDER */
      begin = sdef->nElements;
      end   = sdef->totalElements;
    }
    n_embed = end - begin;

    src = mputstr(src,
      "  if (e_xer && (p_td.xer_bits & USE_ORDER)) {\n"
    );
    for (i = begin; i < end; ++i) {
      if (sdef->elements[i].isOptional) {
        src = mputprintf(src, "    field_%s = OMIT_VALUE;\n",
          sdef->elements[i].name);
        ++n_optionals;
      }
    }

    if (sdef->xerUseNilPossible) { /* USE-NIL and USE-ORDER */
      src = mputprintf(src,
        "    if (nil_attribute) field_%s.set_size(0);\n    else"
        , sdef->elements[uo].name);
    }

    src = mputprintf(src,
      "    {\n"
      "    field_%s.set_size(0);\n"
      "    if (!tag_closed) {\n" /* Nothing to order if there are no child elements */
      "    int e_val, num_seen = 0, *seen_f = new int[%lu];\n"
      , sdef->elements[uo].name
      , (unsigned long)(n_embed)
    );
    if (sdef->xerEmbedValuesPossible) {
      // The index of the latest embedded value can change outside of this function
      // (if the field is a untagged record of), in this case the next value should
      // be ignored, as it's already been handled by the record of
      src = mputstr(src, "    int last_embval_index = 0;\n");
    }
    src = mputprintf(src,
      "    boolean early_exit = FALSE;\n"
      "    for (int i=0; i < %lu; ++i) {\n"
      "      for (rd_ok=p_reader.Ok(); rd_ok==1; rd_ok=p_reader.Read()) {\n"
      , (unsigned long)(n_embed));

    if (sdef->xerEmbedValuesPossible) {
      /* read and store embedValues text if present */
      src = mputprintf(src,
        "        if (0 != emb_val && p_reader.NodeType()==XML_READER_TYPE_TEXT) {\n"
        "          UNIVERSAL_CHARSTRING emb_ustr((const char*)p_reader.Value());\n"
        "          field_%s%s[emb_val->embval_index] = emb_ustr;\n"
        "        }\n"
        , sdef->elements[0].name
        , sdef->elements[0].isOptional ? "()" : "");
    }

    src = mputstr(src,
      "        type = p_reader.NodeType();\n"
      "        if (type==XML_READER_TYPE_ELEMENT) break;\n"
      "        if (type == XML_READER_TYPE_END_ELEMENT) {\n"
      "          early_exit = TRUE;\n"
      "          break;\n"
      "        }\n"
      "      }\n"
      "      if (rd_ok != 1 || early_exit) break;\n"
      "      const char * x_name = (const char*)p_reader.LocalName();\n" /* Name or LocalName ? */);
    
    if (sdef->xerEmbedValuesPossible) {
      src = mputstr(src,
        "      if (0 != emb_val) {\n"
        "        if (last_embval_index == emb_val->embval_index) {\n"
        "          ++emb_val->embval_index;\n"
        "        }\n"
        "        last_embval_index = emb_val->embval_index;\n"
        "      }\n");
    }

    /* * * * * code for USE-ORDER * * * * */

    for (i = begin; i < end; ++i) {
      // Check non-anyElement fields first
      if (!(sdef->elements[i].xerAnyKind & ANY_ELEM_BIT)) {
        src = mputprintf(src,
          "      if (check_name(x_name, %s_xer_, 1)) {\n"
          "        ec_1.set_msg(\"%s': \");\n"
          "        field_%s%s%s%s.XER_decode(%s_xer_, p_reader, p_flavor, p_flavor2, %s);\n"
          , sdef->elements[i].typegen
          , sdef->elements[i].dispname
          , (sdef->xerUseNilPossible ? sdef->elements[sdef->nElements-1].name: sdef->elements[i].name)
          , (sdef->xerUseNilPossible ? "()." : "")
          , (sdef->xerUseNilPossible ? sdef->elements[i].name : "")
          , (sdef->xerUseNilPossible ? "()" : "")
          , sdef->elements[i].typegen
          , sdef->xerEmbedValuesPossible ? "emb_val" : "0"
        );
        src = mputprintf(src,
        "        field_%s[i] = e_val = %s::of_type::%s;\n"
        , sdef->elements[uo].name
        , sdef->elements[uo].typegen, sdef->elements[i].name);
        src = mputstr(src, "      }\n      else");
      }
    }
    src = mputstr(src, 
      " {\n"
      "        boolean any_found = FALSE;\n"
      "        if (!any_found)");
    for (i = begin; i < end; ++i) {
      // Check anyElement fields after all other fields
      if (sdef->elements[i].xerAnyKind & ANY_ELEM_BIT) {
        src = mputstr(src, " {\n");
        src = mputprintf(src,
          "          e_val = %s::of_type::%s;\n"
          , sdef->elements[uo].typegen, sdef->elements[i].name);
        src = mputprintf(src,
          "          boolean next_any = FALSE;\n"
          "          for (int d_f = 0; d_f < num_seen; ++d_f) {\n"
          "            if (e_val == seen_f[d_f]) {\n"
          "              next_any = TRUE;\n"
          "            }\n"
          "          }\n"
          "          if (!next_any) {\n"
          "            ec_1.set_msg(\"%s': \");\n"
          "            field_%s%s%s%s.XER_decode(%s_xer_, p_reader, p_flavor, p_flavor2, 0);\n"
          "            field_%s[i] = e_val;\n"
          "            any_found = TRUE;\n"
          "          }\n"
          "        }\n"
          "        if (!any_found)"
          , sdef->elements[i].dispname
          , (sdef->xerUseNilPossible ? sdef->elements[sdef->nElements-1].name: sdef->elements[i].name)
          , (sdef->xerUseNilPossible ? "()." : "")
          , (sdef->xerUseNilPossible ? sdef->elements[i].name : "")
          , (sdef->xerUseNilPossible ? "()" : "")
          , sdef->elements[i].typegen, sdef->elements[uo].name
        );
      }
    }

    src = mputstr(src,
      " {\n" /* take care of the dangling else */
      "          TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_INVAL_MSG,\n"
      "            \"Bad XML tag '%s' instead of a valid field\", x_name);\n"
      "          break;\n"
      "        }\n"
      "      }\n"
      "      for (int d_f = 0; d_f < num_seen; ++d_f)\n"
      "        if (e_val == seen_f[d_f])\n"
      "          TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_CONSTRAINT, \"Duplicate field\");\n"
      "      seen_f[num_seen++] = e_val;\n"
      "    } // next i\n");

    if (sdef->xerEmbedValuesPossible) {
      /* read and store embedValues text if present */
      src = mputprintf(src,
        "    if (0 != emb_val) {\n"
        "      if (p_reader.NodeType()==XML_READER_TYPE_TEXT) {\n"
        "        UNIVERSAL_CHARSTRING emb_ustr((const char*)p_reader.Value());\n"
        "        field_%s%s[emb_val->embval_index] = emb_ustr;\n"
        "      }\n"
        "      if (last_embval_index == emb_val->embval_index) {\n"
        "        ++emb_val->embval_index;\n"
        "      }\n"
        "    }\n"
        , sdef->elements[0].name
        , sdef->elements[0].isOptional ? "()" : "");
    }

    src = mputprintf(src,
      "    delete [] seen_f;\n"
      "    }\n"
      "    int n_collected = field_%s.size_of();\n"
      "    if (p_td.xer_bits & USE_NIL) {\n"
      "      ;\n"
      "    } else {\n"
      "      if (n_collected < %d || n_collected > %d) {\n"
      "        ec_0.set_msg(\" \");\n"
      "        TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_CONSTRAINT, \"Wrong number of elements\");\n"
      "      }\n"
      "    }\n"
      "  } // !tag_closed\n"
      "  } else { // !uo\n"
      , sdef->elements[uo].dispname
      , min_ordered, max_ordered
    );
  } /* end if(UseOrder possible) */

  /* * * * * non-UseOrder code always written, executed when * * * *
   * * * * * UseOrder not possible or in the "else" clause * * * * */

  if (sdef->xerUseNilPossible) {
    /* value absent, nothing more to do */
    src = mputstr(src, 
      "  if (nil_attribute) {\n"
      "    p_reader.MoveToElement();\n"
      "  } else {\n");
  }
  if (sdef->xerEmbedValuesPossible) {
    // The index of the latest embedded value can change outside of this function
    // (if the field is a untagged record of), in this case the next value should
    // be ignored, as it's already been handled by the record of
    // Omitted fields can also reset this value
    src = mputstr(src, "  int last_embval_index = 0;\n");
  }
  /* for all the non-attribute fields... */
  for (i = start_at + num_attributes; i < sdef->nElements; ++i) {
    if (sdef->xerEmbedValuesPossible) {
      /* read and store embedValues text if present */
      src = mputprintf(src,
        "  if (0 != emb_val) {\n"
        "    if (p_reader.NodeType()==XML_READER_TYPE_TEXT) {\n"
        "      UNIVERSAL_CHARSTRING emb_ustr((const char*)p_reader.Value());\n"
        "      field_%s%s[emb_val->embval_index] = emb_ustr;\n"
        "    }\n"
        "    if (last_embval_index == emb_val->embval_index) {\n"
        "      ++emb_val->embval_index;\n"
        "    }\n"
        "    last_embval_index = emb_val->embval_index;\n"
        "  }\n"
        , sdef->elements[0].name
        , sdef->elements[0].isOptional ? "()" : "");
    }
    /* The DEFAULT-FOR-EMPTY member can not be involved with EMBED-VALUES,
     * so we can use the same pattern: optional "if(...) else" before {}
     * XXX check with single-element record where nElements-1 == 0 !! */
    if (sdef->kind == RECORD && i == sdef->nElements-1) {
      src = mputprintf(src,
        "  if (e_xer && p_td.dfeValue && p_reader.IsEmptyElement()) {\n"
        "    field_%s = *static_cast<const %s*>(p_td.dfeValue);\n"
        "  }\n"
        "  else%s"
        , sdef->elements[i].name, sdef->elements[i].type
        , sdef->xerUseNilPossible ? " if (!already_processed)" : "");
    }
    /* Decode the field */
    src = mputprintf(src,
      "  {\n"
      "    ec_1.set_msg(\"%s': \");\n"
      , sdef->elements[i].dispname);
    
    src = mputprintf(src, 
      "  if ((p_td.xer_bits & UNTAGGED) && 0 != emb_val_parent) {\n"
      "    if (p_reader.NodeType() == XML_READER_TYPE_TEXT) {\n"
      "      UNIVERSAL_CHARSTRING emb_ustr((const char*)p_reader.Value());\n"
      "      if (0 != emb_val_parent->embval_array_reg) {\n"
      "        (*emb_val_parent->embval_array_reg)[emb_val_parent->embval_index] = emb_ustr;\n"
      "      } else {\n"
      "        (*emb_val_parent->embval_array_opt)[emb_val_parent->embval_index] = emb_ustr;\n"
      "      }\n"
      "      ++emb_val_parent->embval_index;\n"
      "    }\n"
      "  }\n");
    
    if ( (sdef->elements[i].xerAnyKind & ANY_ELEM_BIT)
         && !strcmp(sdef->elements[i].type,"UNIVERSAL_CHARSTRING") ) {
      // In case the field is an optional anyElement -> check if it should be omitted
      if (sdef->elements[i].isOptional) {
        src = mputprintf(src,
          "    if (field_%s.XER_check_any_elem(p_reader, \"%s\", tag_closed))\n  "
          , sdef->elements[i].name
          , (i == sdef->nElements - 1) ? "NULL" : sdef->elements[i + 1].name);
      } else {
        // If the record is emptyElement, there's no way it will have an anyElement field
        src = mputstr(src, "    if (tag_closed) p_reader.Read(); \n");
      }
    }
    
    src = mputprintf(src, 
      "    field_%s.XER_decode(%s_xer_, p_reader, p_flavor"
      " | (p_td.xer_bits & USE_NIL)| (tag_closed ? PARENT_CLOSED : XER_NONE), p_flavor2, %s);\n"
      "  }\n"
      , sdef->elements[i].name, sdef->elements[i].typegen
      , sdef->xerEmbedValuesPossible ? "emb_val" : "0");
    if (sdef->xerEmbedValuesPossible) {
      src = mputprintf(src,
        "  if (!field_%s.is_present()) {\n"
        // there was no new element, the last embedded value is for the next field
        // (or the end of the record if this is the last field) 
        "    last_embval_index = -1;\n"
        "  }\n"
        , sdef->elements[i].name);
    }
    if (!sdef->elements[i].isOptional) {
      src = mputprintf(src,
        "  if (field_%s.is_bound()) {\n"
        "    p_flavor &= ~XER_OPTIONAL;\n"
        "  }\n"
        , sdef->elements[i].name);
    }
  } /* next field */
  
  if (i == start_at + num_attributes) {
    src = mputprintf(src, "  (void)emb_val_parent;\n"); // Silence unused warning
  }
  
  if (sdef->xerEmbedValuesPossible) {
    /* read and store embedValues text if present */
    src = mputprintf(src,
      "  if (0 != emb_val) {\n"
      "    if (p_reader.NodeType()==XML_READER_TYPE_TEXT) {\n"
      "      UNIVERSAL_CHARSTRING emb_ustr((const char*)p_reader.Value());\n"
      "      field_%s%s[emb_val->embval_index] = emb_ustr;\n"
      "    }\n"
      "    if (last_embval_index == emb_val->embval_index) {\n"
      "      ++emb_val->embval_index;\n"
      "    }\n"
      "  }\n"
      , sdef->elements[0].name
      , sdef->elements[0].isOptional ? "()" : "");
  }
  
  if (sdef->xerUseNilPossible) {
    src = mputstr(src, "  } // use_nil\n");
  }

  if (sdef->xerUseOrderPossible) {
    src = mputstr(src,  "  } // uo\n");
  }
  
  if (sdef->xerEmbedValuesPossible) {
    src = mputprintf(src,
      "%s"
      "  if (0 != emb_val) {\n"
      "    %s::of_type empty_string(\"\");\n"
      "    for (int j_j = 0; j_j < emb_val->embval_index; ++j_j) {\n"
      "      if (!field_%s%s[j_j].is_bound()) {\n"
      "        field_%s%s[j_j] = empty_string;\n"
      "      }%s%s%s%s"
      "    }\n"
      "    delete emb_val;\n"
      "  }\n"
      , sdef->elements[0].isOptional ? "  boolean all_unbound = TRUE;\n" : ""
      , sdef->elements[0].type
      , sdef->elements[0].name
      , sdef->elements[0].isOptional ? "()" : ""
      , sdef->elements[0].name
      , sdef->elements[0].isOptional ? "()" : ""
      , sdef->elements[0].isOptional ? " else if(field_" : "\n"
      , sdef->elements[0].isOptional ? sdef->elements[0].name : ""
      , sdef->elements[0].isOptional ? "()" : ""
      , sdef->elements[0].isOptional ? "[j_j] != empty_string) {\n        all_unbound = FALSE;\n      }\n" : ""
    );
    
    if(sdef->elements[0].isOptional) {
      src = mputprintf(src,
        "  if (e_xer && (p_td.xer_bits & EMBED_VALUES) && all_unbound) {\n"
        "    field_%s = OMIT_VALUE;\n"
        "  }\n"
        , sdef->elements[0].name
      );
    }
  }

  if (sdef->xerUseQName) {
    src = mputstr(src, "  } // qn\n");
  }
  
  src = mputstr(src,
    "  } // errorcontext\n"); /* End scope for error context objects */

  /* Check if every non-optional field has been set */
  for (i = 0; i < sdef->nElements; ++i) {
    if (!sdef->elements[i].isOptional) {
      src = mputprintf(src,
        "  if (!field_%s.is_bound()) {\n"
        "    if (p_flavor & XER_OPTIONAL) {\n"
        "      clean_up();\n"
        "      return -1;\n"
        "    }\n"
        "    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_INCOMPL_MSG,\n"
        "      \"No data found for non-optional field '%s'\");\n"
        "  }\n"
        , sdef->elements[i].name, sdef->elements[i].dispname);
    }
  }
  src = mputstr(src,
    "  if (!omit_tag) {\n"
    "    int current_depth;\n"
    "    for (rd_ok = p_reader.Ok(); rd_ok == 1; rd_ok = p_reader.Read()) {\n"
    "      type = p_reader.NodeType();\n"
    "      if ((current_depth = p_reader.Depth()) > xml_depth) {\n"
    "        if (XML_READER_TYPE_ELEMENT == type) {\n"
    /*           An element deeper than our start tag has not been processed */
    "          TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_TAG,\n"
    "              \"Unprocessed XML tag `%s'\", (const char *)p_reader.Name());\n"
    "        }\n"
    "        continue;\n"
    "      }\n"
    "      else if (current_depth == xml_depth) {\n"
    "        if (XML_READER_TYPE_ELEMENT == type) {\n"
    "          verify_name(p_reader, p_td, e_xer);\n"
    "          if (p_reader.IsEmptyElement()) {\n"
    "            p_reader.Read();\n" /* advance one more time */
    "            break;\n"
    "          }\n"
    "        }\n"
    "        else if (XML_READER_TYPE_END_ELEMENT == type) {\n"
    "          verify_end(p_reader, p_td, xml_depth, e_xer);\n"
    "          rd_ok = p_reader.Read();\n"
    "          break;\n"
    "        }\n"
    "      }\n"
    "      else break;" /* depth is less, we are in trouble... */
    "    }\n"
    "  }\n"
    "  return 1;\n}\n\n");

  *pdef = def;
  *psrc = src;
}

char* generate_json_decoder(char* src, const struct_def* sdef)
{
  src = mputprintf(src,
    "int %s::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, "
    "boolean p_silent, boolean p_parent_is_map, int)\n"
    "{\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 0;\n"
    "  }\n", sdef->name, sdef->name);

  if (sdef->nElements == 1) {
    if (!sdef->jsonAsValue) {
      src = mputstr(src, "  if (p_td.json->as_value) {\n");
    }
    if (sdef->elements[0].isOptional) {
      // can only happen if the record has the 'JSON:object' attribute;
      // in this case the optional class must not be allowed to decode the
      // JSON literal 'null', since it's not a JSON object;
      // furthermore, the empty JSON object should be decoded as 'omit'
      src = mputprintf(src, 
        "   json_token_t j_token = JSON_TOKEN_NONE;\n"
        "   size_t buf_pos = p_tok.get_buf_pos();\n"
        "   size_t dec_len = p_tok.get_next_token(&j_token, NULL, NULL);\n"
        "   if (j_token == JSON_TOKEN_LITERAL_NULL) {\n"
        "     return JSON_ERROR_FATAL;\n"
        "   }\n"
        "   else if (j_token == JSON_TOKEN_OBJECT_START) {\n"
        "     dec_len += p_tok.get_next_token(&j_token, NULL, NULL);\n"
        "     if (j_token == JSON_TOKEN_OBJECT_END) {\n"
        "       field_%s = OMIT_VALUE;\n"
        "       return dec_len;\n"
        "     }\n"
        "   }\n"
        // otherwise rewind the buffer and decode normally
        "   p_tok.set_buf_pos(buf_pos);\n",
        sdef->elements[0].name);
    }
    src = mputprintf(src, "   return field_%s.JSON_decode(%s_descr_, p_tok, p_silent, FALSE);\n",
      sdef->elements[0].name, sdef->elements[0].typedescrname);
    if (!sdef->jsonAsValue) {
      src = mputstr(src, "  }\n");
    }
  }
  if (!sdef->jsonAsValue) {
    src = mputstr(src, "  json_token_t j_token = JSON_TOKEN_NONE;\n");
  }
  if (sdef->jsonAsMapPossible) {
    src = mputprintf(src,
      "  if (p_parent_is_map) {\n"
      "    char* fld_name = NULL;\n"
      "    size_t name_len = 0;\n"
      "    size_t buf_pos = p_tok.get_buf_pos();\n"
      "    size_t dec_len = p_tok.get_next_token(&j_token, &fld_name, &name_len);\n"
      "    if (JSON_TOKEN_ERROR == j_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_NAME != j_token) {\n"
      "      p_tok.set_buf_pos(buf_pos);\n"
      "      return JSON_ERROR_INVALID_TOKEN;\n"
      "    }\n"
      "    field_%s.decode_utf8(name_len, (unsigned char*) fld_name);\n"
      "    return field_%s.JSON_decode(%s_descr_, p_tok, p_silent, FALSE) + dec_len;\n"
      "  }\n", sdef->elements[0].name,
      sdef->elements[1].name, sdef->elements[1].typedescrname);
  }
  if (!sdef->jsonAsValue) {
    src = mputstr(src,
      "  size_t dec_len = p_tok.get_next_token(&j_token, NULL, NULL);\n"
      "  if (JSON_TOKEN_ERROR == j_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_OBJECT_START != j_token) {\n"
      "    return JSON_ERROR_INVALID_TOKEN;\n"
      "  }\n");

    boolean has_metainfo_enabled = FALSE;
    for (int i = 0; i < sdef->nElements; ++i) {
      if (sdef->elements[i].jsonDefaultValue) {
        // initialize fields with their default values (they will be overwritten
        // later, if the JSON document contains data for these fields)
        src = mputprintf(src,
          "  field_%s.JSON_decode(%s_descr_, DUMMY_BUFFER, p_silent, FALSE);\n"
          , sdef->elements[i].name, sdef->elements[i].typedescrname);
      }
      else {
        src = mputprintf(src, "  boolean %s_found = FALSE;\n", sdef->elements[i].name);
      }
      if (sdef->elements[i].jsonMetainfoUnbound) {
        // initialize meta info states
        src = mputprintf(src, 
          "  int metainfo_%s = JSON_METAINFO_NONE;\n"
          , sdef->elements[i].name);
        has_metainfo_enabled = TRUE;
      }
    }
    src = mputstr(src,
      // Read name - value token pairs until we reach some other token
      "\n  while (TRUE) {\n"
      "    char* fld_name = 0;\n"
      "    size_t name_len = 0;\n"
      "    size_t buf_pos = p_tok.get_buf_pos();\n"
      "    dec_len += p_tok.get_next_token(&j_token, &fld_name, &name_len);\n"
      "    if (JSON_TOKEN_ERROR == j_token) {\n"
      "      JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_NAME_TOKEN_ERROR);\n"
      "      return JSON_ERROR_FATAL;\n"
      "    }\n"
           // undo the last action on the buffer
      "    else if (JSON_TOKEN_NAME != j_token) {\n"
      "      p_tok.set_buf_pos(buf_pos);\n"
      "      break;\n"
      "    }\n"
      "    else {\n      ");
    if (has_metainfo_enabled) {
      // check for meta info
      src = mputstr(src,
        "boolean is_metainfo = FALSE;\n"
        "      if (name_len > 9 && 0 == strncmp(fld_name, \"metainfo \", 9)) {\n"
        "        fld_name += 9;\n"
        "        name_len -= 9;\n"
        "        is_metainfo = TRUE;\n"
        "      }\n      ");
    }
    for (int i = 0; i < sdef->nElements; ++i) {
      src = mputprintf(src,
        // check field name
        "if (%d == name_len && 0 == strncmp(fld_name, \"%s\", name_len)) {\n"
        , (int)strlen(sdef->elements[i].jsonAlias ? sdef->elements[i].jsonAlias : sdef->elements[i].dispname)
        , sdef->elements[i].jsonAlias ? sdef->elements[i].jsonAlias : sdef->elements[i].dispname);
      if (!sdef->elements[i].jsonDefaultValue) {
        src = mputprintf(src,
          "        %s_found = TRUE;\n", sdef->elements[i].name);
      }
      if (has_metainfo_enabled) {
        src = mputstr(src, "        if (is_metainfo) {\n");
        if (sdef->elements[i].jsonMetainfoUnbound) {
          src = mputprintf(src,
            // check meta info
            "          char* info_value = 0;\n"
            "          size_t info_len = 0;\n"
            "          dec_len += p_tok.get_next_token(&j_token, &info_value, &info_len);\n"
            "          if (JSON_TOKEN_STRING == j_token && 9 == info_len &&\n"
            "              0 == strncmp(info_value, \"\\\"unbound\\\"\", 9)) {\n"
            "            metainfo_%s = JSON_METAINFO_UNBOUND;\n"
            "          }\n"
            "          else {\n"
            "            JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_METAINFO_VALUE_ERROR, \"%s\");\n"
            "            return JSON_ERROR_FATAL;\n"
            "          }\n"
            , sdef->elements[i].name, sdef->elements[i].dispname);
        }
        else {
          src = mputprintf(src,
            "          JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_METAINFO_NOT_APPLICABLE, \"%s\");\n"
            "          return JSON_ERROR_FATAL;\n"
            , sdef->elements[i].dispname);
        }
        src = mputstr(src,
          "        }\n"
          "        else {\n");
        if (sdef->elements[i].jsonMetainfoUnbound) {
          src = mputstr(src, "         buf_pos = p_tok.get_buf_pos();\n");
        }
      }
      if (sdef->elements[i].jsonChosen != NULL) {
        /* field index of the otherwise rule */
        char* otherwise_str = NULL;
        boolean first_value = TRUE;
        src = mputstr(src, "         int chosen_field = CHOSEN_FIELD_UNSET;\n");
        int j;
        for (j = 0; j < sdef->elements[i].jsonChosen->nElements; j++) {
          rawAST_coding_taglist* cur_choice =
            sdef->elements[i].jsonChosen->list + j;
          if (cur_choice->nElements > 0) {
            /* this is a normal rule */
            if (first_value) {
              src = mputstr(src, "         if (");
              first_value = FALSE;
            }
            else {
              src = mputstr(src, "         else if (");
            }
            src = genRawFieldChecker(src, cur_choice, TRUE);
            /* set chosen_field in the if's body */
            src = mputstr(src, ") {\n"
              "           chosen_field = ");
            if (cur_choice->fieldnum != -2) {
              src = mputprintf(src, "%d", cur_choice->fieldnum);
            }
            else {
              src = mputstr(src, "CHOSEN_FIELD_OMITTED");
            }
            src = mputstr(src, ";\n"
              "         }\n");
          }
          else {
            /* this is an otherwise rule */
            otherwise_str = cur_choice->fieldnum != -2 ?
              mprintf("%d", cur_choice->fieldnum) : mcopystr("CHOSEN_FIELD_OMITTED");
          }
        }
        if (otherwise_str != NULL && !first_value) {
          /* set chosen_field to the field index of the otherwise rule or -1 */
          src = mputprintf(src,
            "         else {\n"
            "           chosen_field = %s;\n"
            "         }\n", otherwise_str);
          Free(otherwise_str);
        } else if (otherwise_str != NULL && first_value) {
          src = mputprintf(src,
            "         chosen_field = %s;\n", otherwise_str);
          Free(otherwise_str);
        }
      }
      src = mputprintf(src,
        "         int ret_val = field_%s.JSON_decode(%s_descr_, p_tok, p_silent, FALSE%s);\n"
        "         if (0 > ret_val) {\n"
        "           if (JSON_ERROR_INVALID_TOKEN == ret_val) {\n"
        , sdef->elements[i].name, sdef->elements[i].typedescrname
        , sdef->elements[i].jsonChosen != NULL ? ", chosen_field" : "");
      if (sdef->elements[i].jsonMetainfoUnbound) {
        src = mputprintf(src,
          // undo the last action on the buffer, check if the invalid token was a null token 
          "             p_tok.set_buf_pos(buf_pos);\n"
          "             p_tok.get_next_token(&j_token, NULL, NULL);\n"
          "             if (JSON_TOKEN_LITERAL_NULL == j_token) {\n"
          "               if (JSON_METAINFO_NONE == metainfo_%s) {\n"
          // delay reporting an error for now, there might be meta info later
          "                 metainfo_%s = JSON_METAINFO_NEEDED;\n"
          "                 continue;\n"
          "               }\n"
          "               else if (JSON_METAINFO_UNBOUND == metainfo_%s) {\n"
          // meta info already found
          "                 continue;\n"
          "               }\n"
          "             }\n"
          , sdef->elements[i].name, sdef->elements[i].name, sdef->elements[i].name);
      }
      src = mputprintf(src,
        "             JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_FIELD_TOKEN_ERROR, %lu, \"%s\");\n"
        "           }\n"
        "           return JSON_ERROR_FATAL;\n"
        "         }\n"
        "         dec_len += (size_t)ret_val;\n"
        , (unsigned long) strlen(sdef->elements[i].dispname), sdef->elements[i].dispname);
      if (has_metainfo_enabled) {
        src = mputstr(src, "        }\n");
      }
      src = mputstr(src,
        "      }\n"
        "      else ");
    }
    src = mputprintf(src,
      "{\n"
               // invalid field name
      "        if (p_silent) {\n"
      "          return JSON_ERROR_INVALID_TOKEN;\n"
      "        }\n"
      "        JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, %sJSON_DEC_INVALID_NAME_ERROR, (int)name_len, fld_name);\n"
               // if this is set to a warning, skip the value of the field
      "        dec_len += p_tok.get_next_token(&j_token, NULL, NULL);\n"
      "        if (JSON_TOKEN_NUMBER != j_token && JSON_TOKEN_STRING != j_token &&\n"
      "            JSON_TOKEN_LITERAL_TRUE != j_token && JSON_TOKEN_LITERAL_FALSE != j_token &&\n"
      "            JSON_TOKEN_LITERAL_NULL != j_token) {\n"
      "          JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_FIELD_TOKEN_ERROR, (int)name_len, fld_name);\n"
      "          return JSON_ERROR_FATAL;\n"
      "        }\n"
      "      }\n"
      "    }\n"
      "  }\n\n"
      "  dec_len += p_tok.get_next_token(&j_token, NULL, NULL);\n"
      "  if (JSON_TOKEN_OBJECT_END != j_token) {\n"
      "    JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_OBJECT_END_TOKEN_ERROR, \"\");\n"
      "    return JSON_ERROR_FATAL;\n"
      "  }\n\n  "
      , has_metainfo_enabled ? "is_metainfo ?\n          JSON_DEC_METAINFO_NAME_ERROR : " : "");
    // Check if every field has been set and handle meta info
    for (int i = 0; i < sdef->nElements; ++i) {
      if (sdef->elements[i].jsonMetainfoUnbound) {
        src = mputprintf(src,
          "if (JSON_METAINFO_UNBOUND == metainfo_%s) {\n"
          "    field_%s.clean_up();\n"
          "  }\n"
          "  else if (JSON_METAINFO_NEEDED == metainfo_%s) {\n"
          // no meta info was found for this field, report the delayed error
          "    JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_FIELD_TOKEN_ERROR, %lu, \"%s\");\n"
          "  }\n"
          "  else "
          , sdef->elements[i].name, sdef->elements[i].name
          , sdef->elements[i].name
          , (unsigned long) strlen(sdef->elements[i].dispname)
          , sdef->elements[i].dispname);
      }
      if (!sdef->elements[i].jsonDefaultValue) {
        src = mputprintf(src,
          "if (!%s_found) {\n"
          , sdef->elements[i].name);
        if (sdef->elements[i].isOptional) {
          // if the conditions in attribute 'choice' indicate that this field is
          // mandatory, then display an error
          if (sdef->elements[i].jsonChosen != NULL) {
            int j;
            boolean has_otherwise = FALSE;
            boolean omit_otherwise = FALSE;
            for (j = 0; j < sdef->elements[i].jsonChosen->nElements; j++) {
              if (sdef->elements[i].jsonChosen->list[j].nElements == 0) {
                has_otherwise = TRUE;
                if (sdef->elements[i].jsonChosen->list[j].fieldnum == -2) {
                  omit_otherwise = TRUE;
                }
                break;
              }
            }
            boolean first_found = FALSE;
            for (j = 0; j < sdef->elements[i].jsonChosen->nElements; j++) {
              if (((!has_otherwise || omit_otherwise) && sdef->elements[i].jsonChosen->list[j].fieldnum != -2) ||
                   (has_otherwise && !omit_otherwise && sdef->elements[i].jsonChosen->list[j].fieldnum == -2)) {
                if (!first_found) {
                  src = mputstr(src, "    if (");
                  first_found = TRUE;
                }
                else {
                  src = mputstr(src, "\n        || ");
                }
                src = genRawFieldChecker(src, sdef->elements[i].jsonChosen->list + j, !has_otherwise || omit_otherwise);
              }
            }
            if (first_found) {
              src = mputprintf(src,
                ") {\n"
                "      JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_CHOSEN_FIELD_OMITTED, \"%s\");\n"
                "      return JSON_ERROR_FATAL;\n"
                "    }\n", sdef->elements[i].dispname);
            }
          }
          src = mputprintf(src,
            "    field_%s = OMIT_VALUE;\n"
            , sdef->elements[i].name);
        } else {
          src = mputprintf(src,
            "    JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_MISSING_FIELD_ERROR, \"%s\");\n"
            "    return JSON_ERROR_FATAL;\n"
            , sdef->elements[i].dispname);
        }
        src = mputstr(src,
          "  }\n  ");
      } // if there's no default value
    }
    src = mputstr(src,
      "\n  return (int)dec_len;\n");
  }
  return mputstr(src, "}\n\n");
}

char* generate_per_encode_fields(char* str, const struct_def *sdef, int start_idx, int end_idx, boolean ext_add_group)
{
  const char* buf_name = ext_add_group ? "ext_add_buf" : "p_buf";
  // presence bits for OPTIONAL and DEFAULT fields
  int nof_opt_def_fields = 0;
  for (size_t i = start_idx; i < end_idx; ++i) {
    if (sdef->elements[sdef->perFieldOrder[i]].isOptionalAsn ||
        sdef->elements[sdef->perFieldOrder[i]].isDefault) {
      ++nof_opt_def_fields;
    }
  }
  int partial_bits = -1;
  if (nof_opt_def_fields >= PER_16K * 4) {
    partial_bits = PER_16K * 4;
    str = mputprintf(str,
      "INTEGER length_(%d);\n"
      "length_.PER_encode_length(%s, p_options);\n"
      "length_ = length_ - PER_64K;\n",
      nof_opt_def_fields, buf_name);
    nof_opt_def_fields -= partial_bits;
  }
  for (size_t i = start_idx; i < end_idx; ++i) {
    if (sdef->elements[sdef->perFieldOrder[i]].isOptionalAsn ||
        sdef->elements[sdef->perFieldOrder[i]].isDefault) {
      if (!ext_add_group) {
        str = mputprintf(str, "boolean field_%s_present = field_%s.is_present()",
          sdef->elements[sdef->perFieldOrder[i]].name,
          sdef->elements[sdef->perFieldOrder[i]].name);
        if (sdef->elements[sdef->perFieldOrder[i]].isDefault) {
          str = mputprintf(str, " && field_%s != %s",
            sdef->elements[sdef->perFieldOrder[i]].name,
            sdef->elements[sdef->perFieldOrder[i]].defvalname);
        }
        str = mputstr(str, ";\n");
      }
      str = mputprintf(str,
        "%s.PER_put_bit(field_%s_present);\n",
        buf_name, sdef->elements[sdef->perFieldOrder[i]].name);
      if (partial_bits > 0) {
        --partial_bits;
        if (partial_bits == 0) {
          partial_bits = (nof_opt_def_fields < PER_16K * 4) ? (nof_opt_def_fields / PER_16K) : 4;
          str = mputprintf(str, "length_.PER_encode_length(%s, p_options);\n", buf_name);
          if (partial_bits > 0) {
            str = mputprintf(str, "length_ = length_ - PER_16K * %d;\n", partial_bits);
            partial_bits *= PER_16K;
            nof_opt_def_fields -= partial_bits;
          }
          // otherwise do nothing (the for cycle will continue with partial_bits = 0)
        }
      }
    }
  }

  // encoding of the specified fields
  for (size_t i = start_idx; i < end_idx; ++i) {
    if (sdef->elements[sdef->perFieldOrder[i]].isOptionalAsn ||
        sdef->elements[sdef->perFieldOrder[i]].isDefault) {
      str = mputprintf(str, "if (field_%s_present) ",
        sdef->elements[sdef->perFieldOrder[i]].name);
    }
    str = mputprintf(str, "field_%s.PER_encode(%s_descr_, %s, p_options);\n",
      sdef->elements[sdef->perFieldOrder[i]].name,
      sdef->elements[sdef->perFieldOrder[i]].typedescrname, buf_name);
  }
  return str;
}

char* generate_per_decode_fields(char* str, const struct_def *sdef, int start_idx, int end_idx, boolean ext_add_group)
{
  const char* buf_name = ext_add_group ? "ext_add_buf" : "p_buf";
  // presence bits for OPTIONAL and DEFAULT fields
  int nof_opt_def_fields = 0;
  for (size_t i = start_idx; i < end_idx; ++i) {
    if (sdef->elements[sdef->perFieldOrder[i]].isOptionalAsn ||
        sdef->elements[sdef->perFieldOrder[i]].isDefault) {
      ++nof_opt_def_fields;
    }
  }
  int partial_bits = -1;
  if (nof_opt_def_fields >= PER_16K * 4) {
    partial_bits = PER_16K * 4;
    str = mputprintf(str,
      "INTEGER length_;\n"
      "length_.PER_decode_length(%s, p_options);\n",
      buf_name);
    nof_opt_def_fields -= partial_bits;
  }
  for (size_t i = start_idx; i < end_idx; ++i) {
    if (sdef->elements[sdef->perFieldOrder[i]].isOptionalAsn ||
        sdef->elements[sdef->perFieldOrder[i]].isDefault) {
      str = mputprintf(str,
        "boolean field_%s_present = %s.PER_get_bit();\n",
        sdef->elements[sdef->perFieldOrder[i]].name, buf_name);
      if (partial_bits > 0) {
        --partial_bits;
        if (partial_bits == 0) {
          partial_bits = (nof_opt_def_fields < PER_16K * 4) ? (nof_opt_def_fields / PER_16K) : 4;
          str = mputprintf(str, "length_.PER_decode_length(%s, p_options);\n", buf_name);
          if (partial_bits > 0) {
            partial_bits *= PER_16K;
            nof_opt_def_fields -= partial_bits;
          }
          // otherwise do nothing (the for cycle will continue with partial_bits = 0)
        }
      }
    }
  }

  // decoding of the specified fields
  for (size_t i = start_idx; i < end_idx; ++i) {
    if (sdef->elements[sdef->perFieldOrder[i]].isOptionalAsn ||
        sdef->elements[sdef->perFieldOrder[i]].isDefault) {
      str = mputprintf(str, "if (field_%s_present) ",
        sdef->elements[sdef->perFieldOrder[i]].name);
    }
    str = mputprintf(str, "field_%s.PER_decode(%s_descr_, %s, p_options);\n",
      sdef->elements[sdef->perFieldOrder[i]].name,
      sdef->elements[sdef->perFieldOrder[i]].typedescrname, buf_name);
    if (sdef->elements[sdef->perFieldOrder[i]].isOptionalAsn ||
        sdef->elements[sdef->perFieldOrder[i]].isDefault) {
      str = mputprintf(str, "else field_%s = %s;\n",
        sdef->elements[sdef->perFieldOrder[i]].name,
        sdef->elements[sdef->perFieldOrder[i]].isOptionalAsn ? "OMIT_VALUE" :
          sdef->elements[sdef->perFieldOrder[i]].defvalname);
    }
  }
  return str;
}

void defRecordClass1(const struct_def *sdef, output_struct *output)
{
  size_t i;
  size_t mandatory_fields_count;
  const char *name = sdef->name, *dispname = sdef->dispname;
  const char* kind_str = sdef->kind == SET ? "set" : "record";
  char *def = NULL, *src = NULL;
  boolean ber_needed = sdef->isASN1 && !sdef->containsClass && enable_ber();
  boolean raw_needed = sdef->hasRaw && !sdef->containsClass && enable_raw();
  boolean text_needed = sdef->hasText && !sdef->containsClass && enable_text();
  boolean xer_needed = sdef->hasXer && !sdef->containsClass && enable_xer();
  boolean json_needed = sdef->hasJson && !sdef->containsClass && enable_json();
  boolean oer_needed = sdef->hasOer && !sdef->containsClass && enable_oer();
  boolean per_needed = sdef->hasPer && !sdef->containsClass && enable_per();

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

  if (sdef->nElements <= 0) {
    defEmptyRecordClass(sdef, output);
    return;
  }

  /* class definition and source code */
  if(ber_needed || raw_needed || text_needed || xer_needed || json_needed) {
    def=mputprintf
      (def,
#ifndef NDEBUG
        "// written by %s in " __FILE__ " at line %d\n"
#endif
        "class %s : public Base_Type {\n"
#ifndef NDEBUG
        , __FUNCTION__, __LINE__
#endif
        , name);
  } else {
    def=mputprintf(def, "class %s {\n", name);
  }

  for (i = 0; i < sdef->nElements; i++) {
    if(sdef->elements[i].isOptional)
      def = mputprintf(def, "  OPTIONAL< %s > field_%s;\n",
                       sdef->elements[i].type,
                       sdef->elements[i].name);
    else
      def = mputprintf(def, "  %s field_%s;\n",
                       sdef->elements[i].type, sdef->elements[i].name);
  }

  /* default constructor */
  def = mputprintf(def, "public:\n"
    "  %s();\n", name);
  src = mputprintf(src, "%s::%s()\n"
    "{\n"
    "}\n\n", name, name);

  /* constructor by fields */
  def = mputprintf(def, "  %s(", name);
  src = mputprintf(src, "%s::%s(", name, name);

  for (i = 0; i < sdef->nElements; i++) {
    char *tmp = NULL;
    if (i > 0) tmp = mputstr(tmp, ",\n    ");
    if (sdef->elements[i].isOptional)
      tmp = mputprintf
        (tmp,
         "const OPTIONAL< %s >& par_%s",
         sdef->elements[i].type, sdef->elements[i].name);
    else
      tmp = mputprintf
        (tmp,
         "const %s& par_%s",
         sdef->elements[i].type, sdef->elements[i].name);
    def = mputstr(def, tmp);
    src = mputstr(src, tmp);
    Free(tmp);
  }
  def = mputstr(def, ");\n");
  src = mputstr(src, ")\n"
                "  : ");
  for (i = 0; i < sdef->nElements; i++) {
    if (i > 0) src = mputstr(src, ",\n");
    src = mputprintf(src, "  field_%s(par_%s)", sdef->elements[i].name,
                     sdef->elements[i].name);
  }
  src = mputstr(src, "\n"
                "{\n"
                "}\n\n");

  /* copy constructor */
  def = mputprintf(def, "  %s(const %s& other_value);\n", name, name);
  src = mputprintf(src, "%s::%s(const %s& other_value)\n"
      "{\n"
      "if(!other_value.is_bound()) "
      "TTCN_error(\"Copying an unbound value of type %s.\");\n",
      name, name, name, dispname);
  for (i = 0; i < sdef->nElements; i++) {
    src = mputprintf(src,
      "if (other_value.%s().is_bound()) field_%s = other_value.%s();\n"
      "else field_%s.clean_up();\n",
      sdef->elements[i].name, sdef->elements[i].name, sdef->elements[i].name,
      sdef->elements[i].name);
  }
  src = mputstr(src, "}\n\n");
  
  /* not a component */
  def = mputstr(def, "  inline boolean is_component() { return FALSE; }\n");

  /* clean_up */
  def = mputstr(def, "  void clean_up();\n");
  src = mputprintf(src, "void %s::clean_up()\n"
      "{\n", name);
  for (i = 0; i < sdef->nElements; i++) {
    src = mputprintf(src,
      "field_%s.clean_up();\n", sdef->elements[i].name);
  }
  src = mputstr(src,
      "}\n\n");

  if (!sdef->containsClass) {
    def = mputstr(def, "const TTCN_Typedescriptor_t* get_descriptor() const;\n");
    src = mputprintf(src,
      "const TTCN_Typedescriptor_t* %s::get_descriptor() const { return &%s_descr_; }\n",
      name, name);
  }
  else if (use_runtime_2) { // need to implement this, cause it's abstract in RT2
    def = mputstr(def, "const TTCN_Typedescriptor_t* get_descriptor() const { return NULL; }\n");
  }

  /* = operator */
  def = mputprintf(def, "  %s& operator=(const %s& other_value);\n", name, name);
  src = mputprintf(src, "%s& %s::operator=(const %s& other_value)\n"
      "{\n"
      "if (this != &other_value) {\n"
      "  if(!other_value.is_bound()) "
      "TTCN_error(\"Assignment of an unbound value of type %s.\");\n",
      name, name, name, dispname);
  for (i = 0; i < sdef->nElements; i++) {
    src = mputprintf(src,
      "  if (other_value.%s().is_bound()) field_%s = other_value.%s();\n"
      "  else field_%s.clean_up();\n",
      sdef->elements[i].name, sdef->elements[i].name, sdef->elements[i].name,
      sdef->elements[i].name);
  }
  src = mputstr(src,
      "}\n"
      "return *this;\n"
      "}\n\n");

  /* == operator */
  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"
                   "return ", name, name);
  for (i = 0; i < sdef->nElements; i++) {
    if (i > 0) src = mputstr(src, "\n  && ");
    src = mputprintf(src, "field_%s==other_value.field_%s",
                     sdef->elements[i].name, sdef->elements[i].name);
  }
  src = mputstr(src, ";\n}\n\n");

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

  /* is_bound */
  def = mputprintf(def,
      "  boolean is_bound() const;\n\n");
  src = mputprintf(src,
      "boolean %s::is_bound() const\n"
      "{\n"
      "return ", name);
      for(i=0; i < sdef->nElements; i++) {
        if (i != 0 ) {
          src = mputstr(src, "\n  || ");
        }
        if(sdef->elements[i].isOptional) {
          src = mputprintf(src,
            "(OPTIONAL_OMIT == field_%s.get_selection() || field_%s.is_bound())",
            sdef->elements[i].name, sdef->elements[i].name);
        } else {
          src = mputprintf(src,
            "(field_%s.is_bound())",
            sdef->elements[i].name);
        }
      }
  src = mputprintf(src,
      ";\n"
      "}\n");

  /* is_present */
  def = mputstr(def,
      "inline boolean is_present() const { return is_bound(); }\n");

  /* is_value */
  def = mputprintf(def,
      "  boolean is_value() const;\n\n");
  src = mputprintf(src,
      "boolean %s::is_value() const\n"
      "{\n"
      "return ", name);
      for(i=0; i < sdef->nElements; i++) {
        if (i != 0 ) {
          src = mputstr(src, "\n  && ");
        }
        if(sdef->elements[i].isOptional) {
          src = mputprintf(src,
            "(OPTIONAL_OMIT == field_%s.get_selection() || field_%s.is_value())",
            sdef->elements[i].name, sdef->elements[i].name);
        } else {
          src = mputprintf(src,
            "field_%s.is_value()",
            sdef->elements[i].name);
        }
      }
  src = mputprintf(src,
      ";\n}\n");

  /* field accessor methods */
  for (i = 0; i < sdef->nElements; i++) {
    if(sdef->elements[i].isOptional)
      def = mputprintf
        (def,
         "  inline OPTIONAL< %s >& %s()\n"
         "    {return field_%s;}\n"
         "  inline const OPTIONAL< %s >& %s() const\n"
         "    {return field_%s;}\n",
         sdef->elements[i].type, sdef->elements[i].name,
         sdef->elements[i].name,
         sdef->elements[i].type, sdef->elements[i].name,
         sdef->elements[i].name);
    else def = mputprintf
           (def,
            "  inline %s& %s()\n"
            "    {return field_%s;}\n"
            "  inline const %s& %s() const\n"
            "    {return field_%s;}\n",
            sdef->elements[i].type, sdef->elements[i].name,
            sdef->elements[i].name, sdef->elements[i].type,
            sdef->elements[i].name, sdef->elements[i].name);

  }

  /* sizeof operation */
  mandatory_fields_count = 0;
  for (i = 0; i < sdef->nElements; i++)
    if (!sdef->elements[i].isOptional) mandatory_fields_count++;

  if(sdef->nElements == mandatory_fields_count){
    def = mputprintf(def,
        "  inline int size_of() const\n"
        "    {return %lu;}\n", (unsigned long) mandatory_fields_count);
  }else{
    def = mputstr(def, "  int size_of() const;\n");
    src = mputprintf(src,
      "int %s::size_of() const\n"
      "{\n",
      name);
    src = mputprintf(src, "  int ret_val = %lu;\n",
      (unsigned long) mandatory_fields_count);
    for (i = 0; i < sdef->nElements; i++)
      if (sdef->elements[i].isOptional)
        src = mputprintf(src,
          "  if (field_%s.ispresent()) ret_val++;\n",
          sdef->elements[i].name);
    src = mputstr(src,
      "  return ret_val;\n"
      "}\n\n");
  }

  /* log function */
  if (asn1_enc_only) {
    def = mputstr(def, "  CHARSTRING log() const;\n");
    src = mputprintf(src, 
      "CHARSTRING %s::log() const\n{\n"
      "if (!is_bound()) {\n"
      "return UNBOUND_VALUE_LOG;\n"
      "}\n"
      "return ", name);
    for (i = 0; i < sdef->nElements; i++) {
      src = mputprintf(src,
        "CHARSTRING(\"%c %s := \") + field_%s.log() +\n",
        (i == 0) ? '{' : ',', sdef->elements[i].dispname,
        sdef->elements[i].name);
    }
    src = mputstr(src, "CHARSTRING(\" }\");\n}\n\n");
  }
  else {
    def = mputstr(def, "  void log() const;\n");
    src = mputprintf(src, 
      "void %s::log() const\n{\n"
      "if (!is_bound()) {\n"
      "TTCN_Logger::log_event_unbound();\n"
      "return;\n"
      "}\n", name);
    for (i = 0; i < sdef->nElements; i++) {
      src = mputstr(src, "TTCN_Logger::log_event_str(\"");
      if (i == 0) src = mputc(src, '{');
      else src = mputc(src, ',');
      src = mputprintf(src, " %s := \");\n"
        "%s"
        "field_%s.log();\n"
        "%s",
        sdef->elements[i].dispname,
        sdef->elements[i].sensitive_data ? "TTCN_Logger::begin_sensitive_data();\n" : "",
        sdef->elements[i].name,
        sdef->elements[i].sensitive_data ? "TTCN_Logger::end_sensitive_data();\n" : "");
    }
    src = mputstr(src, "TTCN_Logger::log_event_str(\" }\");\n}\n\n");
  }

  /* set implicit omit function, recursive */
  def = mputstr(def, "  void set_implicit_omit();\n");
  src = mputprintf(src, "void %s::set_implicit_omit()\n{\n", name);
  for (i = 0; i < sdef->nElements; i++) {
    if (sdef->elements[i].isOptional || (asn1_enc_only && sdef->elements[i].isDefault)) {
      src = mputprintf(src,
        "if (!%s().is_bound()) %s() = %s;\n"
        "else %s().set_implicit_omit();\n",
        sdef->elements[i].name, sdef->elements[i].name,
        (asn1_enc_only && sdef->elements[i].isDefault) ? sdef->elements[i].defvalname : "OMIT_VALUE",
        sdef->elements[i].name);
    } else {
      src = mputprintf(src,
        "if (%s().is_bound()) %s().set_implicit_omit();\n",
        sdef->elements[i].name, sdef->elements[i].name);
    }
  }
  src = mputstr(src, "}\n\n");

  if (!sdef->containsClass && !asn1_enc_only) {
    /* set param function */
    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, \"%s value\");\n"
       "  switch (param.get_type()) {\n"
       "  case Module_Param::MP_Value_List:\n"
       "    if (%lu<param.get_size()) {\n"
       "      param.error(\"%s value of type %s has %lu fields but list value has %%d fields\", (int)param.get_size());\n"
       "    }\n",
       name, kind_str, (unsigned long)sdef->nElements, kind_str, dispname, (unsigned long)sdef->nElements);

    for (i = 0; i < sdef->nElements; ++i) {
      src = mputprintf(src,
	"    if (param.get_size()>%lu && param.get_elem(%lu)->get_type()!=Module_Param::MP_NotUsed) %s().set_param(*param.get_elem(%lu));\n",
	(unsigned long)i, (unsigned long)i, sdef->elements[i].name, (unsigned long)i);
    }
    src = mputstr(src,
	"    break;\n"
	"  case Module_Param::MP_Assignment_List: {\n"
	"    Vector<bool> value_used(param.get_size());\n"
	"    value_used.resize(param.get_size(), FALSE);\n");
    for (i = 0; i < sdef->nElements; ++i) {
      src = mputprintf(src,
	"    for (size_t val_idx=0; val_idx<param.get_size(); val_idx++) {\n"
	"      Module_Param* const curr_param = param.get_elem(val_idx);\n"
	"      if (!strcmp(curr_param->get_id()->get_name(), \"%s\")) {\n"
	"        if (curr_param->get_type()!=Module_Param::MP_NotUsed) {\n"
	"          %s().set_param(*curr_param);\n"
	"        }\n"
	"        value_used[val_idx]=TRUE;\n"
	"      }\n"
	"    }\n"
	, sdef->elements[i].dispname, sdef->elements[i].name);
    }
    src = mputprintf(src,
	"    for (size_t val_idx=0; val_idx<param.get_size(); val_idx++) if (!value_used[val_idx]) {\n"
	"      Module_Param* const curr_param = param.get_elem(val_idx);\n"
	"      curr_param->error(\"Non existent field name in type %s: %%s\", curr_param->get_id()->get_name());\n"
	"      break;\n"
	"    }\n"
	"  } break;\n"
	"  default:\n"
	"    param.type_error(\"%s value\", \"%s\");\n"
	"  }\n"
	"}\n\n", dispname, kind_str, dispname);

    /* text encoder function */
    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",
		     name);
    for (i = 0; i < sdef->nElements; i++) {
      src = mputprintf(src, "field_%s.encode_text(text_buf);\n",
		       sdef->elements[i].name);
    }
    src = mputstr(src, "}\n\n");

    /* text decoder function */
    def = mputstr(def, "  void decode_text(Text_Buf& text_buf);\n");
    src = mputprintf(src, "void %s::decode_text(Text_Buf& text_buf)\n{\n",
		     name);
    for (i = 0; i < sdef->nElements; i++) {
      src = mputprintf(src, "field_%s.decode_text(text_buf);\n",
		       sdef->elements[i].name);
    }
    src = mputstr(src, "}\n\n");
  }
  else if (use_runtime_2) {
    // implement abstract functions inherited by Base_Type (these are never called)
    def = mputstr(def,
      "  void set_param(Module_Param&) { }\n"
      "  Module_Param* get_param(Module_Param_Name&) const { return NULL; }\n"
      "  void encode_text(Text_Buf&) const { }\n"
      "  void decode_text(Text_Buf&) { }\n"
      "  boolean is_equal(const Base_Type*) const { return FALSE; }\n"
      "  void set_value(const Base_Type*) { }\n"
      "  Base_Type* clone() const { return NULL; }\n\n");
  }

  /* The common "classname::encode()" and "classname::decode()" functions */
  if(ber_needed || raw_needed || text_needed || xer_needed || json_needed
    || oer_needed || per_needed) {
    def_encdec(name, &def, &src, ber_needed, raw_needed,
               text_needed, xer_needed, json_needed, oer_needed, per_needed, FALSE);
  }

  /* BER functions */
  if(ber_needed) {
    /* BER_encode_TLV() */
    src=mputprintf
      (src,
       "ASN_BER_TLV_t* %s::BER_encode_TLV(const TTCN_Typedescriptor_t& p_td,"
       " unsigned p_coding) const\n"
       "{\n"
       "  if (!is_bound()) TTCN_EncDec_ErrorContext::error"
       "(TTCN_EncDec::ET_UNBOUND, \"Encoding an unbound value.\");\n"
       "  BER_chk_descr(p_td);\n"
       "  ASN_BER_TLV_t *new_tlv=ASN_BER_TLV_t::construct(NULL);\n"
       "  TTCN_EncDec_ErrorContext ec_0(\"Component '\");\n"
       "  TTCN_EncDec_ErrorContext ec_1;\n"
       , name
       );
    for(i=0; i<sdef->nElements; i++) {
      if(!default_as_optional && sdef->elements[i].isDefault) { /* is DEFAULT */
        src=mputprintf
          (src,
           "  if(field_%s!=%s) {\n"
           "    ec_1.set_msg(\"%s': \");\n"
           "    new_tlv->add_TLV(field_%s.BER_encode_TLV(%s_descr_,"
           " p_coding));\n"
           "  }\n"
           , sdef->elements[i].name, sdef->elements[i].defvalname
           , sdef->elements[i].dispname
           , sdef->elements[i].name, sdef->elements[i].typedescrname
           );
      }
      else { /* is not DEFAULT */
        src=mputprintf
          (src,
           "  ec_1.set_msg(\"%s': \");\n"
           "  new_tlv->add_TLV(field_%s.BER_encode_TLV(%s_descr_,"
           " p_coding));\n"
           , sdef->elements[i].dispname
           , sdef->elements[i].name, sdef->elements[i].typedescrname
           );
      } /* !isDefault */
    } /* for i */
    if(sdef->kind==SET) {
    src=mputstr
      (src,
       "  if(p_coding==BER_ENCODE_DER)\n"
       "    new_tlv->sort_tlvs_tag();\n"
       );
    }
    src=mputstr
      (src,
       "  new_tlv=ASN_BER_V2TLV(new_tlv, p_td, p_coding);\n"
       "  return new_tlv;\n"
       "}\n"
       "\n"
       );

    /* BER_decode_TLV() */
    src=mputprintf
      (src,
       "boolean %s::BER_decode_TLV(const TTCN_Typedescriptor_t& p_td,"
       " const ASN_BER_TLV_t& p_tlv, unsigned L_form)\n"
       "{\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_0(\"While decoding '%s' type: \");\n"
       "  stripped_tlv.chk_constructed_flag(TRUE);\n"
       "  size_t V_pos=0;\n"
       "  ASN_BER_TLV_t tmp_tlv;\n"
       , name, sdef->dispname
       );

    if(sdef->kind==RECORD)
    { /* SEQUENCE decoding */
      src=mputstr
        (src,
         "  boolean tlv_present=FALSE;\n"
         "  {\n"
         "    TTCN_EncDec_ErrorContext ec_1(\"Component '\");\n"
         "    TTCN_EncDec_ErrorContext ec_2;\n"
         );
      for(i=0; i<sdef->nElements; i++) {
        src=mputprintf
          (src,
           "    ec_2.set_msg(\"%s': \");\n"
           "    if(!tlv_present) tlv_present=BER_decode_constdTLV_next"
           "(stripped_tlv, V_pos, L_form, tmp_tlv);\n"
           , sdef->elements[i].dispname
           );
        if(sdef->elements[i].isDefault) { /* is DEFAULT */
          src=mputprintf
            (src,
             "    if(!tlv_present || !field_%s.BER_decode_isMyMsg(%s_descr_,"
             " tmp_tlv))\n"
             "      field_%s=%s;\n"
             "    else {\n"
             "      field_%s.BER_decode_TLV(%s_descr_, tmp_tlv,"
             " L_form);\n"
             "      tlv_present=FALSE;\n"
             "    }\n"
             , sdef->elements[i].name, sdef->elements[i].typedescrname
             , sdef->elements[i].name, sdef->elements[i].defvalname
             , sdef->elements[i].name, sdef->elements[i].typedescrname
             );
        }
        else if(sdef->elements[i].isOptionalAsn) { /* is OPTIONAL */
          src=mputprintf
            (src,
             "    if(!tlv_present) field_%s=OMIT_VALUE;\n"
             "    else {\n"
             "      field_%s.BER_decode_TLV(%s_descr_, tmp_tlv, L_form);\n"
             "      if(field_%s.ispresent()) tlv_present=FALSE;\n"
             "    }\n"
             , sdef->elements[i].name
             , sdef->elements[i].name, sdef->elements[i].typedescrname
             , sdef->elements[i].name
             );
        }
        else { /* is not DEFAULT OPTIONAL */
          src=mputprintf
            (src,
             "    if(!tlv_present){\n"
             "      ec_2.error(TTCN_EncDec::ET_INCOMPL_MSG,\"Invalid or incomplete message was received.\");\n"
             "      return FALSE;\n"
             "    }\n"
             "    field_%s.BER_decode_TLV(%s_descr_, tmp_tlv, L_form);\n"
             "    tlv_present=FALSE;\n"
             , sdef->elements[i].name, sdef->elements[i].typedescrname
             );
        } /* !isDefault */
      } /* for i */
      src=mputstr
        (src,
         "  }\n"
         "  BER_decode_constdTLV_end(stripped_tlv, V_pos, L_form, tmp_tlv,"
         " tlv_present);\n"
         );
    } /* SEQUENCE decoding */
    else { /* SET decoding */
      src=mputprintf
        (src,
         "  const char *fld_name[%lu]={"
         , (unsigned long) sdef->nElements
         );
      for(i=0; i<sdef->nElements; i++)
        src=mputprintf
          (src,
           "%s\"%s\""
           , i?", ":""
           , sdef->elements[i].dispname
           );
      /* field indicator:
       *   0x01: value arrived
       *   0x02: is optional / not used :)
       *   0x04: has default / not used :)
       */
      src=mputprintf
        (src,
         "};\n"
         "  unsigned char fld_indctr[%lu]={"
         , (unsigned long) sdef->nElements
         );
      for(i=0; i<sdef->nElements; i++)
        src=mputprintf
          (src,
           "%s0"
           , i?", ":""
           );
      src=mputstr
        (src,
         "};\n"
         "  size_t fld_curr;\n"
         "  while(BER_decode_constdTLV_next(stripped_tlv, V_pos,"
         " L_form, tmp_tlv)) {\n"
         );
      for(i=0; i<sdef->nElements; i++)
        src=mputprintf
          (src,
           "    %sif(field_%s.BER_decode_isMyMsg(%s_descr_, tmp_tlv)) {\n"
           "      fld_curr=%lu;\n"
           "      TTCN_EncDec_ErrorContext ec_1(\"Component '%%s': \","
           " fld_name[%lu]);\n"
           "      field_%s.BER_decode_TLV(%s_descr_, tmp_tlv, L_form);\n"
           "    }\n"
           , i?"else ":""
           , sdef->elements[i].name, sdef->elements[i].typedescrname
           , (unsigned long) i, (unsigned long) i
           , sdef->elements[i].name, sdef->elements[i].typedescrname
           );
      src=mputprintf
        (src,
         "    else {\n"
         "      /* ellipsis or error... */\n"
         "      fld_curr=static_cast<size_t>(-1);\n"
         "    }\n"
         "    if(fld_curr!=static_cast<size_t>(-1)) {\n"
         "      if(fld_indctr[fld_curr])\n"
         "        ec_0.error(TTCN_EncDec::ET_DEC_DUPFLD, \"Duplicated"
         " value for component '%%s'.\", fld_name[fld_curr]);\n"
         "      fld_indctr[fld_curr]=1;\n"
         "    }\n" /* if != -1 */
         "  }\n" /* while */
         "  for(fld_curr=0; fld_curr<%lu; fld_curr++) {\n"
         "    if(fld_indctr[fld_curr]) continue;\n"
         "    switch(fld_curr) {\n"
         , (unsigned long) sdef->nElements
         );
      for(i=0; i<sdef->nElements; i++) {
        if(sdef->elements[i].isDefault)
          src=mputprintf
            (src,
             "    case %lu:\n"
             "      field_%s=%s;\n"
             "      break;\n"
             , (unsigned long) i
             , sdef->elements[i].name, sdef->elements[i].defvalname
           );
        else if(sdef->elements[i].isOptionalAsn)
          src=mputprintf
            (src,
             "    case %lu:\n"
             "      field_%s=OMIT_VALUE;\n"
             "      break;\n"
             , (unsigned long) i
             , sdef->elements[i].name
           );
      } /* for i */
      src=mputstr
        (src,
         "    default:\n"
         "      ec_0.error(TTCN_EncDec::ET_DEC_MISSFLD, \"Missing"
         " value for component '%s'.\", fld_name[fld_curr]);\n"
         "    }\n" /* switch */
         "  }\n" /* for fld_curr */
         );
    } /* SET decoding */

    if(sdef->opentype_outermost) {
      src=mputstr
        (src,
         "  TTCN_EncDec_ErrorContext ec_1(\"While decoding opentypes: \");\n"
         "  TTCN_Type_list p_typelist;\n"
         "  BER_decode_opentypes(p_typelist, L_form);\n"
         );
    } /* if sdef->opentype_outermost */
    src=mputstr
      (src,
       "  return TRUE;\n"
       "}\n"
       "\n"
       );

    if(sdef->has_opentypes) {
      /* BER_decode_opentypes() */
      def=mputstr
        (def,
         "void BER_decode_opentypes(TTCN_Type_list& p_typelist,"
         " unsigned L_form);\n");
      src=mputprintf
        (src,
         "void %s::BER_decode_opentypes(TTCN_Type_list& p_typelist,"
         " unsigned L_form)\n"
         "{\n"
         "  p_typelist.push(this);\n"
         "  TTCN_EncDec_ErrorContext ec_0(\"Component '\");\n"
         "  TTCN_EncDec_ErrorContext ec_1;\n"
         , name
         );
      for(i=0; i<sdef->nElements; i++) {
        src=mputprintf
          (src,
           "  ec_1.set_msg(\"%s': \");\n"
           "  field_%s.BER_decode_opentypes(p_typelist, L_form);\n"
           , sdef->elements[i].dispname
           , sdef->elements[i].name
           );
      } /* for i */
      src=mputstr
        (src,
         "  p_typelist.pop();\n"
         "}\n"
         "\n"
         );
    } /* if sdef->has_opentypes */

  } /* if ber_needed */

  if(text_needed){
    int man_num=0;
    int opt_number=0;
    int seof=0;
    size_t last_man_index=0;
    for(i=0;i<sdef->nElements;i++){
      if(sdef->elements[i].isOptional) opt_number++;
      else {man_num++;last_man_index=i+1;}
      if(sdef->elements[i].of_type) seof++;
    }

    src=mputprintf(src,
      "int %s::TEXT_encode(const TTCN_Typedescriptor_t& p_td,"
      " TTCN_Buffer& p_buf) const{\n"
      "  if (!is_bound()) TTCN_EncDec_ErrorContext::error"
      "(TTCN_EncDec::ET_UNBOUND, \"Encoding an unbound value.\");\n"
      "  boolean need_separator=FALSE;\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"
      ,name
     );

    for(i=0;i<sdef->nElements;i++){
      if(sdef->elements[i].isOptional){
        src=mputprintf(src,
        "  if(field_%s.ispresent()){\n"
        ,sdef->elements[i].name
        );
      }
      src=mputprintf(src,
        " if(need_separator && p_td.text->separator_encode){\n"
        "    p_buf.put_cs(*p_td.text->separator_encode);\n"
        "    encoded_length+=p_td.text->separator_encode->lengthof();\n"
        " }\n"
        " encoded_length+=field_%s%s.TEXT_encode(%s_descr_,p_buf);\n"
        " need_separator=TRUE;\n"
        ,sdef->elements[i].name
        ,sdef->elements[i].isOptional?"()":"",sdef->elements[i].typedescrname
       );
      if(sdef->elements[i].isOptional){
        src=mputstr(src,
        "  }\n"
        );
      }
    }

    src=mputstr(src,
      "  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(sdef->kind==SET){ /* set decoder start*/
      src = mputprintf(src,
      "int %s::TEXT_decode(const TTCN_Typedescriptor_t& p_td,"
      " TTCN_Buffer& p_buf, Limit_Token_List& limit, boolean no_err, boolean){\n"
      "  int decoded_length=0;\n"
      "  int decoded_field_length=0;\n"
      "  size_t pos=p_buf.get_pos();\n"
      "  boolean sep_found=FALSE;\n"
      "  int ml=0;\n"
      "  int sep_length=0;\n"
      "  int mand_field_num=%d;\n"
      "  int opt_field_num=%d;\n"
      "  int loop_detector=1;\n"
      "  int last_field_num=-1;\n"
      "  int field_map[%lu];\n"
      "  memset(field_map, 0, sizeof(field_map));\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_td.text->end_decode){\n"
      "    limit.add_token(p_td.text->end_decode);\n"
      "    ml++;\n"
      "  }\n"
      "  if(p_td.text->separator_decode){\n"
      "    limit.add_token(p_td.text->separator_decode);\n"
      "    ml++;\n"
      "  }\n"
        ,name,man_num,opt_number,(unsigned long) sdef->nElements
       );

      if(seof){
          int first=0;
          src=mputstr(src,
          "  int has_repeatable=0;\n"
          "  if("
          );
          for(i=0;i<sdef->nElements;i++){
            if(sdef->elements[i].of_type){
              src=mputprintf(src,
              "%s%s_descr_.text->val.parameters->decoding_params.repeatable"
              ,first?" && ":""
              ,sdef->elements[i].typedescrname
              );
              first=1;
            }
          }
          src=mputstr(src,
          ") has_repeatable=1;\n"
          );

      }
      for(i=0;i<sdef->nElements;i++){
        if(sdef->elements[i].isOptional){
          src=mputprintf(src,
          "  field_%s=OMIT_VALUE;\n"
          ,sdef->elements[i].name
          );
        }
      }
      src = mputprintf(src,
      "  while(mand_field_num+opt_field_num%s){\n"
      "    loop_detector=1;\n"
      "    while(TRUE){\n"
      ,seof?"+has_repeatable":""
      );

      for(i=0;i<sdef->nElements;i++){
        if(sdef->elements[i].of_type){
          src=mputprintf(src,
          "  if( (%s_descr_.text->val.parameters->decoding_params.repeatable"
          " && field_map[%lu]<3) || !field_map[%lu]){\n"
          "    pos=p_buf.get_pos();\n"
          "    decoded_field_length=field_%s%s.TEXT_decode(%s_descr_,p_buf,"
          "limit,TRUE,!field_map[%lu]);\n"
          "    if(decoded_field_length<0){\n"
          "      p_buf.set_pos(pos);\n"
          ,sdef->elements[i].typedescrname,(unsigned long) i,(unsigned long) i
          ,sdef->elements[i].name,sdef->elements[i].isOptional?"()":""
          ,sdef->elements[i].typedescrname
          ,(unsigned long) i
          );

          if(sdef->elements[i].isOptional){
            src=mputprintf(src,
             "   if(!field_map[%lu]) field_%s=OMIT_VALUE;\n"
             ,(unsigned long) i
             ,sdef->elements[i].name
            );
          }
          src=mputprintf(src,
          "    }else{\n"
          "     loop_detector=0;\n"
          "     if(!field_map[%lu]) {%s--;field_map[%lu]=1;}\n"
          "     else field_map[%lu]=2;\n"
          "     last_field_num=%lu;\n"
          "     break;\n"
          "    }\n"
          "    }\n"
          ,(unsigned long) i
          ,sdef->elements[i].isOptional?"opt_field_num":"mand_field_num"
          ,(unsigned long) i,(unsigned long) i,(unsigned long) i
          );
        } else {
          src=mputprintf(src,
          "  if(!field_map[%lu]){\n"
          "    pos=p_buf.get_pos();\n"
          "    decoded_field_length=field_%s%s.TEXT_decode(%s_descr_,p_buf,"
          "limit,TRUE);\n"
          "    if(decoded_field_length<0){\n"
          "      p_buf.set_pos(pos);\n"
          "%s%s%s"
          "    }else{\n"
          "     loop_detector=0;\n"
          "     field_map[%lu]=1;\n"
          "     %s--;\n"
          "     last_field_num=%lu;\n"
          "     break;\n"
          "    }\n"
          "    }\n"
          ,(unsigned long) i
          ,sdef->elements[i].name,sdef->elements[i].isOptional?"()":""
          ,sdef->elements[i].typedescrname
          ,sdef->elements[i].isOptional?"    field_":""
          ,sdef->elements[i].isOptional?sdef->elements[i].name:""
          ,sdef->elements[i].isOptional?"=OMIT_VALUE;\n":"",(unsigned long) i
          ,sdef->elements[i].isOptional?"opt_field_num":"mand_field_num"
          ,(unsigned long) i
          );
        }
     }

      src = mputstr(src,
      "    break;\n"
      "    }\n"
      "    if(loop_detector) break;\n"
      "    if(p_td.text->separator_decode){\n"
      "      int tl;\n"
      "      if((tl=p_td.text->separator_decode->match_begin(p_buf))<0){\n"
       "      if(p_td.text->end_decode){\n"
       "      int tl;\n"
       "      if((tl=p_td.text->end_decode->match_begin(p_buf))!=-1){\n"
       "        sep_found=FALSE;\n"
       "        break;\n"
       "      }\n"
       "    } else if(limit.has_token(ml)){\n"
       "      if(limit.match(p_buf,ml)==0){\n"
       "        sep_found=FALSE;\n"
       "        break;\n"
       "      }\n"
      "     } else break;\n"
       "    p_buf.set_pos(pos);\n"
       "    decoded_length-=decoded_field_length;\n"
       "    field_map[last_field_num]+=2;\n"
      );

      if(opt_number){
       src=mputstr(src,
        "switch(last_field_num){\n"
       );
      for(i=0;i<sdef->nElements;i++){
        if(sdef->elements[i].of_type){
           if(sdef->elements[i].isOptional){
             src=mputprintf(src,
              "  case %lu:\n"
              "   if(field_map[%lu]==3){\n"
              "   field_%s=OMIT_VALUE;\n"
              "   opt_field_num++;\n"
              "   }\n"
              "   break;\n"
              ,(unsigned long) i,(unsigned long) i,sdef->elements[i].name
             );
          } else {
             src=mputprintf(src,
              "  case %lu:\n"
              "   if(field_map[%lu]==3){\n"
              "   mand_field_num++;\n"
              "   }\n"
              "   break;\n"
              ,(unsigned long) i,(unsigned long) i
             );
          }
        } else if(sdef->elements[i].isOptional){
          src=mputprintf(src,
          "  case %lu:\n"
          "   field_%s=OMIT_VALUE;\n"
          "   opt_field_num++;\n"
          "   break;\n"
          ,(unsigned long) i,sdef->elements[i].name
          );
        }
      }
      src=mputstr(src,
        "  default:\n"
        "   mand_field_num++;\n"
        "   break;\n"
        "}\n"
       );
      }

      src = mputprintf(src,
      "        } else {\n"
      "      sep_length=tl;\n"
      "      decoded_length+=tl;\n"
      "      p_buf.increase_pos(tl);\n"
      "      for(int a=0;a<%lu;a++) if(field_map[a]>2) field_map[a]-=3;\n"
      "      sep_found=TRUE;}\n"
      "    } else if(p_td.text->end_decode){\n"
      "      int tl;\n"
      "      if((tl=p_td.text->end_decode->match_begin(p_buf))!=-1){\n"
      "        decoded_length+=tl;\n"
      "        p_buf.increase_pos(tl);\n"
      "        limit.remove_tokens(ml);\n"
      "        if(mand_field_num) return -1;\n"
      "        return decoded_length;\n"
      "      }\n"
      "    } else if(limit.has_token(ml)){\n"
      "      if(limit.match(p_buf,ml)==0){\n"
      "        sep_found=FALSE;\n"
      "        break;\n"
      "      }\n"
      "    }\n"
      "  }\n"
      "  limit.remove_tokens(ml);\n"
      "  if(sep_found){\n"
      "    if(mand_field_num){\n"
      "    if(no_err)return -1;\n"
      "    TTCN_EncDec_ErrorContext::error"
      "(TTCN_EncDec::ET_TOKEN_ERR, \"Error during decoding '%%s': \","
      "p_td.name);\n"
      "    return decoded_length;\n"
      "    } else {\n"
      "      decoded_length-=sep_length;\n"
      "      p_buf.set_pos(p_buf.get_pos()-sep_length);\n"
      "    }\n"
      "  }\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"
      "  if(mand_field_num) return -1;\n"
      "  return decoded_length;\n"
      "}\n"
      ,(unsigned long) sdef->nElements
       );
    } else { /* record decoder start */
      src = mputprintf(src,
      "int %s::TEXT_decode(const TTCN_Typedescriptor_t& p_td,"
      " TTCN_Buffer& p_buf, Limit_Token_List& limit, boolean no_err, boolean){\n"
      "  int decoded_length=0;\n"
      "  int decoded_field_length=0;\n"
      "%s"
      "  boolean sep_found=FALSE;\n"
      "  int sep_length=0;\n"
      "  int ml=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_td.text->end_decode){\n"
      "    limit.add_token(p_td.text->end_decode);\n"
      "    ml++;\n"
      "  }\n"
      "  if(p_td.text->separator_decode){\n"
      "    limit.add_token(p_td.text->separator_decode);\n"
      "    ml++;\n"
      "  }\n"
         ,name,opt_number?"  size_t pos=p_buf.get_pos();\n":""
       );
      for(i=0;i<sdef->nElements;i++){
        if(sdef->elements[i].isOptional){
          src=mputprintf(src,
           "    field_%s=OMIT_VALUE;\n"
          ,sdef->elements[i].name
          );
        }      }
        src=mputstr(src,
         "    while(TRUE){\n"
        );
      for(i=0;i<sdef->nElements;i++){
        if(sdef->elements[i].isOptional){
          src=mputstr(src,
           "    pos=p_buf.get_pos();\n"
          );
        }
        src=mputprintf(src,
        "    decoded_field_length=field_%s%s.TEXT_decode(%s_descr_,p_buf"
        ",limit,TRUE);\n"
        "    if(decoded_field_length<0){\n"
        ,sdef->elements[i].name,sdef->elements[i].isOptional?"()":""
        ,sdef->elements[i].typedescrname
        );
        if(sdef->elements[i].isOptional){
          src=mputprintf(src,
           "    field_%s=OMIT_VALUE;\n"
           "    p_buf.set_pos(pos);\n"
           "    } else {\n"
          ,sdef->elements[i].name
          );
        } else {
          src=mputprintf(src,
           "     limit.remove_tokens(ml);\n"
           "     if(no_err)return -1;\n"
           "     TTCN_EncDec_ErrorContext::error"
           "(TTCN_EncDec::ET_TOKEN_ERR, \"Error during decoding "
           "field '%s' for '%%s': \", p_td.name);\n"
           "     return decoded_length;\n"
           "   } else {\n"
           ,sdef->elements[i].name
          );
        }
        src=mputstr(src,
         "  decoded_length+=decoded_field_length;\n"
        );
        if(last_man_index>(i+1)){
          src=mputstr(src,
          "    if(p_td.text->separator_decode){\n"
          "      int tl;\n"
          "      if((tl=p_td.text->separator_decode->match_begin(p_buf))<0){\n"
          );

          if(sdef->elements[i].isOptional){
            src=mputprintf(src,
             "    field_%s=OMIT_VALUE;\n"
             "    p_buf.set_pos(pos);\n"
             "    decoded_length-=decoded_field_length;\n"
           ,sdef->elements[i].name
            );
          } else {
            src=mputstr(src,
             "          limit.remove_tokens(ml);\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->"
             "separator_decode),p_td.name);\n"
             "          return decoded_length;\n"
            );
          }

          src=mputstr(src,
          "      } else {\n"
          "      decoded_length+=tl;\n"
          "      p_buf.increase_pos(tl);\n"
          "      sep_length=tl;\n"
          "      sep_found=TRUE;}\n"
          "    } else sep_found=FALSE;\n"
          );
        } else if(i==(sdef->nElements-1)){
          src=mputstr(src,
          "    sep_found=FALSE;\n"
          );
        } else {
          src=mputstr(src,
          "    if(p_td.text->separator_decode){\n"
          "      int tl;\n"
          "      if((tl=p_td.text->separator_decode->match_begin(p_buf))<0){\n"
          );
          if(sdef->elements[i].isOptional){
            src=mputprintf(src,
             "      if(p_td.text->end_decode){\n"
             "      int tl;\n"
             "     if((tl=p_td.text->end_decode->match_begin(p_buf))!=-1){\n"
             "        decoded_length+=tl;\n"
             "        p_buf.increase_pos(tl);\n"
             "        limit.remove_tokens(ml);\n"
             "        return decoded_length;\n"
             "      }\n"
             "    } else if(limit.has_token(ml)){\n"
             "      if((tl=limit.match(p_buf,ml))==0){\n"
             "        sep_found=FALSE;\n"
             "        break;\n"
             "      }\n"
             "    } else break;\n"
             "    field_%s=OMIT_VALUE;\n"
             "    p_buf.set_pos(pos);\n"
             "    decoded_length-=decoded_field_length;\n"
           ,sdef->elements[i].name
            );
          } else {
            src=mputstr(src,
            "      sep_found=FALSE;\n"
            "      break;\n"
            );
          }
          src=mputstr(src,
          "      } else {\n"
          "      decoded_length+=tl;\n"
          "      p_buf.increase_pos(tl);\n"
          "      sep_length=tl;\n"
          "      sep_found=TRUE;}\n"
          "    } else {\n"
          "      sep_found=FALSE;\n"
          "      if(p_td.text->end_decode){\n"
          "      int tl;\n"
          "      if((tl=p_td.text->end_decode->match_begin(p_buf))!=-1){\n"
          "        decoded_length+=tl;\n"
          "        p_buf.increase_pos(tl);\n"
          "        limit.remove_tokens(ml);\n"
          "        return decoded_length;\n"
          "      }\n"
          "    } else if(limit.has_token(ml)){\n"
          "      if(limit.match(p_buf,ml)==0){\n"
          "        sep_found=FALSE;\n"
          "        break;\n"
          "      }\n"
          "    }\n"
          "    }\n"
          );
        }
        src=mputstr(src,
         "  }\n"
        );
      }

      src = mputstr(src,
      "   break;\n"
      "  }\n"
      "  limit.remove_tokens(ml);\n"
      "  if(sep_found){\n"
      "      p_buf.set_pos(p_buf.get_pos()-sep_length);\n"
      "      decoded_length-=sep_length;\n"
      "  }\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"
       );
    }
  } /* text_needed */

  /* RAW functions */
  if(raw_needed){
    struct raw_option_struct *raw_options;
    boolean haslengthto, haspointer, hascrosstag, has_ext_bit;
    raw_options = (struct raw_option_struct*)
      Malloc(sdef->nElements * sizeof(*raw_options));

    set_raw_options(sdef, raw_options, &haslengthto,
                    &haspointer, &hascrosstag, &has_ext_bit);

    src = generate_raw_coding(src, sdef, raw_options, haspointer, hascrosstag,
                              has_ext_bit);

    for (i = 0; i < sdef->nElements; i++) {
      Free(raw_options[i].lengthoffield);
      Free(raw_options[i].dependent_fields);
    }
    Free(raw_options);
  } /* if raw_needed */

  if (xer_needed) gen_xer(sdef, &def, &src);
  
  if (json_needed) {
    // JSON encode, RT1
    src = mputprintf(src,
      "int %s::JSON_encode(const TTCN_Typedescriptor_t&%s, JSON_Tokenizer& p_tok, boolean p_parent_is_map) const\n"
      "{\n"
      "  if (!is_bound()) {\n"
      "    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_UNBOUND,\n"
      "      \"Encoding an unbound value of type %s.\");\n"
      "    return -1;\n"
      "  }\n\n", name, 
      ((sdef->nElements == 1 && !sdef->jsonAsValue) || sdef->jsonAsMapPossible) ? " p_td" : "", dispname);
    if (sdef->nElements == 1) {
      if (!sdef->jsonAsValue) {
        src = mputstr(src, "  if (p_td.json->as_value) {\n");
      }
      if (sdef->elements[0].isOptional) {
        // can only happen if the record has the 'JSON:object' attribute;
        // in this case 'omit' is the same as if the field was an empty record of
        src = mputprintf(src,
          "   if (field_%s == OMIT_VALUE) {\n"
          "     return p_tok.put_next_token(JSON_TOKEN_OBJECT_START, NULL) + \n"
          "       p_tok.put_next_token(JSON_TOKEN_OBJECT_END, NULL);\n"
          "   }\n", sdef->elements[0].name);
      }
      src = mputprintf(src, "   return field_%s.JSON_encode(%s_descr_, p_tok, FALSE);\n",
        sdef->elements[0].name, sdef->elements[0].typedescrname);
      if (!sdef->jsonAsValue) {
        src = mputstr(src, "  }\n");
      }
    }
    if (sdef->jsonAsMapPossible) {
      src = mputprintf(src,
        "  if (p_parent_is_map) {\n"
        "    TTCN_Buffer key_buf;\n"
        "    field_%s.encode_utf8(key_buf);\n"
        "    CHARSTRING key_str;\n"
        "    key_buf.get_string(key_str);\n"
        "    return p_tok.put_next_token(JSON_TOKEN_NAME, (const char*) key_str) + \n"
        "      field_%s.JSON_encode(%s_descr_, p_tok, FALSE);\n"
        "  }\n", sdef->elements[0].name,
        sdef->elements[1].name, sdef->elements[1].typedescrname);
    }
    if (!sdef->jsonAsValue) {
      src = mputstr(src, "  int enc_len = p_tok.put_next_token(JSON_TOKEN_OBJECT_START, NULL);\n\n");
      for (i = 0; i < sdef->nElements; ++i) {
        if (sdef->elements[i].isOptional && !sdef->elements[i].jsonOmitAsNull &&
            !sdef->elements[i].jsonMetainfoUnbound) {
          src = mputprintf(src,
            "  if (field_%s.is_present())\n"
            , sdef->elements[i].name);
        }
        src = mputprintf(src,
          "  {\n"
          "    enc_len += p_tok.put_next_token(JSON_TOKEN_NAME, \"%s\");\n    "
          , sdef->elements[i].jsonAlias ? sdef->elements[i].jsonAlias : sdef->elements[i].dispname);
        if (sdef->elements[i].jsonMetainfoUnbound) {
          src = mputprintf(src,
            "if (!field_%s.is_bound()) {\n"
            "      enc_len += p_tok.put_next_token(JSON_TOKEN_LITERAL_NULL);\n"
            "      enc_len += p_tok.put_next_token(JSON_TOKEN_NAME, \"metainfo %s\");\n"
            "      enc_len += p_tok.put_next_token(JSON_TOKEN_STRING, \"\\\"unbound\\\"\");\n"
            "    }\n"
            "    else "
            , sdef->elements[i].name
            , sdef->elements[i].jsonAlias ? sdef->elements[i].jsonAlias : sdef->elements[i].dispname);
        }
        src = mputprintf(src,
          "enc_len += field_%s.JSON_encode(%s_descr_, p_tok, FALSE);\n"
          "  }\n\n"
          , sdef->elements[i].name, sdef->elements[i].typedescrname);
      }
      src = mputstr(src, 
        "  enc_len += p_tok.put_next_token(JSON_TOKEN_OBJECT_END, NULL);\n"
        "  return enc_len;\n");
    }
    src = mputstr(src, "}\n\n");
    
    // JSON decode, RT1
    src = generate_json_decoder(src, sdef);
  }
  
  if (oer_needed) {
    // OER encode, RT1
    src = mputprintf(src,
      "int %s::OER_encode(const TTCN_Typedescriptor_t&, TTCN_Buffer& p_buf) const\n"
      "{\n"
      "  if (!is_bound()) {\n" 
      "  TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_UNBOUND,\n"
      "    \"Encoding an unbound %s value.\");\n"
      "  return -1;\n"
      "  }\n", name, sdef->kind == SET ? "set" : "record");

    if (sdef->oerExtendable && sdef->oerNrOrRootcomps != sdef->nElements) {
      src = mputstr(src,
        "  boolean has_extension = FALSE;\n");
      for (i = sdef->oerNrOrRootcomps; i < sdef->nElements; i++) {
        src = mputprintf(src,
        "  has_extension = has_extension || (field_%s.is_bound() && field_%s.is_present()",
           sdef->elements[sdef->oerP[i]].name, sdef->elements[sdef->oerP[i]].name);
        if (sdef->elements[sdef->oerP[i]].isDefault) {
          src = mputprintf(src, " && field_%s != %s",
            sdef->elements[sdef->oerP[i]].name,
            sdef->elements[sdef->oerP[i]].defvalname);
        }
        src = mputstr(src, ");\n");
      }
    }
    size_t opt_elements = 0;
    size_t limit = sdef->oerExtendable ? sdef->oerNrOrRootcomps : sdef->nElements;
    for (i = 0; i < limit; i++) {
      if (sdef->elements[sdef->oerP[i]].isOptionalAsn || sdef->elements[sdef->oerP[i]].isDefault) {
        opt_elements++;
      }
    }

    int needed_bytes;

    int pos = 8;
    if (opt_elements != 0 || sdef->oerExtendable) {
      src = mputstr(src,
        "  char c = 0;\n");
      if (sdef->oerExtendable) {
        pos--;
        if (sdef->oerNrOrRootcomps != sdef->nElements) {
          src = mputprintf(src,
            "  if (has_extension) {\n"
            "    c |= %i;\n"
            "  }\n", 1 << 7);
        }
      }
    }
    
    int ind = 0;
    for (i = 0; i < limit; i++) {
      if (sdef->elements[sdef->oerP[i]].isOptionalAsn || sdef->elements[sdef->oerP[i]].isDefault) {
        pos--;
        src = mputprintf(src,
          "  if (field_%s.is_present()", sdef->elements[sdef->oerP[i]].name);
        if (sdef->elements[sdef->oerP[i]].isDefault) {
          src = mputprintf(src, " && field_%s != %s",
            sdef->elements[sdef->oerP[i]].name,
            sdef->elements[sdef->oerP[i]].defvalname);
        }
        src = mputprintf(src,
          ") {\n"
          "    c |= %i;\n"
          "  }\n"
          , 1 << pos);
        if (pos == 0) {
          pos = 8;
          ind++;
          src = mputstr(src,
            "  p_buf.put_c(c);\n"
            "  c = 0\n;");
        }
      }
    }
    if (opt_elements != 0 || sdef->oerExtendable) {
      if (pos != 8 || ind == 0) {
        src = mputstr(src,
          "  p_buf.put_c(c);\n");
      }
    }
    for (i = 0; i < limit; i++) {
      if (sdef->elements[sdef->oerP[i]].isOptionalAsn || sdef->elements[sdef->oerP[i]].isDefault) {
        src = mputprintf(src,
          "  if (field_%s.is_present()", sdef->elements[sdef->oerP[i]].name);
        if (sdef->elements[sdef->oerP[i]].isDefault) {
          src = mputprintf(src, " && field_%s != %s",
            sdef->elements[sdef->oerP[i]].name,
            sdef->elements[sdef->oerP[i]].defvalname);
        }
        src = mputstr(src, ")\n  ");
      }
      src = mputprintf(src,
        "  field_%s.OER_encode(%s_descr_, p_buf);\n"
        , sdef->elements[sdef->oerP[i]].name, sdef->elements[sdef->oerP[i]].typedescrname);
    }

    if (sdef->oerExtendable && sdef->oerNrOrRootcomps != sdef->nElements) {
      int bits = 0;
      int eag_pos = 0;
      for (i = limit; i < sdef->nElements; i++) {
        if (sdef->oerEagNum != 0 && sdef->oerEag[eag_pos] == i - limit) {
          eag_pos++;
          bits++;
          i += sdef->oerEag[eag_pos] - sdef->oerEag[eag_pos-1] - 1;
          eag_pos++;
        } else {
          bits++;
        }
      }
      needed_bytes = bits / 8 + (bits % 8 == 0 ? 0 : 1);
      src = mputprintf(src,
        "  if (has_extension) {\n"
        "%s"
        "    TTCN_Buffer tmp_buf;\n"
        "    TTCN_Buffer tmp_buf2;\n"
        "    TTCN_Buffer tmp_buf3;\n"
        "    c = 0;\n"
        , sdef->oerEagNum != 0 ? "    boolean has_present = FALSE;\n" : "");
      eag_pos = 0;
      pos = 8;
      ind = 0;
      for (i = limit; i < sdef->nElements; i++) {
        pos--;
        if (sdef->oerEagNum != 0 && sdef->oerEag[eag_pos] == i - limit) {
          eag_pos++;
          int opt_elems = 0;
          for (int j = i; j < limit + sdef->oerEag[eag_pos]; j++) {
            if (sdef->elements[sdef->oerP[j]].isOptionalAsn || sdef->elements[sdef->oerP[j]].isDefault) {
              opt_elems++;
            }
            src = mputprintf(src,
              "    has_present = has_present || (field_%s.is_present()", sdef->elements[sdef->oerP[j]].name);
            if (sdef->elements[sdef->oerP[j]].isDefault) {
              src = mputprintf(src, " && field_%s != %s",
                sdef->elements[sdef->oerP[j]].name,
                sdef->elements[sdef->oerP[j]].defvalname);
            }
            src = mputstr(src, ");\n");
          }
          src = mputprintf(src,
            "    if (has_present) {\n"
            "      has_present = FALSE;\n"
            "      c |= %i;\n", 1 << pos);
          if (opt_elems != 0) {
            src = mputstr(src,
              "      char c2 = 0;\n");
            int pos2 = 8;
            int ind2 = 0;
            for (int j = i; j < limit + sdef->oerEag[eag_pos]; j++) {
              if (sdef->elements[sdef->oerP[j]].isOptionalAsn || sdef->elements[sdef->oerP[j]].isDefault) {
                pos2--;
                src = mputprintf(src,
                  "      if (field_%s.is_present()",
                  sdef->elements[sdef->oerP[j]].name);
                if (sdef->elements[sdef->oerP[j]].isDefault) {
                  src = mputprintf(src, " && field_%s != %s",
                    sdef->elements[sdef->oerP[j]].name,
                    sdef->elements[sdef->oerP[j]].defvalname);
                }
                src = mputprintf(src,
                  ") {\n"
                  "        c2 |= %i;\n"
                  "      }\n"
                  , 1 << pos2);
                if (pos2 == 0) {
                  pos2 = 8;
                  ind2++;
                  src = mputstr(src,
                    "      tmp_buf2.put_c(c2);\n"
                    "      c2 = 0;\n");
                }
              }
            }
            if (pos2 != 8) {
              src = mputstr(src,
                "      tmp_buf2.put_c(c2);\n");
            }
          }
          for (int j = i; j < limit + sdef->oerEag[eag_pos]; j++) {
            src = mputprintf(src,
              "      field_%s.OER_encode(%s_descr_, tmp_buf2);\n"
              , sdef->elements[sdef->oerP[j]].name, sdef->elements[sdef->oerP[j]].typedescrname);
          }
          src = mputstr(src,
            "      encode_oer_length(tmp_buf2.get_len(), tmp_buf, FALSE);\n"
            "      tmp_buf.put_buf(tmp_buf2);\n"
            "      tmp_buf2.clear();\n"
            "    }\n");
          i += sdef->oerEag[eag_pos] - sdef->oerEag[eag_pos-1] - 1;
          eag_pos++;
        } else {
          src = mputprintf(src,
            "    if (field_%s.is_present()", sdef->elements[sdef->oerP[i]].name);
          if (sdef->elements[sdef->oerP[i]].isDefault) {
            src = mputprintf(src, " && field_%s != %s",
              sdef->elements[sdef->oerP[i]].name,
              sdef->elements[sdef->oerP[i]].defvalname);
          }
          src = mputprintf(src,
            ") {\n"
            "      c |= %i;\n"
            "      field_%s.OER_encode(%s_descr_, tmp_buf2);\n"
            "      encode_oer_length(tmp_buf2.get_len(), tmp_buf, FALSE);\n"
            "      tmp_buf.put_buf(tmp_buf2);\n"
            "      tmp_buf2.clear();\n"
            "    }\n"
             , 1 << pos
             , sdef->elements[sdef->oerP[i]].name, sdef->elements[sdef->oerP[i]].typedescrname);
        }
        if (pos == 0) {
          ind++;
          pos = 8;
          src = mputstr(src,
            "    tmp_buf3.put_c(c);\n"
            "    c = 0;\n");
        }
      }
      if (pos != 8) {
        src = mputstr(src,
            "    tmp_buf3.put_c(c);\n");
      }
      src = mputprintf(src,
        "    encode_oer_length(%i, p_buf, FALSE);\n"
        "    p_buf.put_c(%i);\n"
        "    p_buf.put_buf(tmp_buf3);\n"
        "    p_buf.put_buf(tmp_buf);\n"
        "    tmp_buf.clear();\n"
        "  }\n", needed_bytes+1, pos);
    }
    src = mputstr(src,
      "  return 0;\n"
      "}\n\n");
    
    // OER decode, RT1
    src = mputprintf(src,
      "int %s::OER_decode(const TTCN_Typedescriptor_t&, TTCN_Buffer& p_buf, OER_struct& p_oer)\n"
      "{\n", name);
    int bits = opt_elements+(sdef->oerExtendable == TRUE);
    needed_bytes = bits / 8 + (bits % 8 == 0 ? 0 : 1);
    if (needed_bytes != 0) {
      src = mputprintf(src, 
        "  const unsigned char* uc = p_buf.get_read_data();\n"
        "  p_buf.increase_pos(%i);\n"
        , needed_bytes);
    }
    ind = 0;
    pos = 8;
    if (sdef->oerExtendable) {
      src = mputstr(src,
        "  boolean has_extension = FALSE;\n"
        "  if (uc[0] & 0x80) {\n"
        "    has_extension = TRUE;\n"
        "  }\n");
      pos--;
    }
    for (i = 0; i < limit; i++) {
      if (sdef->elements[sdef->oerP[i]].isOptionalAsn || sdef->elements[sdef->oerP[i]].isDefault) {
        pos--;
          src = mputprintf(src,
            "  if (uc[%i] & %i)\n"
            "    field_%s.OER_decode(%s_descr_, p_buf, p_oer);\n"
            , ind, 1 << pos, sdef->elements[i].name
            , sdef->elements[sdef->oerP[i]].typedescrname);
          if (sdef->elements[sdef->oerP[i]].isOptionalAsn ||
              sdef->elements[sdef->oerP[i]].isDefault) {
            src = mputprintf(src, " else\n"
              "    field_%s = %s;\n"
              , sdef->elements[sdef->oerP[i]].name
              , sdef->elements[sdef->oerP[i]].isOptionalAsn ? "OMIT_VALUE"
              : sdef->elements[sdef->oerP[i]].defvalname);
          }
        if (pos == 0) {
          ind++;
          pos = 8;
        }
      } else {
        src = mputprintf(src,
          "  field_%s.OER_decode(%s_descr_, p_buf, p_oer);\n"
          , sdef->elements[sdef->oerP[i]].name
          , sdef->elements[sdef->oerP[i]].typedescrname);
      }
    }
    
    if (sdef->oerExtendable) {
      src = mputstr(src,
        "  if (has_extension) {\n"
        "    size_t bytes = decode_oer_length(p_buf, FALSE);\n"
        "    uc = p_buf.get_read_data();\n"
        "    p_buf.increase_pos(bytes);\n"
        "    uc++;\n");
      int eag_pos = 0;
      pos = 8;
      ind = 0;
      boolean has_optional_in_extension = FALSE;
      for (i = limit; i < sdef->nElements; i++) {
        pos--;
        src = mputprintf(src,
          "    if (!(uc[%i] & %i)) {\n"
          , ind, 1 << pos);
        if (sdef->oerEagNum != 0 && sdef->oerEag[eag_pos] == i - limit) {
          eag_pos++;
          for (int j = i; j < limit + sdef->oerEag[eag_pos]; j++) {
            if (sdef->elements[sdef->oerP[j]].isOptionalAsn ||
                sdef->elements[sdef->oerP[j]].isDefault) {
              has_optional_in_extension = TRUE;
              src = mputprintf(src,
                "      field_%s = %s;\n"
                , sdef->elements[sdef->oerP[j]].name
                , sdef->elements[sdef->oerP[j]].isOptionalAsn ? "OMIT_VALUE"
                : sdef->elements[sdef->oerP[j]].defvalname);
            }
          }
          eag_pos--;
        } else {
          if (sdef->elements[sdef->oerP[i]].isOptionalAsn ||
              sdef->elements[sdef->oerP[i]].isDefault) {
            has_optional_in_extension = TRUE;
            src = mputprintf(src,
              "      field_%s = %s;\n"
              , sdef->elements[sdef->oerP[i]].name
              , sdef->elements[sdef->oerP[i]].isOptionalAsn ? "OMIT_VALUE"
              : sdef->elements[sdef->oerP[i]].defvalname);
          }
        }
        src = mputstr(src,
          "    } else {\n"
          "      decode_oer_length(p_buf, FALSE);\n");
        if (sdef->oerEagNum != 0 && sdef->oerEag[eag_pos] == i - limit) {
          eag_pos++;
          int nof_opt = 0;
          for (int j = i; j < limit + sdef->oerEag[eag_pos]; j++) {
            if (sdef->elements[sdef->oerP[j]].isOptionalAsn || sdef->elements[sdef->oerP[j]].isDefault) {
              nof_opt++;
            }
          }
          needed_bytes = nof_opt / 8 + (nof_opt % 8 == 0 ? 0 : 1);
          src = mputprintf(src,
            "      const unsigned char* uc2 = p_buf.get_read_data();\n"
            "      (void)uc2;\n"
            "      p_buf.increase_pos(%i);\n"
            , needed_bytes);
          int pos2 = 8;
          int ind2 = 0;
          for (int j = i; j < limit + sdef->oerEag[eag_pos]; j++) {
            if (sdef->elements[sdef->oerP[j]].isOptionalAsn || sdef->elements[sdef->oerP[j]].isDefault) {
              pos2--;
              src = mputprintf(src,
                "      if (uc2[%i] & %i) {\n"
                "        field_%s.OER_decode(%s_descr_, p_buf, p_oer);\n"
                "      }\n"
                , ind2, 1 << pos2, sdef->elements[sdef->oerP[j]].name, sdef->elements[sdef->oerP[j]].typedescrname);
              if (sdef->elements[sdef->oerP[j]].isOptionalAsn ||
                  sdef->elements[sdef->oerP[j]].isDefault) {
                src = mputprintf(src,
                  "        else {\n"
                  "          field_%s = %s;\n"
                  "        }\n"
                  , sdef->elements[sdef->oerP[j]].name
                  , sdef->elements[sdef->oerP[j]].isOptionalAsn ? "OMIT_VALUE"
                  : sdef->elements[sdef->oerP[j]].defvalname);
              }
              if (pos2 == 0) {
                pos2 = 8;
                ind2++;
              }
            } else {
              src = mputprintf(src,
                "      field_%s.OER_decode(%s_descr_, p_buf, p_oer);\n"
                , sdef->elements[sdef->oerP[j]].name, sdef->elements[sdef->oerP[j]].typedescrname);
            }
          }
          i += sdef->oerEag[eag_pos] - sdef->oerEag[eag_pos-1] - 1;
          eag_pos++;
        } else {
          src = mputprintf(src,
            "      field_%s.OER_decode(%s_descr_, p_buf, p_oer);\n"
            , sdef->elements[sdef->oerP[i]].name, sdef->elements[sdef->oerP[i]].typedescrname);
        }
        src = mputstr(src, "    }\n");
        if (pos == 0) {
          pos = 8;
          ind++;
        }
      }
      if (has_optional_in_extension) {
        src = mputstr(src,
          "  }\n"
          "  else {\n");
        for (i = limit; i < sdef->nElements; i++) {
          if (sdef->elements[sdef->oerP[i]].isOptionalAsn ||
              sdef->elements[sdef->oerP[i]].isDefault) {
            src = mputprintf(src,
              "    field_%s = %s;\n"
              , sdef->elements[sdef->oerP[i]].name
              , sdef->elements[sdef->oerP[i]].isOptionalAsn ? "OMIT_VALUE"
              : sdef->elements[sdef->oerP[i]].defvalname);
          }
        }
      }
      src = mputstr(src,
        "  }\n");
    }
    
    if (sdef->opentype_outermost) {
      src = mputstr(src,
        "  TTCN_EncDec_ErrorContext ec_1(\"While decoding opentypes: \");"
        "  TTCN_Type_list p_typelist;\n"
        "  OER_decode_opentypes(p_typelist, p_buf, p_oer);\n");
    }
    src = mputstr(src, 
      "  return 0;\n"
      "}\n\n");
  }
  
  if(sdef->has_opentypes) {
    /* OER_decode_opentypes() */
    def=mputstr
      (def,
       "void OER_decode_opentypes(TTCN_Type_list& p_typelist, TTCN_Buffer& p_buf, OER_struct& p_oer);\n");
    src=mputprintf
      (src,
       "void %s::OER_decode_opentypes(TTCN_Type_list& p_typelist, TTCN_Buffer& p_buf, OER_struct& p_oer)\n"
       "{\n"
       "  p_typelist.push(this);\n"
       "  TTCN_EncDec_ErrorContext ec_0(\"Component '\");\n"
       "  TTCN_EncDec_ErrorContext ec_1;\n"
       , name
       );
    for(i=0; i<sdef->nElements; i++) {
      src=mputprintf
        (src,
         "  ec_1.set_msg(\"%s': \");\n"
         "  field_%s.OER_decode_opentypes(p_typelist, p_buf, p_oer);\n"
         , sdef->elements[i].dispname
         , sdef->elements[i].name
         );
    } /* for i */
    src=mputstr
      (src,
       "  p_typelist.pop();\n"
       "}\n"
       "\n"
       );
    } /* if sdef->has_opentypes */

  if (per_needed) {
    // PER encode, RT1
    src = mputprintf(src,
      "void %s::PER_encode(const TTCN_Typedescriptor_t& p_td, TTCN_Buffer& p_buf, int p_options) const\n"
      "{\n"
      "if (!is_bound()) {\n" 
      "TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_UNBOUND, "
      "\"Encoding an unbound %s value.\");\n"
      "return;\n"
      "}\n", name, sdef->kind == SET ? "SET" : "SEQUENCE");
    if (sdef->perExtendable) {
      // extension bit (also calculating the presence of extension additions)
      char* ext_bit_str = mprintf("boolean ext_bit = ");
      if (sdef->perExtAddCount != 0) {
        for (i = 0; i < sdef->perExtAddCount; ++i) {
          int ext_start_idx = sdef->perExtAddIndexes[i].start_idx;
          if (!sdef->perExtAddIndexes[i].is_group) {
            // single extension addition
            src = mputprintf(src, "boolean ext_%d_present = field_%s.is_present()",
              (int)i, sdef->elements[sdef->perFieldOrder[ext_start_idx]].name);
            if (sdef->elements[sdef->perFieldOrder[ext_start_idx]].isDefault) {
              src = mputprintf(src, " && field_%s != %s",
              sdef->elements[sdef->perFieldOrder[ext_start_idx]].name,
              sdef->elements[sdef->perFieldOrder[ext_start_idx]].defvalname);
            }
            src = mputstr(src, ";\n");
          }
          else {
            // extension addition group
            int ext_end_idx = (i == sdef->perExtAddCount - 1) ? sdef->nElements : sdef->perExtAddIndexes[i + 1].start_idx;
            char* ext_add_present_str = mprintf("boolean ext_%d_present = ", (int)i);
            for (int j = ext_start_idx; j < ext_end_idx; ++j) {
              src = mputprintf(src, "boolean field_%s_present = field_%s.is_present()",
                sdef->elements[sdef->perFieldOrder[j]].name,
                sdef->elements[sdef->perFieldOrder[j]].name);
              if (sdef->elements[sdef->perFieldOrder[j]].isDefault) {
                src = mputprintf(src, " && field_%s != %s",
                sdef->elements[sdef->perFieldOrder[j]].name,
                sdef->elements[sdef->perFieldOrder[j]].defvalname);
              }
              src = mputstr(src, ";\n");
              if (j != ext_start_idx) {
                ext_add_present_str = mputstr(ext_add_present_str, " || ");
              }
              ext_add_present_str = mputprintf(ext_add_present_str, "field_%s_present",
                sdef->elements[sdef->perFieldOrder[j]].name);
            }
            src = mputprintf(src, "%s;\n", ext_add_present_str);
            Free(ext_add_present_str);
          }
          if (i != 0) {
            ext_bit_str = mputstr(ext_bit_str, " || ");
          }
          ext_bit_str = mputprintf(ext_bit_str, "ext_%d_present", (int)i);
        }
      }
      else {
        ext_bit_str = mputstr(ext_bit_str, "FALSE");
      }
      src = mputprintf(src,
        "%s;\n"
        "p_buf.PER_put_bit(ext_bit);\n",
        ext_bit_str);
      Free(ext_bit_str);
    }

   
    int root_end_idx = sdef->perExtAddIndexes != NULL ? sdef->perExtAddIndexes[0].start_idx : sdef->nElements;
    if (root_end_idx != 0) {
      src = generate_per_encode_fields(src, sdef, 0, root_end_idx, FALSE);
    }

    if (sdef->perExtAddCount != 0) {
      // presence bits for extension additions
      int len = sdef->perExtAddCount;
      src = mputprintf(src,
        "if (ext_bit) {\n"
        "INTEGER length_(%d);\n"
        "length_.PER_encode_length(p_buf, p_options, TRUE);\n",
        len);
      int mul_16k = (len < PER_16K * 4) ? len / PER_16K : 4;
      int encoded_bits = (mul_16k == 0) ? len : (mul_16k * PER_16K);
      len -= encoded_bits;
      for (i = 0; i < sdef->perExtAddCount; ++i) {
        src = mputprintf(src, "p_buf.PER_put_bit(ext_%d_present);\n", (int)i);
        --encoded_bits;
        if (encoded_bits == 0 && len != 0) {
          src = mputprintf(src,
            "length_ = length_ - PER_16K * %d;\n"
            "length_.PER_encode_length(p_buf, p_options, FALSE);\n", mul_16k);
          mul_16k = (len < PER_16K * 4) ? len / PER_16K : 4;
          encoded_bits = (mul_16k == 0) ? len : (mul_16k * PER_16K);
          len -= encoded_bits;
        }
      }

      // encoding of extension addition fields
      for (i = 0; i < sdef->perExtAddCount; ++i) {
        src = mputprintf(src,
          "if (ext_%d_present) {\n"
          "TTCN_Buffer ext_add_buf;\n", (int)i);
        int ext_start_idx = sdef->perExtAddIndexes[i].start_idx;
        if (!sdef->perExtAddIndexes[i].is_group) {
          // single extension addition
          src = mputprintf(src,
            "field_%s.PER_encode(%s_descr_, ext_add_buf, p_options);\n",
            sdef->elements[sdef->perFieldOrder[ext_start_idx]].name,
            sdef->elements[sdef->perFieldOrder[ext_start_idx]].typedescrname);
        }
        else {
          // extension addition group -> encode as if it was a SEQUENCE with no extensions
          int ext_end_idx = (i == sdef->perExtAddCount - 1) ? sdef->nElements : sdef->perExtAddIndexes[i + 1].start_idx;
          src = generate_per_encode_fields(src, sdef, ext_start_idx, ext_end_idx, TRUE);
        }
        src = mputstr(src,
          "PER_encode_opentype(p_buf, ext_add_buf, p_options);\n"
          "}\n");
      }

      src = mputstr(src, "}\n"); // end of 'if (ext_bit)'
    }

    src = mputstr(src, "}\n\n"); // end of function

    // PER decode, RT1
    src = mputprintf(src,
      "void %s::PER_decode(const TTCN_Typedescriptor_t& p_td, TTCN_Buffer& p_buf, int p_options)\n"
      "{\n"
      "clean_up();\n", name);

    if (sdef->perExtendable) {
      src = mputstr(src, "boolean ext_bit = p_buf.PER_get_bit();\n");
    }

    if (root_end_idx != 0) {
      src = generate_per_decode_fields(src, sdef, 0, root_end_idx, FALSE);
    }

    if (sdef->perExtendable) {
      // presence bits for extension additions
      src = mputstr(src,
        "Vector<boolean> ext_presences;\n"
        "if (ext_bit) {\n"
        "INTEGER length_;\n"
        "int normally_small_length = TRUE;\n"
        "int mul_16k;\n"
        "do {\n"
        "mul_16k = length_.PER_decode_length(p_buf, p_options, normally_small_length);\n"
        "if (mul_16k > 0) normally_small_length = FALSE;\n"
        "for (int i = 0; i < length_; ++i) {\n"
        "ext_presences.push_back(p_buf.PER_get_bit());\n"
        "}\n"
        "}\n"
        "while (mul_16k > 0);\n"
        "}\n" // if (ext_bit)
        "int nof_decoded_ext_adds = ext_presences.size();\n"); // will be zero if ext_bit is not set
      if (sdef->perExtAddCount != 0) {
        // if there are fewer presence bits in the buffer than there are extension additions in this seq/set,
        // then the remaining extension additions are not present
        src = mputprintf(src,
          "if (nof_decoded_ext_adds < %d) {\n"
          "for (int i = nof_decoded_ext_adds; i < %d; ++i) {\n"
          "ext_presences.push_back(FALSE);\n"
          "}\n"
          "}\n", sdef->perExtAddCount, sdef->perExtAddCount);
      }

      // decoding of extension addition fields
      for (i = 0; i < sdef->perExtAddCount; ++i) {
        src = mputprintf(src,
          "if (ext_presences[%d]) {\n"
          "TTCN_Buffer ext_add_buf;\n"
          "PER_decode_opentype(p_buf, ext_add_buf, p_options);\n", (int)i);
        int ext_start_idx = sdef->perExtAddIndexes[i].start_idx;
        int ext_end_idx = (i == sdef->perExtAddCount - 1) ? sdef->nElements : sdef->perExtAddIndexes[i + 1].start_idx;
        if (!sdef->perExtAddIndexes[i].is_group) {
          // single extension addition
          src = mputprintf(src,
            "field_%s.PER_decode(%s_descr_, ext_add_buf, p_options);\n",
            sdef->elements[sdef->perFieldOrder[ext_start_idx]].name,
            sdef->elements[sdef->perFieldOrder[ext_start_idx]].typedescrname);
        }
        else {
          // extension addition group -> decode as if it was a SEQUENCE with no extensions
          src = generate_per_decode_fields(src, sdef, ext_start_idx, ext_end_idx, TRUE);
        }
        // if the extension is not present -> set any optional or default fields to omit or to their default values
        // (non-optional and non-default extension addition fields will remain unbound)
        char* else_str = NULL;
        for (int j = ext_start_idx; j < ext_end_idx; ++j) {
          if (sdef->elements[sdef->perFieldOrder[j]].isOptionalAsn ||
              sdef->elements[sdef->perFieldOrder[j]].isDefault) {
            else_str = mputprintf(else_str, "field_%s = %s;\n",
              sdef->elements[sdef->perFieldOrder[j]].name,
              sdef->elements[sdef->perFieldOrder[j]].isOptionalAsn ? "OMIT_VALUE" :
                sdef->elements[sdef->perFieldOrder[j]].defvalname);
          }
        }
        if (else_str != NULL) {
          src = mputprintf(src,
            "}\n"
            "else {\n"
            "%s", else_str);
          Free(else_str);
        }
        src = mputstr(src, "}\n"); // end of 'if (ext_presence)' or its else branch
      }

      // jump over any extra extension additions
      if (sdef->perExtAddCount > 1) {
        src = mputprintf(src, "if (nof_decoded_ext_adds > %d) ", sdef->perExtAddCount);
      }
      src = mputprintf(src,
        "for (int i = %d; i < nof_decoded_ext_adds; ++i) {\n"
        "if (ext_presences[i]) PER_skip_opentype(p_buf, p_options);\n"
        "}\n", sdef->perExtAddCount);
    }
    
    if (sdef->opentype_outermost) {
      src = mputstr(src,
        "TTCN_EncDec_ErrorContext ec_1(\"While decoding opentypes: \");"
        "TTCN_Type_list p_typelist;\n"
        "PER_decode_opentypes(p_typelist, p_options);\n");
    }

    src = mputstr(src, "}\n\n"); // end of function

    if (sdef->has_opentypes) {
      /* PER_decode_opentypes() */
      def = mputstr(def,
        "void PER_decode_opentypes(TTCN_Type_list& p_typelist, int p_options);\n");
      src = mputprintf(src,
        "void %s::PER_decode_opentypes(TTCN_Type_list& p_typelist, int p_options)\n"
        "{\n"
        "p_typelist.push(this);\n"
        "TTCN_EncDec_ErrorContext ec_0(\"Component '\");\n"
        "TTCN_EncDec_ErrorContext ec_1;\n", name);
      for (i = 0; i < sdef->nElements; ++i) {
        src = mputprintf(src,
          "ec_1.set_msg(\"%s': \");\n"
          "field_%s.PER_decode_opentypes(p_typelist, p_options);\n",
          sdef->elements[i].dispname, sdef->elements[i].name);
      }
      src = mputstr(src,
        "p_typelist.pop();\n"
        "}\n"
        "\n");
    } /* if sdef->has_opentypes */
  }
  
  /* end of class definition */
  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);
}


static char *genRawFieldDecodeLimit(char *src, const struct_def *sdef,
  int i, const struct raw_option_struct *raw_options)
{
  /* number of arguments passed to min_of_ints() */
  int nof_args = 1;
  int j;
  for (j = 0; j < raw_options[i].lengthof; j++) {
    int field_index = raw_options[i].lengthoffield[j];
    if (i > field_index && sdef->elements[field_index].raw.unit!=-1)
      nof_args++;
  }
  if (raw_options[i].extbitgroup && sdef->raw.ext_bit_groups[
      raw_options[i].extbitgroup-1].ext_bit != XDEFNO) nof_args++;
  if (nof_args > 1) {
    src = mputprintf(src, "min_of_ints(%d, limit", nof_args);
    for (j = 0; j < raw_options[i].lengthof; j++) {
      int field_index = raw_options[i].lengthoffield[j];
      if (i > field_index && sdef->elements[field_index].raw.unit != -1)
	src = mputprintf(src, ", value_of_length_field%d", field_index);
    }
    if (raw_options[i].extbitgroup && sdef->raw.ext_bit_groups[
	raw_options[i].extbitgroup-1].ext_bit != XDEFNO)
      src = mputstr(src, ", group_limit");
    src = mputc(src, ')');
  } else {
    src = mputstr(src, "limit");
  }
  return src;
}

static char *genRawDecodeRecordField(char *src, const struct_def *sdef,
  int i, const struct raw_option_struct *raw_options, boolean delayed_decode,
  int *prev_ext_group)
{
  int a;
  if(raw_options[i].ptrbase)
    src=mputprintf(src,
    "  start_pos_of_field%d=p_buf.get_pos_bit();\n"
    ,i
    );
  if (*prev_ext_group != raw_options[i].extbitgroup) {
    *prev_ext_group = raw_options[i].extbitgroup;
    if(prev_ext_group &&
          sdef->raw.ext_bit_groups[raw_options[i].extbitgroup-1].ext_bit!=XDEFNO){
       if(raw_options[i].pointerof)  /* pointed field */
         src=mputprintf(src,
         "  {size_t old_pos=p_buf.get_pos_bit();\n"
         "  if(start_of_field%d!=-1 && start_pos_of_field%d!=-1){\n"
         "  start_of_field%d=start_pos_of_field%d"
         "+(int)field_%s%s*%d+%d;\n"
         "  p_buf.set_pos_bit(start_of_field%d);\n"
         "  limit=end_of_available_data-start_of_field%d;\n"
         ,i
         ,sdef->elements[raw_options[i].pointerof-1].raw.pointerbase
         ,i
         ,sdef->elements[raw_options[i].pointerof-1].raw.pointerbase
         ,sdef->elements[raw_options[i].pointerof-1].name
         ,sdef->elements[raw_options[i].pointerof-1].isOptional?"()":""
         ,sdef->elements[raw_options[i].pointerof-1].raw.unit
         ,sdef->elements[raw_options[i].pointerof-1].raw.ptroffset
          ,i,i
         );
       src=mputprintf(src,
         "  {\n"
         "  cbyte* data=p_buf.get_read_data();\n"
         "  int count=1;\n"
         "  int rot=local_top_order==ORDER_LSB?0:7;\n"
         "    while(((data[count-1]>>rot)&0x01)==%d && count*8<"
         "(int)limit) count++;\n"
          "  if(limit) group_limit=count*8;\n"
         "  }\n"
       ,sdef->raw.ext_bit_groups[raw_options[i].extbitgroup-1].ext_bit==
                                                               XDEFYES?0:1
       );
       if(raw_options[i].pointerof)  /* pointed field */
         src=mputstr(src,
         "  } else group_limit=0;\n"
         "  p_buf.set_pos_bit(old_pos);\n"
         "  limit=end_of_available_data-old_pos;}\n"
         );
    }
  }
  if(sdef->elements[i].hasRaw &&  /* check the crosstag */
                            sdef->elements[i].raw.crosstaglist.nElements){
    /* field index of the otherwise rule */
    int other = -1;
    boolean first_value = TRUE;
    boolean if_added = FALSE;
    int j;
    for (j = 0; j < sdef->elements[i].raw.crosstaglist.nElements; j++) {
      rawAST_coding_taglist* cur_choice =
	sdef->elements[i].raw.crosstaglist.list + j;
      if (cur_choice->nElements > 0) {
        if_added = TRUE;
        /* this is a normal rule */
	if (first_value) {
	  src = mputstr(src, "  if (");
	  first_value = FALSE;
	} else src = mputstr(src, "  else if (");
	src = genRawFieldChecker(src, cur_choice, TRUE);
	/* set selected_field in the if's body */
	src = mputprintf(src, ") selected_field = %d;\n",
	  cur_choice->fieldnum);
      } else {
        /* this is an otherwise rule */
	other = cur_choice->fieldnum;
      }
    }
    /* set selected_field to the field index of the otherwise rule or -1 */
    src = mputprintf(src, "  %s selected_field = %d;\n", if_added?"else":"", other);
  }
  /* check the presence of optional field*/
  if(sdef->elements[i].isOptional){
    /* check if enough bits to decode the field*/
    src = mputstr(src, "  if (limit > 0");
    for (a = 0; a < raw_options[i].lengthof; a++){
      int field_index = raw_options[i].lengthoffield[a];
      if (i > field_index) src = mputprintf(src,
	  " && value_of_length_field%d > 0", field_index);
    }
    if (raw_options[i].extbitgroup &&
        sdef->raw.ext_bit_groups[raw_options[i].extbitgroup-1].ext_bit!=XDEFNO){
      src = mputstr(src, " && group_limit > 0");
    }
    if(raw_options[i].pointerof){ /* valid pointer?*/
      src=mputprintf(src,
      " && start_of_field%d!=-1 && start_pos_of_field%d!=-1"
      ,i,sdef->elements[raw_options[i].pointerof-1].raw.pointerbase
      );
    }
    if(sdef->elements[i].hasRaw &&
                                 sdef->elements[i].raw.presence.nElements)
    {
      src = mputstr(src, " && ");
      if (sdef->elements[i].raw.presence.nElements > 1) src = mputc(src, '(');
      src = genRawFieldChecker(src, &sdef->elements[i].raw.presence, TRUE);
      if (sdef->elements[i].raw.presence.nElements > 1) src = mputc(src, ')');
    }
    if(sdef->elements[i].hasRaw &&
                             sdef->elements[i].raw.crosstaglist.nElements)
    {
      src=mputstr(src,
      "&& selected_field!=-1"
      );
    }
    src=mputstr(src,
    "){\n"
    );
  }
  if(raw_options[i].pointerof){  /* pointed field */
   src=mputprintf(src,
     "  start_of_field%d=start_pos_of_field%d"
     "+(int)field_%s%s*%d+%d;\n"
     ,i
     ,sdef->elements[raw_options[i].pointerof-1].raw.pointerbase
     ,sdef->elements[raw_options[i].pointerof-1].name
     ,sdef->elements[raw_options[i].pointerof-1].isOptional?"()":""
     ,sdef->elements[raw_options[i].pointerof-1].raw.unit
     ,sdef->elements[raw_options[i].pointerof-1].raw.ptroffset
     );
    src=mputprintf(src,
    "  p_buf.set_pos_bit(start_of_field%d);\n"
    "  limit=end_of_available_data-start_of_field%d;\n"
    ,i,i
    );
  }
  /* decoding of normal field */
  if (sdef->elements[i].isOptional) {
    src = mputprintf(src,
      "  if (force_omit != NULL && (*force_omit)(%d)) {\n"
      "    field_%s = OMIT_VALUE;\n"
      "  }\n"
      "  else {\n"
      "  size_t fl_start_pos = p_buf.get_pos_bit();\n",
      i, sdef->elements[i].name);
  }
  src = mputprintf(src,
    "  RAW_Force_Omit field_%d_force_omit(%d, force_omit, "
    "%s_descr_.raw->forceomit);\n"
    "  decoded_field_length = field_%s%s.RAW_decode(%s_descr_, p_buf, ",
    i, i, sdef->elements[i].typedescrname,
    sdef->elements[i].name, sdef->elements[i].isOptional ? "()" : "",
    sdef->elements[i].typedescrname);
  if (delayed_decode) {
    /* the fixed field length is used as limit in case of delayed decoding */
    src = mputprintf(src, "%d", sdef->elements[i].raw.length);
  } else {
    src = genRawFieldDecodeLimit(src, sdef, i, raw_options);
  }
  src = mputprintf(src, ", local_top_order, %s",
    sdef->elements[i].isOptional ? "TRUE" : "no_err");
  boolean lengthof_or_crostag_found = FALSE;
  if (sdef->elements[i].hasRaw &&
      sdef->elements[i].raw.crosstaglist.nElements > 0){
    src = mputstr(src, ", selected_field");
    lengthof_or_crostag_found = TRUE;
  }
  else {
    for (a = 0; a < raw_options[i].lengthof; a++) {
      int field_index = raw_options[i].lengthoffield[a];
      /* number of elements in 'record of' or 'set of' */
      if (sdef->elements[field_index].raw.unit == -1) {
        src = mputprintf(src, ", value_of_length_field%d", field_index);
        lengthof_or_crostag_found = TRUE;
        break;
      }
    }
  }
  if (!lengthof_or_crostag_found) {
    src = mputstr(src, ", -1");
  }

  src = mputstr(src, ", TRUE");
  src = mputprintf(src, ", &field_%d_force_omit);\n", i);
  if (delayed_decode) {
    src = mputprintf(src, "  if (decoded_field_length != %d) return -1;\n",
      sdef->elements[i].raw.length);
  } else if (sdef->elements[i].isOptional) {
    src = mputprintf(src, "  if (decoded_field_length < 1) {\n"
      "  field_%s = OMIT_VALUE;\n" /* transform errors into omit */
      "  p_buf.set_pos_bit(fl_start_pos);\n"
      "  } else {\n", sdef->elements[i].name);
  } else {
    src = mputstr(src, "  if (decoded_field_length < 0) return decoded_field_length;\n");
  }
  if(raw_options[i].tag_type && sdef->raw.taglist.list[raw_options[i].tag_type-1].nElements){
    rawAST_coding_taglist* cur_choice=
                               sdef->raw.taglist.list+raw_options[i].tag_type-1;
    src=mputstr(src,
      "       if(");
    src=genRawFieldChecker(src,cur_choice,FALSE);
    src=mputstr(src,
      ")");
    if(sdef->elements[i].isOptional){
      src=mputprintf(src,
      "{\n  field_%s=OMIT_VALUE;\n"
      "  p_buf.set_pos_bit(fl_start_pos);\n  }\n"
      "  else {\n"
      ,sdef->elements[i].name
      );
    }else src=mputstr(src, " return -1;\n");
  }
  if (!delayed_decode) {
    src=mputstr(src,
      "  decoded_length+=decoded_field_length;\n"
      "  limit-=decoded_field_length;\n"
      "  last_decoded_pos=bigger(last_decoded_pos, p_buf.get_pos_bit());\n"
      );
  }
  if(raw_options[i].extbitgroup &&
          sdef->raw.ext_bit_groups[raw_options[i].extbitgroup-1].ext_bit!=XDEFNO){
    src=mputstr(src,
    "group_limit-=decoded_field_length;\n"
    );
  }
  if(raw_options[i].lengthto){   /* if the field is lengthto store the value*/
    if(sdef->elements[i].raw.lengthindex){
      if(sdef->elements[i].raw.lengthindex->fieldtype == OPTIONAL_FIELD){
        src=mputprintf(src,
        "  if(field_%s%s.%s().ispresent()){\n"
        ,sdef->elements[i].name,sdef->elements[i].isOptional?"()":""
        ,sdef->elements[i].raw.lengthindex->nthfieldname
        );
      }
      src = mputprintf(src, "  INTEGER tmp = field_%s%s.%s%s().convert_to_Integer(%s_descr_);\n",
        sdef->elements[i].name, sdef->elements[i].isOptional ? "()" : "",
        sdef->elements[i].raw.lengthindex->nthfieldname,
        sdef->elements[i].raw.lengthindex->fieldtype == OPTIONAL_FIELD ? "()" : "",
        sdef->elements[i].raw.lengthindex->typedescr);
      if (sdef->elements[i].raw.lengthto_offset != 0) {
        src = mputprintf(src, "  tmp = tmp - %d;\n", sdef->elements[i].raw.lengthto_offset);
      }
      src = mputprintf(src, "  field_%s%s.%s%s() = tmp;\n", 
        sdef->elements[i].name, sdef->elements[i].isOptional ? "()" : "",
        sdef->elements[i].raw.lengthindex->nthfieldname,
        sdef->elements[i].raw.lengthindex->fieldtype == OPTIONAL_FIELD ? "()" : "");
      src = mputprintf(src, "  value_of_length_field%d += tmp.get_long_long_val() * %d;\n",
        i, sdef->elements[i].raw.unit == -1 ? 1 : sdef->elements[i].raw.unit);
      if(sdef->elements[i].raw.lengthindex->fieldtype == OPTIONAL_FIELD){
        src=mputstr(src,
        "  }\n"
        );
      }
    }
    else if(sdef->elements[i].raw.union_member_num){
      int m;
      src = mputprintf(src, "  switch (field_%s%s.get_selection()) {\n",
	      sdef->elements[i].name, sdef->elements[i].isOptional ? "()" : "");
      for (m = 1; m < sdef->elements[i].raw.union_member_num + 1; m++) {
	      src = mputprintf(src, "  case %s%s%s: {\n", sdef->elements[i].raw.member_name[0],
	        "::ALT_", sdef->elements[i].raw.member_name[m]);
        src = mputprintf(src, "    INTEGER tmp%d = field_%s%s.%s().convert_to_Integer(%s_%s_descr_);\n",
          m, sdef->elements[i].name, sdef->elements[i].isOptional ? "()" : "",
          sdef->elements[i].raw.member_name[m], sdef->elements[i].type,
          sdef->elements[i].raw.member_name[m]);
        if (sdef->elements[i].raw.lengthto_offset != 0) {
          src = mputprintf(src, "    tmp%d = tmp%d - %d;\n",
          m, m, sdef->elements[i].raw.lengthto_offset);
        }
        src = mputprintf(src, "    field_%s%s.%s() = tmp%d;\n", 
        sdef->elements[i].name, sdef->elements[i].isOptional ? "()" : "",
        sdef->elements[i].raw.member_name[m], m);
        src = mputprintf(src,
          "    value_of_length_field%d += tmp%d.get_long_long_val() * %d;\n"
          "    break; }\n",
          i, m, sdef->elements[i].raw.unit == -1 ? 1 : sdef->elements[i].raw.unit);
      }
      src = mputprintf(src, "  default:\n"
	      "    value_of_length_field%d = 0;\n"
	      "  }\n", i);
    }
    else{
      src = mputprintf(src, "  INTEGER tmp%d = field_%s%s.convert_to_Integer(%s_descr_);\n",
        i, sdef->elements[i].name,
        sdef->elements[i].isOptional ? "()" : "", sdef->elements[i].typedescrname);
      if (sdef->elements[i].raw.lengthto_offset != 0) {
        src = mputprintf(src, "  tmp%d = tmp%d - %d;\n",
          i, i, sdef->elements[i].raw.lengthto_offset);
      }
      src = mputprintf(src, "  field_%s%s = tmp%d;\n",
        sdef->elements[i].name, sdef->elements[i].isOptional ? "()" : "", i);
      src = mputprintf(src,
        "  value_of_length_field%d += tmp%d.get_long_long_val() * %d;\n",
        i, i, sdef->elements[i].raw.unit == -1 ? 1 : sdef->elements[i].raw.unit);
    }
  }
  if(raw_options[i].pointerto){   /* store the start of pointed field*/
    src=mputprintf(src,
      "  start_of_field%d=(int)field_%s%s+%d?1:-1;\n"
      ,sdef->elements[i].raw.pointerto
      ,sdef->elements[i].name,sdef->elements[i].isOptional?"()":""
      ,sdef->elements[i].raw.ptroffset
    );
  }
  if (!delayed_decode) {
    /* mark the used bits in length area*/
    for (a = 0; a < raw_options[i].lengthof; a++) {
      int field_index = raw_options[i].lengthoffield[a];
      src = mputprintf(src,
	"  value_of_length_field%d -= decoded_field_length;\n", field_index);
      if (i == field_index) src = mputprintf(src,
	  "  if (value_of_length_field%d < 0) return -1;\n", field_index);
    }
  }
  if(sdef->elements[i].isOptional){
    src=mputprintf(src,
    "  }\n  }\n  }\n%s"
    "  else field_%s=OMIT_VALUE;\n"
    ,raw_options[i].tag_type?"  }\n":"",sdef->elements[i].name
    );
  }
  return src;
}

#ifdef __SUNPRO_C
#define SUNPRO_PUBLIC  "public:\n"
#define SUNPRO_PRIVATE "private:\n"
#else
#define SUNPRO_PUBLIC
#define SUNPRO_PRIVATE
#endif

void defRecordTemplate1(const struct_def *sdef, output_struct *output)
{
    int i;
    const char *name = sdef->name, *dispname = sdef->dispname;
    const char* kind_str = sdef->kind == SET ? "set" : "record";
    char *def = NULL, *src = NULL;
    const char* base_class_name = use_runtime_2 && sdef->containsClass ?
      "Base_Class_Template" : "Base_Template";

    size_t mandatory_fields_count;

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

    if(sdef->nElements <= 0) {
	defEmptyRecordTemplate(name, dispname, base_class_name, output);
	return;
    }

    /* template class definition */
    def = mputprintf(def, "class %s_template : public %s {\n", name, base_class_name);

    /* data members */
    def = mputprintf(def,
      SUNPRO_PUBLIC
      "struct single_value_struct;\n"
      SUNPRO_PRIVATE
      "union {\n"
      "single_value_struct *single_value;\n"
      "struct {\n"
      "unsigned int n_values;\n"
      "%s_template *list_value;\n"
      "} value_list;\n"
      "struct {\n"
      "%s_template* precondition;\n"
      "%s_template* implied_template;\n"
      "} implication_;\n"
      "dynmatch_struct<%s>* dyn_match;\n"
      "};\n\n", name, name, name, name);
    /* the definition of single_value_struct must be put into the source file
     * because the types of optional fields may be incomplete in this position
     * of header file (e.g. due to type recursion) */
    src = mputprintf(src, "struct %s_template::single_value_struct {\n", name);
    for (i = 0; i < sdef->nElements; i++) {
	src = mputprintf(src, "%s field_%s;\n",
	    sdef->elements[i].type_temp, sdef->elements[i].name);
    }
    src = mputstr(src, "};\n\n");

    /* set_specific function (used in field access members) */
    def = mputstr(def, "void set_specific();\n");
    src = mputprintf(src, "void %s_template::set_specific()\n"
	"{\n"
	"if (template_selection != SPECIFIC_VALUE) {\n"
	"template_sel old_selection = template_selection;\n"
	"clean_up();\n"
	"single_value = new single_value_struct;\n"
	"set_selection(SPECIFIC_VALUE);\n"
	"if (old_selection == ANY_VALUE || old_selection == ANY_OR_OMIT) {\n",
	name);
    for (i = 0; i < sdef->nElements; i++) {
	src = mputprintf(src, "single_value->field_%s = %s;\n",
	    sdef->elements[i].name,
	    sdef->elements[i].isOptional ? "ANY_OR_OMIT" : "ANY_VALUE");
    }
    src = mputstr(src, "}\n"
	"}\n"
	"}\n\n");

    if (!sdef->containsClass) {
      /* copy_value function */
      def = mputprintf(def, "void copy_value(const %s& other_value);\n", name);
      src = mputprintf(src,
    "void %s_template::copy_value(const %s& other_value)\n"
    "{\n"
    "single_value = new single_value_struct;\n", name, name);
      for (i = 0; i < sdef->nElements; i++) {
          src = mputprintf(src,
            "if (other_value.%s().is_bound()) {\n", sdef->elements[i].name);
          if (sdef->elements[i].isOptional) {
            /* If the value is present, "reach into" the OPTIONAL with operator()
             * and pass the contained object to the template's constructor.
             * Else set the template to omit. */
            src = mputprintf(src,
        "  if (other_value.%s().ispresent()) single_value->field_%s = "
      "other_value.%s()();\n"
        "  else single_value->field_%s = OMIT_VALUE;\n",
        sdef->elements[i].name, sdef->elements[i].name,
        sdef->elements[i].name, sdef->elements[i].name);
          } else {
             src = mputprintf(src,
        "  single_value->field_%s = other_value.%s();\n",
        sdef->elements[i].name, sdef->elements[i].name);
          }
          src = mputprintf(src, "} else {\n"
            "  single_value->field_%s.clean_up();\n"
            "}\n", sdef->elements[i].name);
      }
      src = mputstr(src, "set_selection(SPECIFIC_VALUE);\n"
    "}\n\n");
    }

    /* copy_template function */
    def = mputprintf(def, "void copy_template(const %s_template& "
	"other_value);\n\n", name);
    src = mputprintf(src,
	"void %s_template::copy_template(const %s_template& other_value)\n"
	"{\n"
	"switch (other_value.template_selection) {\n"
	"case SPECIFIC_VALUE:\n", name, name);
    src = mputstr(src,
        "single_value = new single_value_struct;\n");
    for (i = 0; i < sdef->nElements; i++) {
        src = mputprintf(src,
          "if (UNINITIALIZED_TEMPLATE != other_value.%s().get_selection()) {\n",
          sdef->elements[i].name);
        src = mputprintf(src,
          "single_value->field_%s = other_value.%s();\n",
          sdef->elements[i].name, sdef->elements[i].name);
        src = mputprintf(src, "} else {\n"
          "single_value->field_%s.clean_up();\n"
          "}\n", sdef->elements[i].name);
    }
    src = mputprintf(src,
      "case OMIT_VALUE:\n"
      "case ANY_VALUE:\n"
      "case ANY_OR_OMIT:\n"
      "break;\n"
      "case VALUE_LIST:\n"
      "case COMPLEMENTED_LIST:\n"
      "case CONJUNCTION_MATCH:\n"
      "value_list.n_values = other_value.value_list.n_values;\n"
      "value_list.list_value = new %s_template[value_list.n_values];\n"
      "for (unsigned int list_count = 0; list_count < value_list.n_values; "
          "list_count++)\n"
      "value_list.list_value[list_count].copy_template("
          "other_value.value_list.list_value[list_count]);\n"
      "break;\n"
      "case IMPLICATION_MATCH:\n"
      "implication_.precondition = new %s_template(*other_value.implication_.precondition);\n"
      "implication_.implied_template = new %s_template(*other_value.implication_.implied_template);\n"
      "break;\n"
      "case DYNAMIC_MATCH:\n"
      "dyn_match = other_value.dyn_match;\n"
      "dyn_match->ref_count++;\n"
      "break;\n"
      "default:\n"
      "TTCN_error(\"Copying an uninitialized/unsupported template of type "
          "%s.\");\n"
      "break;\n"
      "}\n"
      "set_selection(other_value);\n"
      "}\n\n", name, name, name, dispname);

    /* default constructor */
    def = mputprintf(def, "public:\n"
	"%s_template();\n", name);
    src = mputprintf(src, "%s_template::%s_template()\n"
	"{\n"
	"}\n\n", name, name);

    /* constructor t1_template(template_sel other_value) */
    def = mputprintf(def, "%s_template(template_sel other_value);\n", name);
    src = mputprintf(src, "%s_template::%s_template(template_sel other_value)\n"
	" : %s(other_value)\n"
	"{\n"
	"check_single_selection(other_value);\n"
	"}\n\n", name, name, base_class_name);

    if (!sdef->containsClass) {
      /* constructor t1_template(const t1& other_value) */
      def = mputprintf(def, "%s_template(const %s& other_value);\n", name, name);
      src = mputprintf(src, "%s_template::%s_template(const %s& other_value)\n"
    "{\n"
    "copy_value(other_value);\n"
    "}\n\n", name, name, name);

      /* constructor t1_template(const OPTIONAL<t1>& other_value) */
      def = mputprintf(def, "%s_template(const OPTIONAL<%s>& other_value);\n",
    name, name);
      src = mputprintf(src,
    "%s_template::%s_template(const OPTIONAL<%s>& other_value)\n"
    "{\n"
    "switch (other_value.get_selection()) {\n"
    "case OPTIONAL_PRESENT:\n"
    "copy_value((const %s&)other_value);\n"
    "break;\n"
    "case OPTIONAL_OMIT:\n"
    "set_selection(OMIT_VALUE);\n"
    "break;\n"
    "default:\n"
    "TTCN_error(\"Creating a template of type %s from an unbound optional "
        "field.\");\n"
    "}\n"
    "}\n\n", name, name, name, name, dispname);
    }

    /* constructor t1_template(t1_template*& p_precondition, t1_template* p_implied_template) */
    def = mputprintf(def, "%s_template(%s_template* p_precondition, "
      "%s_template* p_implied_template);\n", name, name, name);
    src = mputprintf(src, "%s_template::%s_template(%s_template* p_precondition, "
      "%s_template* p_implied_template)\n"
      " : %s(IMPLICATION_MATCH)\n"
      "{\n"
      "implication_.precondition = p_precondition;\n"
      "implication_.implied_template = p_implied_template;\n"
      "}\n\n", name, name, name, name, base_class_name);

    /* constructor t1_template(Dynamic_Match_Interface<t1>* p_dyn_match) */
    def = mputprintf(def, "%s_template(Dynamic_Match_Interface<%s>* p_dyn_match);\n", name, name);
    src = mputprintf(src, "%s_template::%s_template(Dynamic_Match_Interface<%s>* p_dyn_match)\n"
      " : %s(DYNAMIC_MATCH)\n"
      "{\n"
      "dyn_match = new dynmatch_struct<%s>;\n"
      "dyn_match->ptr = p_dyn_match;\n"
      "dyn_match->ref_count = 1;\n"
      "}\n\n", name, name, name, base_class_name, name);

    /* copy constructor */
    def = mputprintf(def, "%s_template(const %s_template& other_value);\n",
	name, name);
    src = mputprintf(src, "%s_template::%s_template(const %s_template& "
	"other_value)\n"
	": %s()\n" /* yes, the base class _default_ constructor */
	"{\n"
	"copy_template(other_value);\n"
	"}\n\n", name, name, name, base_class_name);

    /* destructor */
    def = mputprintf(def, "~%s_template();\n", name);
    src = mputprintf(src, "%s_template::~%s_template()\n"
	"{\n"
	"clean_up();\n"
	"}\n\n", name, name);

    /* assignment operator <- template_sel */
    def = mputprintf(def, "%s_template& operator=(template_sel other_value);\n",
	name);
    src = mputprintf(src,
	"%s_template& %s_template::operator=(template_sel other_value)\n"
	"{\n"
	"check_single_selection(other_value);\n"
	"clean_up();\n"
	"set_selection(other_value);\n"
	"return *this;\n"
	"}\n\n", name, name);

    /* assignment operator <- value */
    def = mputprintf(def, "%s_template& operator=(const %s& other_value);\n",
	name, name);

    if (!sdef->containsClass) {
      src = mputprintf(src,
    "%s_template& %s_template::operator=(const %s& other_value)\n"
    "{\n"
    "clean_up();\n"
    "copy_value(other_value);\n"
    "return *this;\n"
    "}\n\n", name, name, name);

      /* assignment operator <- optional value */
      def = mputprintf(def, "%s_template& operator=(const OPTIONAL<%s>& "
    "other_value);\n", name, name);

      src = mputprintf(src,
    "%s_template& %s_template::operator=(const OPTIONAL<%s>& other_value)\n"
    "{\n"
    "clean_up();\n"
    "switch (other_value.get_selection()) {\n"
    "case OPTIONAL_PRESENT:\n"
    "copy_value((const %s&)other_value);\n"
    "break;\n"
    "case OPTIONAL_OMIT:\n"
    "set_selection(OMIT_VALUE);\n"
    "break;\n"
    "default:\n"
    "TTCN_error(\"Assignment of an unbound optional field to a template "
      "of type %s.\");\n"
    "}\n"
    "return *this;\n"
    "}\n\n", name, name, name, name, dispname);
    }

    /* assignment operator <- template*/
    def = mputprintf(def,
	"%s_template& operator=(const %s_template& other_value);\n",
	name, name);

    src = mputprintf(src,
	"%s_template& %s_template::operator=(const %s_template& other_value)\n"
	"{\n"
	"if (&other_value != this) {\n"
	"clean_up();\n"
	"copy_template(other_value);\n"
	"}\n"
	"return *this;\n"
	"}\n\n", name, name, name);

    /* match operation (template matching) */
    def = mputprintf(def, "boolean match(const %s& other_value, boolean legacy "
      "= FALSE) const;\n", name);

    src = mputprintf(src,
	"boolean %s_template::match(const %s& other_value, boolean legacy) const\n"
	"{\n"
    "if (!other_value.is_bound()) return FALSE;\n"
	"switch (template_selection) {\n"
	"case ANY_VALUE:\n"
	"case ANY_OR_OMIT:\n"
	"return TRUE;\n"
	"case OMIT_VALUE:\n"
	"return FALSE;\n"
	"case SPECIFIC_VALUE:\n", name, name);
    for (i = 0; i < sdef->nElements; i++) {
      src = mputprintf(src,"if(!other_value.%s().is_bound()) return FALSE;\n", sdef->elements[i].name);
	if (sdef->elements[i].isOptional) src = mputprintf(src,
	    "if((other_value.%s().ispresent() ? "
	    "!single_value->field_%s.match((const %s&)other_value.%s(), legacy) : "
	    "!single_value->field_%s.match_omit(legacy)))",
	    sdef->elements[i].name, sdef->elements[i].name,
	    sdef->elements[i].type, sdef->elements[i].name,
	    sdef->elements[i].name);
	else src = mputprintf(src,
	    "if(!single_value->field_%s.match(other_value.%s(), legacy))",
	    sdef->elements[i].name, sdef->elements[i].name);
	  src = mputstr(src, "return FALSE;\n");
    }
    src = mputprintf(src,
	"return TRUE;\n"
	"case VALUE_LIST:\n"
	"case COMPLEMENTED_LIST:\n"
	"for (unsigned int list_count = 0; list_count < value_list.n_values; "
	    "list_count++)\n"
	"if (value_list.list_value[list_count].match(other_value, legacy)) "
	    "return template_selection == VALUE_LIST;\n"
	"return template_selection == COMPLEMENTED_LIST;\n"
	"case CONJUNCTION_MATCH:\n"
  "for (unsigned int i = 0; i < value_list.n_values; i++) {\n"
  "if (!value_list.list_value[i].match(other_value)) {\n"
  "return FALSE;\n"
  "}\n"
  "}\n"
  "return TRUE;\n"
  "case IMPLICATION_MATCH:\n"
  "return !implication_.precondition->match(other_value) || implication_.implied_template->match(other_value);\n"
  "case DYNAMIC_MATCH:\n"
  "return dyn_match->ptr->match(other_value);\n"
	"default:\n"
	"TTCN_error(\"Matching an uninitialized/unsupported template of "
	    "type %s.\");\n"
	"}\n"
	"return FALSE;\n"
	"}\n\n", dispname);

    /* is_bound */
    def = mputstr(def, "boolean is_bound() const;\n");

    src = mputprintf(src, "boolean %s_template::is_bound() const\n"
        "{\n"
        "if (template_selection == UNINITIALIZED_TEMPLATE && !is_ifpresent) "
        "return FALSE;\n", name);
    src = mputstr(src, "if (template_selection != SPECIFIC_VALUE) return TRUE;\n");
    src = mputstr(src, "return ");
    for (i = 0; i < sdef->nElements; i++) {
      if (i != 0) {
        src = mputstr(src, "\n || ");
      }
        if (sdef->elements[i].isOptional) {
            src = mputprintf(src,
                "(single_value->field_%s.is_omit() ||"
                    " single_value->field_%s.is_bound())",
                sdef->elements[i].name, sdef->elements[i].name);
        } else {
            src = mputprintf(src,
                "single_value->field_%s.is_bound()",
                sdef->elements[i].name);
        }
    }
    src = mputstr(src, ";\n"
        "}\n\n");

    /* is_value */
    def = mputstr(def, "boolean is_value() const;\n");

    src = mputprintf(src, "boolean %s_template::is_value() const\n"
        "{\n"
        "if (template_selection != SPECIFIC_VALUE || is_ifpresent) "
        "return FALSE;\n", name);
        src = mputstr(src, "return ");
    for (i = 0; i < sdef->nElements; i++) {
      if (i != 0) {
        src = mputstr(src, "\n && ");
      }
        if (sdef->elements[i].isOptional) {
            src = mputprintf(src,
                "(single_value->field_%s.is_omit() ||"
                    " single_value->field_%s.is_value())",
                sdef->elements[i].name, sdef->elements[i].name);
        } else {
            src = mputprintf(src,
                "single_value->field_%s.is_value()",
                sdef->elements[i].name);
        }
    }
    src = mputstr(src, ";\n"
        "}\n\n");

    /* clean_up function */
    def = mputstr(def, "void clean_up();\n");
    src = mputprintf(src,
	"void %s_template::clean_up()\n"
	"{\n"
	"switch (template_selection) {\n"
	"case SPECIFIC_VALUE:\n"
	"delete single_value;\n"
	"break;\n"
	"case VALUE_LIST:\n"
	"case COMPLEMENTED_LIST:\n"
	"case CONJUNCTION_MATCH:\n"
	"delete [] value_list.list_value;\n"
	"break;\n"
	"case IMPLICATION_MATCH:\n"
  "delete implication_.precondition;\n"
  "delete implication_.implied_template;\n"
  "break;\n"
  "case DYNAMIC_MATCH:\n"
  "dyn_match->ref_count--;\n"
  "if (dyn_match->ref_count == 0) {\n"
  "delete dyn_match->ptr;\n"
  "delete dyn_match;\n"
  "}\n"
  "break;\n"
	"default:\n"
	"break;\n"
	"}\n"
	"template_selection = UNINITIALIZED_TEMPLATE;\n"
	"}\n\n", name);

    if (!sdef->containsClass) {
      /* valueof operation */
      def = mputprintf(def, "%s valueof() const;\n", name);

      src = mputprintf(src, "%s %s_template::valueof() const\n"
    "{\n"
    "if (template_selection != SPECIFIC_VALUE || is_ifpresent)\n"
    "TTCN_error(\"Performing a valueof or send operation on a non-specific "
    "template of type %s.\");\n"
    "%s ret_val;\n", name, name, dispname, name);
      for (i = 0; i < sdef->nElements; i++) {
        if (sdef->elements[i].isOptional) {
          src = mputprintf(src,
        "if (single_value->field_%s.is_omit()) "
          "ret_val.%s() = OMIT_VALUE;\n"
           "else ",
        sdef->elements[i].name, sdef->elements[i].name);
        }
        src = mputprintf(src, "if (single_value->field_%s.is_bound()) {\n"
          "ret_val.%s() = single_value->field_%s.valueof();\n"
          "}\n",
          sdef->elements[i].name, sdef->elements[i].name, sdef->elements[i].name);
      }
      src = mputstr(src, "return ret_val;\n"
      "}\n\n");
    }

    /* void set_type(template_sel, int) function */
    def = mputstr(def,
	"void set_type(template_sel template_type, "
	    "unsigned int list_length);\n");

    src = mputprintf(src,
      "void %s_template::set_type(template_sel template_type, "
          "unsigned int list_length)\n"
      "{\n"
      "if (template_type != VALUE_LIST "
          "&& template_type != COMPLEMENTED_LIST"
	        "&& template_type != CONJUNCTION_MATCH)\n"
      "TTCN_error(\"Setting an invalid list for a template of type %s.\");\n"
      "clean_up();\n"
      "set_selection(template_type);\n"
      "value_list.n_values = list_length;\n"
      "value_list.list_value = new %s_template[list_length];\n"
      "}\n\n", name, dispname, name);

    /* list_item(int) function */

    def = mputprintf(def,
	"%s_template& list_item(unsigned int list_index) const;\n", name);

    src = mputprintf(src,
      "%s_template& %s_template::list_item(unsigned int list_index) const\n"
      "{\n"
      "if (template_selection != VALUE_LIST "
	        "&& template_selection != COMPLEMENTED_LIST"
	        "&& template_selection != CONJUNCTION_MATCH)\n"
      "TTCN_error(\"Accessing a list element of a non-list template of "
      "type %s.\");\n"
      "if (list_index >= value_list.n_values)\n"
      "TTCN_error(\"Index overflow in a value list template of type "
      "%s.\");\n"
      "return value_list.list_value[list_index];\n"
      "}\n\n", name, name, dispname, dispname);

    /* template field access functions (non-const & const) */
    for (i = 0; i < sdef->nElements; i++) {
	def = mputprintf(def, "%s& %s();\n",
	    sdef->elements[i].type_temp, sdef->elements[i].name);
	src = mputprintf(src, "%s& %s_template::%s()\n"
	    "{\n"
	    "set_specific();\n"
	    "return single_value->field_%s;\n"
	    "}\n\n",
	    sdef->elements[i].type_temp, name, sdef->elements[i].name,
	    sdef->elements[i].name);
	def = mputprintf(def, "const %s& %s() const;\n",
	    sdef->elements[i].type_temp, sdef->elements[i].name);
	src = mputprintf(src, "const %s& %s_template::%s() const\n"
	    "{\n"
	    "if (template_selection != SPECIFIC_VALUE)\n"
	    "TTCN_error(\"Accessing field %s of a non-specific "
		"template of type %s.\");\n"
	    "return single_value->field_%s;\n"
	    "}\n\n",
	    sdef->elements[i].type_temp, name, sdef->elements[i].name,
	    sdef->elements[i].dispname, dispname, sdef->elements[i].name);
    }

    /* sizeof operation */
    def = mputstr(def, "int size_of() const;\n");
    src = mputprintf(src,
      "int %s_template::size_of() const\n"
      "{\n"
      "  if (is_ifpresent) TTCN_error(\"Performing sizeof() operation on a "
        "template of type %s which has an ifpresent attribute.\");\n"
      "  switch (template_selection)\n"
      "  {\n"
      "  case SPECIFIC_VALUE:\n",
      name, dispname);
    mandatory_fields_count = 0;
    for (i = 0; i < sdef->nElements; i++)
      if (!sdef->elements[i].isOptional) mandatory_fields_count++;
    if(sdef->nElements == mandatory_fields_count){
      src = mputprintf(src,"    return %lu;\n",
        (unsigned long) mandatory_fields_count);
    }else{
      src = mputprintf(src,
        "  {"
        "    int ret_val = %lu;\n", (unsigned long) mandatory_fields_count);
      for (i = 0; i < sdef->nElements; i++)
        if (sdef->elements[i].isOptional)
          src = mputprintf(src,
            "      if (single_value->field_%s.is_present()) ret_val++;\n",
            sdef->elements[i].name);
      src = mputstr(src,
        "      return ret_val;\n"
        "    }\n");
    }
    src = mputprintf(src,

      "  case VALUE_LIST:\n"
      "   {\n"
      "     if (value_list.n_values<1)\n"
      "       TTCN_error(\"Internal error: Performing sizeof() operation on a "
      "template of type %s containing an empty list.\");\n"
      "      int item_size = value_list.list_value[0].size_of();\n"
      "      for (unsigned int l_idx = 1; l_idx < value_list.n_values; l_idx++)\n"
      "      {\n"
      "        if (value_list.list_value[l_idx].size_of()!=item_size)\n"
      "          TTCN_error(\"Performing sizeof() operation on a template "
      "of type %s containing a value list with different sizes.\");\n"
      "      }\n"
      "      return item_size;\n"
      "    }\n"
      "  case OMIT_VALUE:\n"
      "    TTCN_error(\"Performing sizeof() operation on a template of type %s "
      "containing omit value.\");\n"
      "  case ANY_VALUE:\n"
      "  case ANY_OR_OMIT:\n"
      "    TTCN_error(\"Performing sizeof() operation on a template of type %s "
      "containing */? value.\");\n"
      "  case COMPLEMENTED_LIST:\n"
      "    TTCN_error(\"Performing sizeof() operation on a template of type %s "
      "containing complemented list.\");\n"
      "  case CONJUNCTION_MATCH:\n"
      "    TTCN_error(\"Performing sizeof() operation on a template of type %s "
      "containing a conjunction list match.\");\n"
      "  case IMPLICATION_MATCH:\n"
      "    TTCN_error(\"Performing sizeof() operation on a template of type %s "
      "containing an implication match.\");\n"
      "  case DYNAMIC_MATCH:\n"
      "    TTCN_error(\"Performing sizeof() operation on a template of type %s "
      "containing a dynamic match.\");\n"
      "  default:\n"
      "    TTCN_error(\"Performing sizeof() operation on an "
      "uninitialized/unsupported template of type %s.\");\n"
      "  }\n"
      "  return 0;\n"
      "}\n\n",
      dispname,dispname,dispname,dispname,dispname,dispname,dispname,dispname,dispname);

    /* log function */
    def = mputstr(def, "void log() const;\n");
    src = mputprintf(src,
	"void %s_template::log() const\n"
	"{\n"
	"switch (template_selection) {\n"
	"case SPECIFIC_VALUE:\n", name);
    for (i = 0; i < sdef->nElements; i++) {
	src = mputstr(src, "TTCN_Logger::log_event_str(\"");
	if (i == 0) src = mputc(src, '{');
	else src = mputc(src, ',');
	src = mputprintf(src, " %s := \");\n"
	    "%s"
	    "single_value->field_%s.log();\n"
	    "%s",
	    sdef->elements[i].dispname,
	    sdef->elements[i].sensitive_data ? "TTCN_Logger::begin_sensitive_data();\n" : "",
	    sdef->elements[i].name,
	    sdef->elements[i].sensitive_data ? "TTCN_Logger::end_sensitive_data();\n" : "");
    }
    src = mputstr(src,
	"TTCN_Logger::log_event_str(\" }\");\n"
	"break;\n"
	"case COMPLEMENTED_LIST:\n"
	"TTCN_Logger::log_event_str(\"complement\");\n"
	"case CONJUNCTION_MATCH:\n"
  "if (template_selection == CONJUNCTION_MATCH) {\n"
  "TTCN_Logger::log_event_str(\"conjunct\");\n"
  "}\n"
	"case VALUE_LIST:\n"
	"TTCN_Logger::log_char('(');\n"
	"for (unsigned int list_count = 0; list_count < value_list.n_values; "
	    "list_count++) {\n"
	"if (list_count > 0) TTCN_Logger::log_event_str(\", \");\n"
	"value_list.list_value[list_count].log();\n"
	"}\n"
	"TTCN_Logger::log_char(')');\n"
	"break;\n"
	"case IMPLICATION_MATCH:\n"
  "implication_.precondition->log();\n"
  "TTCN_Logger::log_event_str(\" implies \");\n"
  "implication_.implied_template->log();\n"
  "break;\n"
  "case DYNAMIC_MATCH:\n"
  "TTCN_Logger::log_event_str(\"@dynamic template\");\n"
  "break;\n"
	"default:\n"
	"log_generic();\n"
	"}\n"
	"log_ifpresent();\n"
	"}\n\n");

    /* log_match function */
    def = mputprintf(def, "void log_match(const %s& match_value, "
      "boolean legacy = FALSE) const;\n", name);
    src = mputprintf(src,
    "void %s_template::log_match(const %s& match_value, boolean legacy) const\n"
    "{\n"
    "if(TTCN_Logger::VERBOSITY_COMPACT"
      " == TTCN_Logger::get_matching_verbosity()){\n"
    "if(match(match_value, legacy)){\n"
    "TTCN_Logger::print_logmatch_buffer();\n"
    "TTCN_Logger::log_event_str(\" matched\");\n"
    "} else{\n"
    "if (template_selection == SPECIFIC_VALUE) {\n"
    "size_t previous_size = TTCN_Logger::get_logmatch_buffer_len();\n"
    , name, name);
    for (i = 0; i < sdef->nElements; i++) {
      if (sdef->elements[i].isOptional){
        src = mputprintf(src,
        "if (match_value.%s().ispresent()){\n"
        "if(!single_value->field_%s.match(match_value.%s(), legacy)){\n"
        "TTCN_Logger::log_logmatch_info(\".%s\");\n"
        "%s"
        "single_value->field_%s.log_match(match_value.%s(), legacy);\n"
        "%s"
        "TTCN_Logger::set_logmatch_buffer_len(previous_size);\n"
        "}\n"
        "} else {\n"
        "if (!single_value->field_%s.match_omit(legacy)){\n "
        "TTCN_Logger::log_logmatch_info(\".%s := omit with \");\n"
        "TTCN_Logger::print_logmatch_buffer();\n"
        "%s"
        "single_value->field_%s.log();\n"
        "%s"
        "TTCN_Logger::log_event_str(\" unmatched\");\n"
        "TTCN_Logger::set_logmatch_buffer_len(previous_size);\n"
        "}\n"
        "}\n"
        ,  sdef->elements[i].name, sdef->elements[i].name,
        sdef->elements[i].name, sdef->elements[i].dispname,
        sdef->elements[i].sensitive_data ? "TTCN_Logger::begin_sensitive_data();\n" : "",
        sdef->elements[i].name, sdef->elements[i].name,
        sdef->elements[i].sensitive_data ? "TTCN_Logger::end_sensitive_data();\n" : "",
        sdef->elements[i].name, sdef->elements[i].dispname,
        sdef->elements[i].sensitive_data ? "TTCN_Logger::begin_sensitive_data();\n" : "",
        sdef->elements[i].name,
        sdef->elements[i].sensitive_data ? "TTCN_Logger::end_sensitive_data();\n" : "");
      }else{
        src = mputprintf(src,
        "if(!single_value->field_%s.match(match_value.%s(), legacy)){\n"
        "TTCN_Logger::log_logmatch_info(\".%s\");\n"
        "%s"
        "single_value->field_%s.log_match(match_value.%s(), legacy);\n"
        "%s"
        "TTCN_Logger::set_logmatch_buffer_len(previous_size);\n"
        "}\n",sdef->elements[i].name, sdef->elements[i].name,
        sdef->elements[i].dispname,
        sdef->elements[i].sensitive_data ? "TTCN_Logger::begin_sensitive_data();\n" : "",
        sdef->elements[i].name, sdef->elements[i].name,
        sdef->elements[i].sensitive_data ? "TTCN_Logger::end_sensitive_data();\n" : "");
      }
    }

    src = mputstr(src,"}else {\n"
    "TTCN_Logger::print_logmatch_buffer();\n"
    "match_value.log();\n"
    "TTCN_Logger::log_event_str(\" with \");\n"
    "log();\n"
    "TTCN_Logger::log_event_str(\" unmatched\");\n"
    "}\n"
    "}\n"
    "return;\n"
    "}\n"
    "if (template_selection == SPECIFIC_VALUE) {\n");
    for (i = 0; i < sdef->nElements; i++) {
	src = mputstr(src, "TTCN_Logger::log_event_str(\"");
	if (i == 0) src = mputc(src, '{');
	else src = mputc(src, ',');
	src = mputprintf(src, " %s := \");\n", sdef->elements[i].dispname);
	if (sdef->elements[i].isOptional) src = mputprintf(src,
	    "if (match_value.%s().ispresent()) {\n"
	    "%s"
	    "single_value->field_%s.log_match(match_value.%s(), legacy);\n"
	    "%s"
	    "} else {\n"
	    "TTCN_Logger::log_event_str(\"omit with \");\n"
	    "%s"
	    "single_value->field_%s.log();\n"
	    "%s"
            "if (single_value->field_%s.match_omit(legacy)) "
              "TTCN_Logger::log_event_str(\" matched\");\n"
            "else TTCN_Logger::log_event_str(\" unmatched\");\n"
	    "}\n",
	    sdef->elements[i].name,
	    sdef->elements[i].sensitive_data ? "TTCN_Logger::begin_sensitive_data();\n" : "",
	    sdef->elements[i].name, sdef->elements[i].name,
	    sdef->elements[i].sensitive_data ? "TTCN_Logger::end_sensitive_data();\n" : "",
	    sdef->elements[i].sensitive_data ? "TTCN_Logger::begin_sensitive_data();\n" : "",
	    sdef->elements[i].name,
	    sdef->elements[i].sensitive_data ? "TTCN_Logger::end_sensitive_data();\n" : "",
	    sdef->elements[i].name);
	else src = mputprintf(src,
	    "%s"
	    "single_value->field_%s.log_match(match_value.%s(), legacy);\n"
	    "%s",
	    sdef->elements[i].sensitive_data ? "TTCN_Logger::begin_sensitive_data();\n" : "",
	    sdef->elements[i].name, sdef->elements[i].name,
	    sdef->elements[i].sensitive_data ? "TTCN_Logger::end_sensitive_data();\n" : "");
    }
    src = mputstr(src,
	"TTCN_Logger::log_event_str(\" }\");\n"
	"} else {\n"
	"match_value.log();\n"
	"TTCN_Logger::log_event_str(\" with \");\n"
	"log();\n"
	"if (match(match_value, legacy)) TTCN_Logger::log_event_str(\" matched\");\n"
	"else TTCN_Logger::log_event_str(\" unmatched\");\n"
	"}\n"
	"}\n\n");

    /* set implicit omit function, recursive */
    def = mputstr(def, "  void set_implicit_omit();\n");
    src = mputprintf(src,
      "void %s_template::set_implicit_omit()\n"
      "{\n"
      "if (template_selection != SPECIFIC_VALUE) return;\n", name);
    for (i = 0; i < sdef->nElements; i++) {
      if (sdef->elements[i].isOptional || (asn1_enc_only && sdef->elements[i].isDefault)) {
        src = mputprintf(src,
          "if (!%s().is_bound()) %s() = %s;\n"
          "else %s().set_implicit_omit();\n",
          sdef->elements[i].name, sdef->elements[i].name,
          (asn1_enc_only && sdef->elements[i].isDefault) ? sdef->elements[i].defvalname : "OMIT_VALUE",
          sdef->elements[i].name);
      } else {
        src = mputprintf(src,
          "if (%s().is_bound()) %s().set_implicit_omit();\n",
          sdef->elements[i].name, sdef->elements[i].name);
      }
    }
    src = mputstr(src, "}\n\n");

    if (!sdef->containsClass) {
      /*encode_text function*/
      def = mputstr(def, "void encode_text(Text_Buf& text_buf) const;\n");
      src = mputprintf(src,
	  "void %s_template::encode_text(Text_Buf& text_buf) const\n"
	  "{\n"
	  "encode_text_base(text_buf);\n"
	  "switch (template_selection) {\n"
	  "case SPECIFIC_VALUE:\n", name);
      for (i = 0; i < sdef->nElements; i++) {
	  src = mputprintf(src, "single_value->field_%s.encode_text(text_buf);\n",
	      sdef->elements[i].name);
      }
      src = mputprintf(src,
	  "case OMIT_VALUE:\n"
	  "case ANY_VALUE:\n"
	  "case ANY_OR_OMIT:\n"
	  "break;\n"
	  "case VALUE_LIST:\n"
	  "case COMPLEMENTED_LIST:\n"
	  "text_buf.push_int(value_list.n_values);\n"
	  "for (unsigned int list_count = 0; list_count < value_list.n_values; "
	      "list_count++)\n"
	  "value_list.list_value[list_count].encode_text(text_buf);\n"
	  "break;\n"
	  "default:\n"
	  "TTCN_error(\"Text encoder: Encoding an uninitialized/unsupported "
	      "template of type %s.\");\n"
	  "}\n"
	  "}\n\n", dispname);

      /*decode_text function*/
      def = mputstr(def, "void decode_text(Text_Buf& text_buf);\n");
      src = mputprintf(src,
	  "void %s_template::decode_text(Text_Buf& text_buf)\n"
	  "{\n"
	  "clean_up();\n"
	  "decode_text_base(text_buf);\n"
	  "switch (template_selection) {\n"
	  "case SPECIFIC_VALUE:\n"
	  "single_value = new single_value_struct;\n", name);
      for (i = 0; i < sdef->nElements; i++) {
	  src = mputprintf(src, "single_value->field_%s.decode_text(text_buf);\n",
	      sdef->elements[i].name);
      }
      src = mputprintf(src,
	  "case OMIT_VALUE:\n"
	  "case ANY_VALUE:\n"
	  "case ANY_OR_OMIT:\n"
	  "break;\n"
	  "case VALUE_LIST:\n"
	  "case COMPLEMENTED_LIST:\n"
	  "value_list.n_values = text_buf.pull_int().get_val();\n"
	  "value_list.list_value = new %s_template[value_list.n_values];\n"
	  "for (unsigned int list_count = 0; list_count < value_list.n_values; "
	      "list_count++)\n"
	  "value_list.list_value[list_count].decode_text(text_buf);\n"
	  "break;\n"
	  "default:\n"
	  "TTCN_error(\"Text decoder: An unknown/unsupported selection was "
	      "received in a template of type %s.\");\n"
	  "}\n"
	  "}\n\n", name, dispname);

    /* set_param() */
    def = mputstr(def, "void set_param(Module_Param& param);\n");
    src = mputprintf(src,
      "void %s_template::set_param(Module_Param& param)\n"
      "{\n"
      "  param.basic_check(Module_Param::BC_TEMPLATE, \"%s template\");\n"
      "  switch (param.get_type()) {\n"
      "  case Module_Param::MP_Omit:\n"
      "    *this = OMIT_VALUE;\n"
      "    break;\n"
      "  case Module_Param::MP_Any:\n"
      "    *this = ANY_VALUE;\n"
      "    break;\n"
      "  case Module_Param::MP_AnyOrNone:\n"
      "    *this = ANY_OR_OMIT;\n"
      "    break;\n"
      "  case Module_Param::MP_List_Template:\n"
      "  case Module_Param::MP_ComplementList_Template:\n"
      "  case Module_Param::MP_ConjunctList_Template: {\n"
      "    %s_template new_temp;\n"
      "    new_temp.set_type(param.get_type()==Module_Param::MP_List_Template ? "
      "VALUE_LIST : (param.get_type() == Module_Param::MP_ConjunctList_Template ? "
      "CONJUNCTION_MATCH : COMPLEMENTED_LIST), param.get_size());\n"
      "    for (size_t p_i=0; p_i<param.get_size(); p_i++) {\n"
      "      new_temp.list_item(p_i).set_param(*param.get_elem(p_i));\n"
      "    }\n"
      "    *this = new_temp;\n"
      "    break; }\n"
      "  case Module_Param::MP_Value_List:\n"
      "    if (%lu<param.get_size()) {\n"
      "      param.error(\"%s template of type %s has %lu fields but list value has %%d fields\", (int)param.get_size());\n"
      "    }\n",
      name, kind_str, name, (unsigned long)sdef->nElements, kind_str, dispname, (unsigned long)sdef->nElements);
    for (i = 0; i < sdef->nElements; ++i) {
      src = mputprintf(src,
        "    if (param.get_size()>%lu && param.get_elem(%lu)->get_type()!=Module_Param::MP_NotUsed) %s().set_param(*param.get_elem(%lu));\n",
        (unsigned long)i, (unsigned long)i, sdef->elements[i].name, (unsigned long)i);
    }
    src = mputstr(src,
      "    break;\n"
      "  case Module_Param::MP_Assignment_List: {\n"
      "    Vector<bool> value_used(param.get_size());\n"
      "    value_used.resize(param.get_size(), FALSE);\n");
    for (i = 0; i < sdef->nElements; ++i) {
      src = mputprintf(src,
        "    for (size_t val_idx=0; val_idx<param.get_size(); val_idx++) {\n"
        "      Module_Param* const curr_param = param.get_elem(val_idx);\n"
        "      if (!strcmp(curr_param->get_id()->get_name(), \"%s\")) {\n"
        "        if (curr_param->get_type()!=Module_Param::MP_NotUsed) {\n"
        "          %s().set_param(*curr_param);\n"
        "        }\n"
        "        value_used[val_idx]=TRUE;\n"
        "      }\n"
        "    }\n"
        , sdef->elements[i].dispname, sdef->elements[i].name);
    }
    src = mputprintf(src,
      "    for (size_t val_idx=0; val_idx<param.get_size(); val_idx++) if (!value_used[val_idx]) {\n"
      "      Module_Param* const curr_param = param.get_elem(val_idx);\n"
      "      curr_param->error(\"Non existent field name in type %s: %%s\", curr_param->get_id()->get_name());\n"
      "      break;\n"
      "    }\n"
      "  } break;\n"
      "  case Module_Param::MP_Implication_Template: {\n"
      "    %s_template* precondition = new %s_template;\n"
      "    precondition->set_param(*param.get_elem(0));\n"
      "    %s_template* implied_template = new %s_template;\n"
      "    implied_template->set_param(*param.get_elem(1));\n"
      "    *this = %s_template(precondition, implied_template);\n"
      "  } break;\n"
      "  default:\n"
      "    param.type_error(\"%s template\", \"%s\");\n"
      "  }\n"
      "  is_ifpresent = param.get_ifpresent();\n"
      "}\n\n", dispname, name, name, name, name, name, kind_str, dispname);
    }

    /* check template restriction */
    def = mputstr(def, "void check_restriction(template_res t_res, "
        "const char* t_name=NULL, boolean legacy = FALSE) const;\n");
    src = mputprintf(src,
        "void %s_template::check_restriction("
          "template_res t_res, const char* t_name, boolean legacy) const\n"
        "{\n"
        "if (template_selection==UNINITIALIZED_TEMPLATE) return;\n"
        "switch ((t_name&&(t_res==TR_VALUE))?TR_OMIT:t_res) {\n"
        "case TR_OMIT:\n"
        "if (template_selection==OMIT_VALUE) return;\n"
        "case TR_VALUE:\n"
        "if (template_selection!=SPECIFIC_VALUE || is_ifpresent) break;\n",
        name);
    for (i = 0; i < sdef->nElements; i++) {
	    src = mputprintf(src, "single_value->field_%s.check_restriction("
        "t_res, t_name ? t_name : \"%s\");\n",
	      sdef->elements[i].name, dispname);
    }
    src = mputprintf(src,
        "return;\n"
        "case TR_PRESENT:\n"
        "if (!match_omit(legacy)) return;\n"
        "break;\n"
        "default:\n"
        "return;\n"
        "}\n"
        "TTCN_error(\"Restriction `%%s' on template of type %%s "
          "violated.\", get_res_name(t_res), t_name ? t_name : \"%s\");\n"
        "}\n\n", dispname);

    defCommonRecordTemplate(name, &def, &src);

    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);
}

static void defEmptyRecordClass(const struct_def *sdef,
                                output_struct *output)
{
    const char *name = sdef->name, *dispname = sdef->dispname;
    char *def = NULL, *src = NULL;
    boolean ber_needed = sdef->isASN1 && enable_ber();
    boolean raw_needed = sdef->hasRaw && enable_raw();
    boolean text_needed = sdef->hasText && enable_text();
    boolean xer_needed = sdef->hasXer && enable_xer();
    boolean json_needed = sdef->hasJson && enable_json();
    boolean oer_needed = sdef->hasOer && enable_oer();
    boolean per_needed = sdef->hasPer && enable_per();

    def = mputprintf(def,
#ifndef NDEBUG
        "// written by %s in " __FILE__ " at line %d\n"
#endif
        "class %s : public Base_Type {\n"
	"boolean bound_flag;\n"
	"public:\n"
#ifndef NDEBUG
        , __FUNCTION__, __LINE__
#endif
        , name);

    /* default ctor */
    def = mputprintf(def, "%s();\n", name);
    src = mprintf("%s::%s()\n"
	"{\n"
	"bound_flag = FALSE;\n"
	"}\n\n", name, name);

    /* ctor from NULL_VALUE (i.e. {}) */
    def = mputprintf(def, "%s(null_type other_value);\n", name);
    src = mputprintf(src, "%s::%s(null_type)\n"
	"{\n"
	"bound_flag = TRUE;\n"
	"}\n\n", name, name);

    /* copy ctor */
    def = mputprintf(def, "%s(const %s& other_value);\n", name, name);
    src = mputprintf(src, "%s::%s(const %s& other_value)\n"
	"{\n"
	"other_value.must_bound(\"Copying an unbound value of "
	    "type %s.\");\n"
	"bound_flag = TRUE;\n"
	"}\n\n", name, name, name, dispname);

    /* assignment op: from NULL_VALUE */
    def = mputprintf(def, "%s& operator=(null_type other_value);\n", name);
    src = mputprintf(src, "%s& %s::operator=(null_type)\n"
	"{\n"
	"bound_flag = TRUE;\n"
	"return *this;\n"
	"}\n\n", name, name);

    /* assignment op: from itself */
    def = mputprintf(def, "%s& operator=(const %s& other_value);\n", name,
	name);
    src = mputprintf(src, "%s& %s::operator=(const %s& other_value)\n"
	"{\n"
	"other_value.must_bound(\"Assignment of an unbound value of type "
	    "%s.\");\n"
	"bound_flag = TRUE;\n"
	"return *this;\n"
	"}\n\n", name, name, name, dispname);

    /* comparison op: with NULL_VALUE */
    def = mputstr(def, "boolean operator==(null_type other_value) const;\n");
    src = mputprintf(src,
	"boolean %s::operator==(null_type) const\n"
	"{\n"
	"must_bound(\"Comparison of an unbound value of type %s.\");\n"
	"return TRUE;\n"
	"}\n\n", name, dispname);

    /* comparison op: with itself */
    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"
	"must_bound(\"Comparison of an unbound value of type %s.\");\n"
	"other_value.must_bound(\"Comparison of an unbound value of type "
	    "%s.\");\n"
	"return TRUE;\n"
	"}\n\n", name, name, dispname, dispname);

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


    /* is_bound function */
    def = mputstr(def, "inline boolean is_bound() const "
	"{ return bound_flag; }\n");

    /* is_present function */
    def = mputstr(def,
      "inline boolean is_present() const { return is_bound(); }\n");

    /* is_value function */
    def = mputstr(def, "inline boolean is_value() const "
        "{ return bound_flag; }\n");

    /* clean_up function */
    def = mputstr(def, "inline void clean_up() "
        "{ bound_flag = FALSE; }\n");

    /* must_bound function */
    def = mputstr(def, "inline void must_bound(const char *err_msg) const "
	"{ if (!bound_flag) TTCN_error(\"%s\", err_msg); }\n");

    /* log function */
    if (asn1_enc_only) {
      def = mputstr(def, "CHARSTRING log() const;\n");
      src = mputprintf(src, "CHARSTRING %s::log() const\n"
        "{\n"
        "if (bound_flag) return CHARSTRING(\"{ }\");\n"
        "else return UNBOUND_VALUE_LOG;\n"
        "}\n\n", name);
    }
    else {
      def = mputstr(def, "void log() const;\n");
      src = mputprintf(src, "void %s::log() const\n"
        "{\n"
        "if (bound_flag) TTCN_Logger::log_event_str(\"{ }\");\n"
        "else TTCN_Logger::log_event_unbound();\n"
        "}\n\n", name);

      /* set_param function */
      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, \"empty record/set value (i.e. { })\");\n"
        "  if (param.get_type()!=Module_Param::MP_Value_List || param.get_size()>0) {\n"
        "    param.type_error(\"empty record/set value (i.e. { })\", \"%s\");\n"
        "  }\n"
        "  bound_flag = TRUE;\n"
        "}\n\n", name, dispname);

      /* encode_text function */
      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"
        "must_bound(\"Text encoder: Encoding an unbound value of type %s.\");\n"
        "}\n\n", name, dispname);

      /* decode_text function */
      def = mputstr(def, "void decode_text(Text_Buf& text_buf);\n");
      src = mputprintf(src, "void %s::decode_text(Text_Buf& /*text_buf*/)\n"
        "{\n"
        "bound_flag = TRUE;\n"
        "}\n\n", name);
    }

    if(ber_needed || raw_needed || text_needed || xer_needed || json_needed
      || oer_needed || per_needed) {
      def_encdec(name, &def, &src, ber_needed, raw_needed,
                 text_needed, xer_needed, json_needed, oer_needed, per_needed, FALSE);
    }

  /* BER functions */
  if(ber_needed) {
    /* BER_encode_TLV() */
    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=ASN_BER_TLV_t::construct(NULL);\n"
       "  new_tlv=ASN_BER_V2TLV(new_tlv, p_td, p_coding);\n"
       "  return new_tlv;\n"
       "}\n"
       "\n"
       , name
       );

    /* BER_decode_TLV() */
    src=mputprintf
      (src,
       "boolean %s::BER_decode_TLV(const TTCN_Typedescriptor_t& p_td,"
       " const ASN_BER_TLV_t& p_tlv, unsigned L_form)\n"
       "{\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_0(\"While decoding '%s' type: \");\n"
       "  stripped_tlv.chk_constructed_flag(TRUE);\n"
       "  bound_flag=TRUE;\n"
       "  return TRUE;\n"
       "}\n"
       "\n"
       , name, sdef->dispname
       );
  } /* if ber_needed */
  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(!bound_flag) {\n"
      "    TTCN_EncDec_ErrorContext::error\n"
      "      (TTCN_EncDec::ET_UNBOUND, \"Encoding an unbound value.\");\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"
      ,name
     );
    src = mputprintf(src,
      "int %s::TEXT_decode(const TTCN_Typedescriptor_t& p_td,"
      " TTCN_Buffer& p_buf, Limit_Token_List& limit, boolean no_err, boolean){\n"
      "  bound_flag = TRUE;\n"
      "  int decoded_length=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_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\n"
      "              (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 0;\n"
      "        }\n"
      "    decoded_length+=tl;\n"
      "    p_buf.increase_pos(tl);\n"
      "  }\n"
      "  return decoded_length;\n"
      "}\n"
      ,name
     );

  }
    /* RAW functions */
    if (raw_needed) {
	src = mputprintf(src,
	    "int %s::RAW_encode(const TTCN_Typedescriptor_t& p_td, "
		"RAW_enc_tree& /*myleaf*/) const\n"
	    "{\n"
	    "if (!bound_flag) TTCN_EncDec_ErrorContext::error"
		"(TTCN_EncDec::ET_UNBOUND, \"Encoding an unbound value of "
		"type %%s.\", p_td.name);\n"
	    "return 0;\n"
	    "}\n\n", name);

	src = mputprintf(src,
	    "int %s::RAW_decode(const TTCN_Typedescriptor_t& p_td, "
		"TTCN_Buffer& p_buf, int, raw_order_t, boolean, int, boolean, "
		"const RAW_Force_Omit*)\n"
	    "{\n"
	    "bound_flag = TRUE;\n"
	    "return p_buf.increase_pos_padd(p_td.raw->prepadding) + "
		"p_buf.increase_pos_padd(p_td.raw->padding);\n"
	    "}\n\n", name);
    }

    if (xer_needed) { /* XERSTUFF codegen for empty record/SEQUENCE */
      src=mputprintf(src,
        "boolean %s::can_start(const char *p_name, const char *p_uri, "
        "const XERdescriptor_t& p_td, unsigned int p_flavor, unsigned int /*p_flavor2*/) {\n"
        "  boolean e_xer = is_exer(p_flavor);\n"
        "  if (e_xer && (p_td.xer_bits & UNTAGGED)) return FALSE;\n"
        "  else return check_name(p_name, p_td, e_xer) && (!e_xer || check_namespace(p_uri, p_td));\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"
        "  int encoded_length=(int)p_buf.get_len();\n"
        "  int is_indented = !is_canonical(p_flavor);\n"
        "  boolean e_xer = is_exer(p_flavor);\n"
        "  if (is_indented) do_indent(p_buf, p_indent);\n"
        "  p_buf.put_c('<');\n"
        "  if (e_xer) write_ns_prefix(p_td, p_buf);\n"
        "  p_buf.put_s((size_t)p_td.namelens[e_xer]-2, (cbyte*)p_td.names[e_xer]);\n"
        "  p_buf.put_s(2 + is_indented, (cbyte*)\"/>\\n\");\n"
        "  return (int)p_buf.get_len() - encoded_length;\n"
        "}\n\n"
        , name);
      src = mputprintf(src,
#ifndef NDEBUG
        "// written by %s in " __FILE__ " at %d\n"
#endif
        "int %s::XER_decode(const XERdescriptor_t& p_td, XmlReaderWrap& p_reader, "
        "unsigned int p_flavor, unsigned int /*p_flavor2*/, embed_values_dec_struct_t*)\n"
        "{\n"
        "  boolean e_xer = is_exer(p_flavor);\n"
        "  bound_flag = TRUE;\n"
        "  int rd_ok, depth=-1;\n"
        "  for (rd_ok=p_reader.Ok(); rd_ok==1; rd_ok=p_reader.Read()) {\n"
        "    int type = p_reader.NodeType();\n"
        "    if (type==XML_READER_TYPE_ELEMENT) {\n"
        "      verify_name(p_reader, p_td, e_xer);\n"
        "      depth=p_reader.Depth();\n"
        "      if (p_reader.IsEmptyElement()) {\n"
        "        rd_ok = p_reader.Read(); break;\n"
        "      }\n"
        "      else if ((p_flavor & XER_MASK) == XER_CANONICAL) {\n"
        "        TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_INVAL_MSG, "
        "\"Expected an empty element tag\");\n"
        "      }\n"
        "    }\n"
        "    else if (type == XML_READER_TYPE_END_ELEMENT && depth != -1) {\n"
        "      verify_end(p_reader, p_td, depth, e_xer);\n"
        "      rd_ok = p_reader.Read(); break;\n"
        "    }\n"
        "  }\n"
        "  return 1;\n"
        "}\n\n"
#ifndef NDEBUG
        , __FUNCTION__, __LINE__
#endif
        , name);
    }
  if (json_needed) {
    // JSON encode, RT1
    src = mputprintf(src,
      "int %s::JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok, boolean) const\n"
      "{\n"
      "  if (!is_bound()) {\n"
      "    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_UNBOUND,\n"
      "      \"Encoding an unbound value of type %s.\");\n"
      "    return -1;\n"
      "  }\n\n"
      "  return p_tok.put_next_token(JSON_TOKEN_OBJECT_START, NULL) + \n"
      "    p_tok.put_next_token(JSON_TOKEN_OBJECT_END, NULL);\n"
      "}\n\n"
      , name, dispname);
    
    // JSON decode, RT1
    src = mputprintf(src,
      "int %s::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, boolean, int)\n"
      "{\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 0;\n"
      "  }\n"
      "  if (p_td.json->default_value.type != JD_UNSET && 0 == p_tok.get_buffer_length()) {\n"
      // use the default value
      "    bound_flag = TRUE;\n"
      "    return strlen(p_td.json->default_value.str);\n"
      "  }\n"
      "  json_token_t token = JSON_TOKEN_NONE;\n"
      "  size_t dec_len = p_tok.get_next_token(&token, NULL, NULL);\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_OBJECT_START != token) {\n"
      "    return JSON_ERROR_INVALID_TOKEN;\n"
      "  }\n\n"
      "  dec_len += p_tok.get_next_token(&token, NULL, NULL);\n"
      "  if (JSON_TOKEN_OBJECT_END != token) {\n"
      "    JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_STATIC_OBJECT_END_TOKEN_ERROR, \"\");\n"
      "    return JSON_ERROR_FATAL;\n"
      "  }\n\n"
      "  bound_flag = TRUE;\n\n"
      "  return (int)dec_len;\n"
      "}\n\n"
      , name, name);
  }
    
  if (oer_needed) {
    // OER encode, RT1
    src = mputprintf(src,
      "int %s::OER_encode(const TTCN_Typedescriptor_t&, TTCN_Buffer&%s) const\n"
      "{\n"
      "  if (!is_bound()) {\n"
      "    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_UNBOUND,\n"
      "      \"Encoding an unbound value of type %s.\");\n"
      "    return -1;\n"
      "  }\n\n"
      "%s"
      "  return 0;\n"
      "}\n\n"
      , name, sdef->oerExtendable ? " p_buf" : "", dispname
      , sdef->oerExtendable ? "  p_buf.put_c(0);\n" : "");
    
    // OER decode, RT1
    src = mputprintf(src,
      "int %s::OER_decode(const TTCN_Typedescriptor_t&, TTCN_Buffer&%s, OER_struct&)\n"
      "{\n"
      "  bound_flag = TRUE;\n"
      , name, sdef->oerExtendable ? " p_buf" : "");
    if (sdef->oerExtendable) {
      src = mputstr(src,
        "  const unsigned char* uc = p_buf.get_read_data();\n"
        "  boolean has_extension = (uc[0] & 0x80) != 0;\n"
        "  p_buf.increase_pos(1);\n"
        "  if (has_extension) {\n"
        "    size_t bytes = decode_oer_length(p_buf, FALSE);\n"
        "    p_buf.increase_pos(bytes);\n"
        // TODO: handle extension fields
        "  }\n");
    }
    src = mputstr(src,
      "  return 0;\n"
      "}\n\n");      
  }

  if (per_needed) {
    // PER encode, RT1
    src = mputprintf(src,
      "void %s::PER_encode(const TTCN_Typedescriptor_t& p_td, TTCN_Buffer& p_buf, int p_options) const\n"
      "{\n"
      "if (!is_bound()) {\n" 
      "TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_UNBOUND, "
      "\"Encoding an unbound %s value.\");\n"
      "return;\n"
      "}\n", name, sdef->kind == SET ? "SET" : "SEQUENCE");
    if (sdef->perExtendable) {
      src = mputstr(src, "p_buf.PER_put_bit(FALSE);\n");
    }
    src = mputstr(src, "}\n\n"); // end of function

    // PER decode, RT1
    src = mputprintf(src,
      "void %s::PER_decode(const TTCN_Typedescriptor_t& p_td, TTCN_Buffer& p_buf, int p_options)\n"
      "{\n"
      "bound_flag = TRUE;\n", name);
    
    if (sdef->perExtendable) {
      src = mputstr(src,
        "if (p_buf.PER_get_bit()) {\n"
        // extension addition presence bits (we are only interested in how many of them are present)
        "int nof_extensions = 0;\n"
        "INTEGER length;\n"
        "int normally_small_length = TRUE;\n"
        "int mul_16k;\n"
        "do {\n"
        "mul_16k = length.PER_decode_length(p_buf, p_options, normally_small_length);\n"
        "if (mul_16k > 0) normally_small_length = FALSE;\n"
        "for (int i = 0; i < length; ++i) {\n"
        "if (p_buf.PER_get_bit()) ++nof_extensions;\n"
        "}\n"
        "}\n"
        "while (mul_16k > 0);\n"
        // jump through as many extension additions as were marked present
        "for (int i = 0; i < nof_extensions; ++i) {\n"
        "PER_skip_opentype(p_buf, p_options);\n"
        "}\n"
        "}\n");
    }
    
    src = mputstr(src, "}\n\n");
  }

    /* closing class definition */
    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);

    output->header.function_prototypes =
	mputprintf(output->header.function_prototypes,
	"extern boolean operator==(null_type null_value, const %s& "
	    "other_value);\n", name);
    output->source.function_bodies =
	mputprintf(output->source.function_bodies,
	"boolean operator==(null_type, const %s& other_value)\n"
	"{\n"
	"other_value.must_bound(\"Comparison of an unbound value of type "
	"%s.\");\n"
	"return TRUE;\n"
	"}\n\n", name, dispname);

    output->header.function_prototypes =
	mputprintf(output->header.function_prototypes,
	"inline boolean operator!=(null_type null_value, const %s& "
	    "other_value) "
	"{ return !(null_value == other_value); }\n", name);
}

static void defEmptyRecordTemplate(const char *name, const char *dispname,
    const char* base_class_name, output_struct *output)
{
    char *def = NULL, *src = NULL;

    /* class definition */
    def = mprintf("class %s_template : public %s {\n"
	"union {\n"
	"struct {\n"
	"unsigned int n_values;\n"
	"%s_template *list_value;\n"
	"} value_list;\n"
	"struct {\n"
  "%s_template* precondition;\n"
  "%s_template* implied_template;\n"
  "} implication_;\n"
  "dynmatch_struct<%s>* dyn_match;\n"
	"};\n", name, base_class_name, name, name, name, name);

    /* copy_template function */
    def = mputprintf(def,  "void copy_template(const %s_template& "
	"other_value);\n\n", name);
    src = mputprintf(src,
      "void %s_template::copy_template(const %s_template& other_value)\n"
      "{\n"
      "set_selection(other_value);\n"
      "switch (template_selection) {\n"
      "case OMIT_VALUE:\n"
      "case ANY_VALUE:\n"
      "case ANY_OR_OMIT:\n"
      "case SPECIFIC_VALUE:\n"
      "break;\n"
      "case VALUE_LIST:\n"
      "case COMPLEMENTED_LIST:\n"
      "case CONJUNCTION_MATCH:\n"
      "value_list.n_values = other_value.value_list.n_values;\n"
      "value_list.list_value = new %s_template[value_list.n_values];\n"
      "for (unsigned int list_count = 0; list_count < value_list.n_values; "
          "list_count++)\n"
      "value_list.list_value[list_count].copy_template("
          "other_value.value_list.list_value[list_count]);\n"
      "break;\n"
      "case IMPLICATION_MATCH:\n"
      "implication_.precondition = new %s_template(*other_value.implication_.precondition);\n"
      "implication_.implied_template = new %s_template(*other_value.implication_.implied_template);\n"
      "break;\n"
      "case DYNAMIC_MATCH:\n"
      "dyn_match = other_value.dyn_match;\n"
      "dyn_match->ref_count++;\n"
      "break;\n"
      "default:\n"
      "TTCN_error(\"Copying an uninitialized/unsupported template of type "
      "%s.\");\n"
      "break;\n"
      "}\n"
      "}\n\n", name, name, name, name, name, dispname);

    /* default ctor */
    def = mputprintf(def, "public:\n"
	"%s_template();\n", name);
    src = mputprintf(src, "%s_template::%s_template()\n"
	"{\n"
	"}\n\n", name, name);

    /* ctor for generic wildcards */
    def = mputprintf(def, "%s_template(template_sel other_value);\n", name);
    src = mputprintf(src, "%s_template::%s_template(template_sel other_value)\n"
	" : %s(other_value)\n"
	"{\n"
	"check_single_selection(other_value);\n"
	"}\n\n", name, name, base_class_name);

    /* ctor for value {} */
    def = mputprintf(def, "%s_template(null_type other_value);\n", name);
    src = mputprintf(src, "%s_template::%s_template(null_type)\n"
	" : %s(SPECIFIC_VALUE)\n"
	"{\n"
	"}\n\n", name, name, base_class_name);

    /* ctor for specific value */
    def = mputprintf(def, "%s_template(const %s& other_value);\n", name, name);
    src = mputprintf(src, "%s_template::%s_template(const %s& other_value)\n"
	" : %s(SPECIFIC_VALUE)\n"
	"{\n"
	"other_value.must_bound(\"Creating a template from an unbound value of "
	    "type %s.\");\n"
	"}\n\n", name, name, name, base_class_name, dispname);

    /* ctor for optional value */
    def = mputprintf(def, "%s_template(const OPTIONAL<%s>& other_value);\n",
	name, name);
    src = mputprintf(src, "%s_template::%s_template(const OPTIONAL<%s>& "
	"other_value)\n"
	"{\n"
	"switch (other_value.get_selection()) {\n"
	"case OPTIONAL_PRESENT:\n"
	"set_selection(SPECIFIC_VALUE);\n"
	"break;\n"
	"case OPTIONAL_OMIT:\n"
	"set_selection(OMIT_VALUE);\n"
	"break;\n"
	"default:\n"
	"TTCN_error(\"Creating a template of type %s from an unbound optional "
	    "field.\");\n"
	"}\n"
	"}\n\n", name, name, name, dispname);

    /* constructor t1_template(t1_template*& p_precondition, t1_template* p_implied_template) */
    def = mputprintf(def, "%s_template(%s_template* p_precondition, "
      "%s_template* p_implied_template);\n", name, name, name);
    src = mputprintf(src, "%s_template::%s_template(%s_template* p_precondition, "
      "%s_template* p_implied_template)\n"
      " : %s(IMPLICATION_MATCH)\n"
      "{\n"
      "implication_.precondition = p_precondition;\n"
      "implication_.implied_template = p_implied_template;\n"
      "}\n\n", name, name, name, name, base_class_name);

    /* constructor t1_template(Dynamic_Match_Interface<t1>* p_dyn_match) */
    def = mputprintf(def, "%s_template(Dynamic_Match_Interface<%s>* p_dyn_match);\n", name, name);
    src = mputprintf(src, "%s_template::%s_template(Dynamic_Match_Interface<%s>* p_dyn_match)\n"
      " : %s(DYNAMIC_MATCH)\n"
      "{\n"
      "dyn_match = new dynmatch_struct<%s>;\n"
      "dyn_match->ptr = p_dyn_match;\n"
      "dyn_match->ref_count = 1;\n"
      "}\n\n", name, name, name, base_class_name, name);

    /* copy ctor */
    def = mputprintf(def, "%s_template(const %s_template& other_value);\n",
	name, name);
    src = mputprintf(src, "%s_template::%s_template(const %s_template& "
	"other_value)\n"
	": %s()" /* yes, the base class _default_ constructor */
	"{\n"
	"copy_template(other_value);\n"
	"}\n\n", name, name, name, base_class_name);

    /* dtor */
    def = mputprintf(def, "~%s_template();\n", name);
    src = mputprintf(src, "%s_template::~%s_template()\n"
	"{\n"
	"clean_up();\n"
	"}\n\n", name, name);

    /* clean_up function */
    def = mputstr(def,  "void clean_up();\n");
    src = mputprintf(src, "void %s_template::clean_up()\n"
	"{\n"
	"switch (template_selection) {\n"
	"case VALUE_LIST:\n"
	"case COMPLEMENTED_LIST:\n"
	"case CONJUNCTION_MATCH:\n"
	"delete [] value_list.list_value;\n"
	"break;\n"
	"case IMPLICATION_MATCH:\n"
  "delete implication_.precondition;\n"
  "delete implication_.implied_template;\n"
  "break;\n"
  "case DYNAMIC_MATCH:\n"
  "dyn_match->ref_count--;\n"
  "if (dyn_match->ref_count == 0) {\n"
  "delete dyn_match->ptr;\n"
  "delete dyn_match;\n"
  "}\n"
  "break;\n"
  "default:\n"
  "break;\n"
  "}\n"
	"template_selection = UNINITIALIZED_TEMPLATE;\n"
	"}\n\n", name);

    /* assignment op for generic wildcards */
    def = mputprintf(def, "%s_template& operator=(template_sel other_value);\n",
	name);
    src = mputprintf(src, "%s_template& %s_template::operator=(template_sel "
	"other_value)\n"
	"{\n"
	"check_single_selection(other_value);\n"
	"clean_up();\n"
	"set_selection(other_value);\n"
	"return *this;\n"
	"}\n\n", name, name);

    /* assignment op for value {} */
    def = mputprintf(def, "%s_template& operator=(null_type other_value);\n",
	name);
    src = mputprintf(src, "%s_template& %s_template::operator=(null_type)\n"
	"{\n"
	"clean_up();\n"
	"set_selection(SPECIFIC_VALUE);\n"
	"return *this;\n"
	"}\n\n", name, name);

    /* assignment op for specific value */
    def = mputprintf(def, "%s_template& operator=(const %s& other_value);\n",
	name, name);
    src = mputprintf(src, "%s_template& %s_template::operator=(const %s& "
	"other_value)\n"
	"{\n"
	"other_value.must_bound(\"Assignment of an unbound value of type %s "
	    "to a template.\");\n"
	"clean_up();\n"
	"set_selection(SPECIFIC_VALUE);\n"
	"return *this;\n"
	"}\n\n", name, name, name, dispname);

    /* assignment op for optional value */
    def = mputprintf(def, "%s_template& operator=(const OPTIONAL<%s>& "
	"other_value);\n", name, name);
    src = mputprintf(src, "%s_template& %s_template::operator="
	"(const OPTIONAL<%s>& other_value)\n"
	"{\n"
	"clean_up();\n"
	"switch (other_value.get_selection()) {\n"
	"case OPTIONAL_PRESENT:\n"
	"set_selection(SPECIFIC_VALUE);\n"
	"break;\n"
	"case OPTIONAL_OMIT:\n"
	"set_selection(OMIT_VALUE);\n"
	"break;\n"
	"default:\n"
	"TTCN_error(\"Assignment of an unbound optional field to a template "
	  "of type %s.\");\n"
	"}\n"
	"return *this;\n"
	"}\n\n", name, name, name, dispname);

    /* assignment op for itself */
    def = mputprintf(def, "%s_template& operator=(const %s_template& "
	"other_value);\n", name, name);
    src = mputprintf(src, "%s_template& %s_template::operator="
	"(const %s_template& other_value)\n"
	"{\n"
	"if (&other_value != this) {\n"
	"clean_up();\n"
	"set_selection(other_value);\n"
	"}\n"
	"return *this;\n"
	"}\n\n", name, name, name);

    /* match operation with {} */
    def = mputstr(def, "boolean match(null_type other_value, boolean legacy "
      "= FALSE) const;\n");
    src = mputprintf(src, "boolean %s_template::match(null_type other_value,"
      "boolean) const\n"
	"{\n"
	"switch (template_selection) {\n"
	"case ANY_VALUE:\n"
	"case ANY_OR_OMIT:\n"
	"case SPECIFIC_VALUE:\n"
	"return TRUE;\n"
	"case OMIT_VALUE:\n"
	"return FALSE;\n"
	"case VALUE_LIST:\n"
	"case COMPLEMENTED_LIST:\n"
	"for (unsigned int list_count = 0; list_count < value_list.n_values; "
	    "list_count++)\n"
	"if (value_list.list_value[list_count].match(other_value)) "
	    "return template_selection == VALUE_LIST;\n"
	"return template_selection == COMPLEMENTED_LIST;\n"
  "case CONJUNCTION_MATCH:\n"
  "for (unsigned int i = 0; i < value_list.n_values; i++) {\n"
  "if (!value_list.list_value[i].match(other_value)) {\n"
  "return FALSE;\n"
  "}\n"
  "}\n"
  "return TRUE;\n"
  "case IMPLICATION_MATCH:\n"
  "return !implication_.precondition->match(other_value) || implication_.implied_template->match(other_value);\n"
  "case DYNAMIC_MATCH:\n"
  "return dyn_match->ptr->match(other_value);\n"
	"default:\n"
	"TTCN_error(\"Matching an uninitialized/unsupported template of "
	    "type %s.\");\n"
	"}\n"
	"return FALSE;\n"
	"}\n\n", name, dispname);

    /* match operation with specific value */
    def = mputprintf(def, "boolean match(const %s& other_value, boolean legacy "
      "= FALSE) const;\n", name);
    src = mputprintf(src, "boolean %s_template::match(const %s& other_value, "
      "boolean) const\n"
	"{\n"
    "if (!other_value.is_bound()) return FALSE;\n"
	"return match(NULL_VALUE);\n"
	"}\n\n", name, name);

    /* valueof operation */
    def = mputprintf(def, "%s valueof() const;\n", name);
    src = mputprintf(src, "%s %s_template::valueof() const\n"
	"{\n"
	"if (template_selection != SPECIFIC_VALUE || is_ifpresent) "
            "TTCN_error(\"Performing a valueof or send operation on a "
            "non-specific template of type %s.\");\n"
	"return NULL_VALUE;\n"
	"}\n\n", name, name, dispname);

    /* void set_type(template_sel, int) function */
    def = mputstr(def,
      "void set_type(template_sel template_type, "
          "unsigned int list_length);\n");

        src = mputprintf(src,
      "void %s_template::set_type(template_sel template_type, "
          "unsigned int list_length)\n"
      "{\n"
      "if (template_type != VALUE_LIST "
          "&& template_type != COMPLEMENTED_LIST"
          "&& template_type != CONJUNCTION_MATCH)\n"
      "TTCN_error(\"Setting an invalid list for a template of type %s.\");\n"
      "clean_up();\n"
      "set_selection(template_type);\n"
      "value_list.n_values = list_length;\n"
      "value_list.list_value = new %s_template[list_length];\n"
      "}\n\n", name, dispname, name);

    /* list_item(int) function */

    def = mputprintf(def,
	"%s_template& list_item(unsigned int list_index) const;\n", name);

    src = mputprintf(src,
      "%s_template& %s_template::list_item(unsigned int list_index) const\n"
      "{\n"
      "if (template_selection != VALUE_LIST "
          "&& template_selection != COMPLEMENTED_LIST"
          "&& template_selection != CONJUNCTION_MATCH)\n"
      "TTCN_error(\"Accessing a list element of a non-list template of "
      "type %s.\");\n"
      "if (list_index >= value_list.n_values)\n"
      "TTCN_error(\"Index overflow in a value list template of type "
      "%s.\");\n"
      "return value_list.list_value[list_index];\n"
      "}\n\n", name, name, dispname, dispname);

    /* log function */
    def = mputstr(def, "void log() const;\n");
    src = mputprintf(src, "void %s_template::log() const\n"
      "{\n"
      "switch (template_selection) {\n"
      "case SPECIFIC_VALUE:\n"
      "TTCN_Logger::log_event_str(\"{ }\");\n"
      "break;\n"
      "case COMPLEMENTED_LIST:\n"
      "TTCN_Logger::log_event_str(\"complement\");\n"
      "case CONJUNCTION_MATCH:\n"
      "if (template_selection == CONJUNCTION_MATCH) {\n"
      "TTCN_Logger::log_event_str(\"conjunct\");\n"
      "}\n"
      "case VALUE_LIST:\n"
      "TTCN_Logger::log_char('(');\n"
      "for (unsigned int list_count = 0; list_count < value_list.n_values; "
          "list_count++) {\n"
      "if (list_count > 0) TTCN_Logger::log_event_str(\", \");\n"
      "value_list.list_value[list_count].log();\n"
      "}\n"
      "TTCN_Logger::log_char(')');\n"
      "break;\n"
      "case IMPLICATION_MATCH:\n"
      "implication_.precondition->log();\n"
      "TTCN_Logger::log_event_str(\" implies \");\n"
      "implication_.implied_template->log();\n"
      "break;\n"
      "case DYNAMIC_MATCH:\n"
      "TTCN_Logger::log_event_str(\"@dynamic template\");\n"
      "break;\n"
      "default:\n"
      "log_generic();\n"
      "}\n"
      "log_ifpresent();\n"
      "}\n\n", name);

    /* log_match function */
    def = mputprintf(def, "void log_match(const %s& match_value, "
      "boolean legacy = FALSE) const;\n", name);
    src = mputprintf(src, "void %s_template::log_match(const %s& match_value, "
      "boolean) const\n"
	"{\n"
	"match_value.log();\n"
	"TTCN_Logger::log_event_str(\" with \");\n"
	"log();\n"
	"if (match(match_value)) TTCN_Logger::log_event_str(\" matched\");\n"
	"else TTCN_Logger::log_event_str(\" unmatched\");\n"
	"}\n\n", name, name);

    /* encode_text function */
    def = mputstr(def, "void encode_text(Text_Buf& text_buf) const;\n");
    src = mputprintf(src, "void %s_template::encode_text(Text_Buf& text_buf) "
	    "const\n"
	"{\n"
	"encode_text_base(text_buf);\n"
	"switch (template_selection) {\n"
	"case OMIT_VALUE:\n"
	"case ANY_VALUE:\n"
	"case ANY_OR_OMIT:\n"
	"case SPECIFIC_VALUE:\n"
	"break;\n"
	"case VALUE_LIST:\n"
	"case COMPLEMENTED_LIST:\n"
	"text_buf.push_int(value_list.n_values);\n"
	"for (unsigned int list_count = 0; list_count < value_list.n_values; "
	    "list_count++)\n"
	"value_list.list_value[list_count].encode_text(text_buf);\n"
	"break;\n"
	"default:\n"
	"TTCN_error(\"Text encoder: Encoding an uninitialized/unsupported "
	    "template of type %s.\");\n"
	"}\n"
	"}\n\n", name, dispname);

    /* decode_text function */
    def = mputstr(def, "void decode_text(Text_Buf& text_buf);\n");
    src = mputprintf(src, "void %s_template::decode_text(Text_Buf& text_buf)\n"
	"{\n"
	"clean_up();\n"
	"decode_text_base(text_buf);\n"
	"switch (template_selection) {\n"
	"case OMIT_VALUE:\n"
	"case ANY_VALUE:\n"
	"case ANY_OR_OMIT:\n"
	"case SPECIFIC_VALUE:\n"
	"break;\n"
	"case VALUE_LIST:\n"
	"case COMPLEMENTED_LIST:\n"
	"value_list.n_values = text_buf.pull_int().get_val();\n"
	"value_list.list_value = new %s_template[value_list.n_values];\n"
	"for (unsigned int list_count = 0; list_count < value_list.n_values; "
	    "list_count++)\n"
	"value_list.list_value[list_count].decode_text(text_buf);\n"
	"break;\n"
	"default:\n"
	"TTCN_error(\"Text decoder: An unknown/unsupported selection was "
	    "received in a template of type %s.\");\n"
	"}\n"
	"}\n\n", name, name, dispname);

  /* set_param() */
  def = mputstr(def, "void set_param(Module_Param& param);\n");
  src = mputprintf(src,
    "void %s_template::set_param(Module_Param& param)\n"
    "{\n"
    "  param.basic_check(Module_Param::BC_TEMPLATE, \"empty record/set template\");\n"
    "  switch (param.get_type()) {\n"
    "  case Module_Param::MP_Omit:\n"
    "    *this = OMIT_VALUE;\n"
    "    break;\n"
    "  case Module_Param::MP_Any:\n"
    "    *this = ANY_VALUE;\n"
    "    break;\n"
    "  case Module_Param::MP_AnyOrNone:\n"
    "    *this = ANY_OR_OMIT;\n"
    "    break;\n"
    "  case Module_Param::MP_List_Template:\n"
    "  case Module_Param::MP_ComplementList_Template:\n"
    "  case Module_Param::MP_ConjunctList_Template: {\n"
    "    %s_template temp;\n"
    "    temp.set_type(param.get_type()==Module_Param::MP_List_Template ? "
    "VALUE_LIST : (param.get_type() == Module_Param::MP_ConjunctList_Template ? "
    "CONJUNCTION_MATCH : COMPLEMENTED_LIST), param.get_size());\n"
    "    for (size_t p_i=0; p_i<param.get_size(); p_i++) {\n"
    "      temp.list_item(p_i).set_param(*param.get_elem(p_i));\n"
    "    }\n"
    "    *this = temp;\n"
    "    break; }\n"
    "  case Module_Param::MP_Value_List:\n"
    "    if (param.get_size()>0) param.type_error(\"empty record/set template\", \"%s\");\n"
    "    *this = NULL_VALUE;\n"
    "    break;\n"
    "  case Module_Param::MP_Implication_Template: {\n"
    "    %s_template* precondition = new %s_template;\n"
    "    precondition->set_param(*param.get_elem(0));\n"
    "    %s_template* implied_template = new %s_template;\n"
    "    implied_template->set_param(*param.get_elem(1));\n"
    "    *this = %s_template(precondition, implied_template);\n"
    "  } break;\n"
    "  default:\n"
    "    param.type_error(\"empty record/set template\", \"%s\");\n"
    "  }\n"
    "  is_ifpresent = param.get_ifpresent();\n"
    "}\n\n", name, name, dispname, name, name, name, name, name, dispname);

    /* check template restriction */
    def = mputstr(def, "void check_restriction(template_res t_res, "
        "const char* t_name=NULL, boolean legacy = FALSE) const;\n");
    src = mputprintf(src,
        "void %s_template::check_restriction("
          "template_res t_res, const char* t_name, boolean legacy) const\n"
        "{\n"
        "if (template_selection==UNINITIALIZED_TEMPLATE) return;\n"
        "switch ((t_name&&(t_res==TR_VALUE))?TR_OMIT:t_res) {\n"
        "case TR_OMIT:\n"
        "if (template_selection==OMIT_VALUE) return;\n"
        "case TR_VALUE:\n"
        "if (template_selection!=SPECIFIC_VALUE || is_ifpresent) break;\n"
        "return;\n"
        "case TR_PRESENT:\n"
        "if (!match_omit(legacy)) return;\n"
        "break;\n"
        "default:\n"
        "return;\n"
        "}\n"
        "TTCN_error(\"Restriction `%%s' on template of type %%s "
          "violated.\", get_res_name(t_res), t_name ? t_name : \"%s\");\n"
        "}\n\n", name, dispname);

    defCommonRecordTemplate(name, &def, &src);

    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);
}

static void defCommonRecordTemplate(const char *name,
  char **def, char **src)
{
    /* TTCN-3 ispresent() function */
    *def = mputstr(*def, "boolean is_present(boolean legacy = FALSE) const;\n");
    *src = mputprintf(*src,
        "boolean %s_template::is_present(boolean legacy) const\n"
        "{\n"
        "if (template_selection==UNINITIALIZED_TEMPLATE) return FALSE;\n"
        "return !match_omit(legacy);\n"
        "}\n\n", name);

    /* match_omit() */
    *def = mputstr(*def, "boolean match_omit(boolean legacy = FALSE) const;\n");
    *src = mputprintf(*src,
        "boolean %s_template::match_omit(boolean legacy) const\n"
        "{\n"
        "if (is_ifpresent) return TRUE;\n"
        "switch (template_selection) {\n"
        "case OMIT_VALUE:\n"
        "case ANY_OR_OMIT:\n"
        "return TRUE;\n"
        "case IMPLICATION_MATCH:\n"
        "return !implication_.precondition->match_omit() || implication_.implied_template->match_omit();\n"
        "case VALUE_LIST:\n"
        "case COMPLEMENTED_LIST:\n"
        "if (legacy) {\n"
        "for (unsigned int l_idx=0; l_idx<value_list.n_values; l_idx++)\n"
        "if (value_list.list_value[l_idx].match_omit())\n"
        "return template_selection==VALUE_LIST;\n"
        "return template_selection==COMPLEMENTED_LIST;\n"
        "} // else fall through\n"
        "default:\n"
        "return FALSE;\n"
        "}\n"
        "return FALSE;\n"
        "}\n\n", name);
}

/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

void defRecordClass2(const struct_def *sdef, output_struct *output)
{
  size_t i;
  size_t optional_num = 0;
  const char *name = sdef->name;
  char *def = NULL, *src = NULL;

  boolean xer_needed = sdef->hasXer && enable_xer();
  boolean raw_needed = sdef->hasRaw && enable_raw();
  boolean json_needed = sdef->hasJson && enable_json();
  boolean has_optional = FALSE;
  boolean has_optional_asn = FALSE;
  boolean has_default  = FALSE;
  boolean has_sensitive = FALSE;

  const char* base_class = (sdef->nElements==0) ? "Empty_Record_Type" : "Record_Type";

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

  /* class definition and source code */
  def=mputprintf(def, "class %s : public %s {\n", name, base_class);

  /* data members */
  for (i = 0; i < sdef->nElements; i++) {
    if(sdef->elements[i].isOptional) {
      optional_num++;
      def = mputprintf(def, "  OPTIONAL<%s> field_%s;\n",
                       sdef->elements[i].type,
                       sdef->elements[i].name);
    } else {
      def = mputprintf(def, "  %s field_%s;\n",
                       sdef->elements[i].type, sdef->elements[i].name);
    }
  }
  if (sdef->nElements) {
    def = mputprintf(def, "  Base_Type* fld_vec[%lu];\n", (unsigned long)sdef->nElements);
  }

  /* default constructor */
  if (sdef->nElements==0) {
    def = mputprintf(def, "public:\n"
    "  %s();\n", name);
    src = mputprintf(src,
      "%s::%s() : Empty_Record_Type() {}\n\n",
      name, name);
  } else {
    def = mputstr(def, "  void init_vec();\n");
    src = mputprintf(src, "void %s::init_vec() { ", name);
    for (i = 0; i < sdef->nElements; i++) {
      src = mputprintf(src, "fld_vec[%lu]=&field_%s; ",
        (unsigned long)i, sdef->elements[i].name);
    }
    src = mputstr(src, " }\n\n");

    def = mputprintf(def, "public:\n"
        "  %s();\n", name);
    src = mputprintf(src,
        "%s::%s() : Record_Type() { init_vec(); }\n\n", name, name);
  }

  /* copy constructor */
  if (sdef->nElements) {
    def = mputprintf(def, "  %s(const %s& other_value);\n", name, name);
    src = mputprintf(src, "%s::%s(const %s& other_value) : Record_Type(other_value)\n", name, name, name);
    src = mputstr(src, "{\n"
      "  if(!other_value.is_bound()) "
      "TTCN_error(\"Copying an unbound record/set value.\");\n");
    for (i = 0; i < sdef->nElements; i++) {
      src = mputprintf(src,
          "if (other_value.field_%s.is_bound() )\n"
          "  field_%s = other_value.field_%s;\n",
          sdef->elements[i].name, sdef->elements[i].name,
          sdef->elements[i].name);
    }

    src = mputstr(src, "init_vec();\n"
                  "}\n\n");
  } else {
    def = mputprintf(def, "  %s(const %s& other_value): %s(other_value) {}\n", name, name, base_class);
  }

  if (sdef->nElements>0) { /* constructor by fields */
    def = mputprintf(def, "  %s(", name);
    src = mputprintf(src, "%s::%s(", name, name);
    for (i = 0; i < sdef->nElements; i++) {
      char *tmp = NULL;
      if (i > 0) tmp = mputstr(tmp, ",\n    ");
      if (sdef->elements[i].isOptional)
        tmp = mputprintf
          (tmp,
           "const OPTIONAL<%s>& par_%s",
           sdef->elements[i].type, sdef->elements[i].name);
      else
        tmp = mputprintf
          (tmp,
           "const %s& par_%s",
           sdef->elements[i].type, sdef->elements[i].name);
      def = mputstr(def, tmp);
      src = mputstr(src, tmp);
      Free(tmp);
    }
    def = mputstr(def, ");\n");
    src = mputprintf(src, ") : ");
    for (i = 0; i < sdef->nElements; i++) {
      if (i > 0) src = mputstr(src, ",\n    ");
      src = mputprintf(src, "field_%s(par_%s)", sdef->elements[i].name,
                       sdef->elements[i].name);
    }
    src = mputstr(src, "\n"
                  "{\n"
                  "init_vec();\n"
                  "}\n\n");
  } else { /* constructor from null */
    def = mputprintf(def, "  %s(null_type) {bound_flag = TRUE;}\n", name);
  }

  /* assignment operators */
  def = mputprintf(def, "inline %s& operator=(const %s& other_value) "
    "{ set_value(&other_value); return *this; }\n\n", name, name);
  if (sdef->nElements == 0) {
    def = mputprintf(def, "inline %s& operator=(null_type) "
      "{ bound_flag = TRUE; return *this; }\n", name);
  }

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

  for (i = 0; i < sdef->nElements; i++) {
    if(sdef->elements[i].isOptional)
      def = mputprintf
        (def,
         "  inline OPTIONAL<%s>& %s()\n"
         "    {return field_%s;}\n"
         "  inline const OPTIONAL<%s>& %s() const\n"
         "    {return field_%s;}\n",
         sdef->elements[i].type, sdef->elements[i].name,
         sdef->elements[i].name,
         sdef->elements[i].type, sdef->elements[i].name,
         sdef->elements[i].name);
    else def = mputprintf
           (def,
            "  inline %s& %s()\n"
            "    {return field_%s;}\n"
            "  inline const %s& %s() const\n"
            "    {return field_%s;}\n",
	    sdef->elements[i].type, sdef->elements[i].name,
	    sdef->elements[i].name, sdef->elements[i].type,
	    sdef->elements[i].name, sdef->elements[i].name);

  }

  /* override virtual functions where needed */
  def = mputprintf(def,
    "Base_Type* clone() const { return new %s(*this); }\n"
    "const TTCN_Typedescriptor_t* get_descriptor() const;\n"
    "boolean is_set() const { return %s; }\n",
    name,
    (sdef->kind==SET) ? "TRUE" : "FALSE");
  src = mputprintf(src,
    "const TTCN_Typedescriptor_t* %s::get_descriptor() const { return &%s_descr_; }\n",
    name, name);

  if (sdef->nElements > 0) {

    /* field access functions */
    def = mputprintf(def,
      "Base_Type* get_at(int index_value) { return fld_vec[index_value]; }\n"
      "const Base_Type* get_at(int index_value) const { return fld_vec[index_value]; }\n\n"
      "int get_count() const { return %lu; }\n", (unsigned long)sdef->nElements);

    /* override if there are optional fields */
    if (optional_num) {
      def = mputprintf(def,
        "int optional_count() const { return %lu; }\n", (unsigned long)optional_num);
    }

    if (sdef->opentype_outermost)
      def = mputstr(def, "boolean is_opentype_outermost() const { return TRUE; }\n");

    /* FIXME: use static member in Record_Type and initialize somewhere */
    if (default_as_optional)
      def = mputstr(def, "boolean default_as_optional() const { return TRUE; }\n");

    for (i = 0; i < sdef->nElements; i++) {
      if (sdef->elements[i].isOptional) {
        has_optional = TRUE;
        break;
      }
    }

    for (i = 0; i < sdef->nElements; i++) {
      if (sdef->elements[i].isOptionalAsn) {
        has_optional_asn = TRUE;
        break;
      }
    }

    for (i = 0; i < sdef->nElements; i++) {
      if (sdef->elements[i].isDefault) {
        has_default = TRUE;
        break;
      }
    }

    for (i = 0; i < sdef->nElements; i++) {
      if (sdef->elements[i].sensitive_data) {
        has_sensitive = TRUE;
        break;
      }
    }

    def = mputstr(def,
      "static const TTCN_Typedescriptor_t* fld_descriptors[];\n"
      "const TTCN_Typedescriptor_t* fld_descr(int p_index) const;\n\n"
      "static const char* fld_names[];\n"
      "const char* fld_name(int p_index) const;\n\n");

    src = mputprintf(src, "const TTCN_Typedescriptor_t* %s::fld_descriptors[] = ", name);
    for (i = 0; i < sdef->nElements; i++) {
      src = mputprintf(src, "%c &%s_descr_",
                       (i ? ',' : '{'), sdef->elements[i].typedescrname);
    }
    src = mputstr(src, " };\n");
    src = mputprintf(src,
      "const TTCN_Typedescriptor_t* %s::fld_descr(int p_index) const "
      "{ return fld_descriptors[p_index]; }\n\n", name);

    src = mputprintf(src, "const char* %s::fld_names[] = ", name);
    for (i = 0; i < sdef->nElements; i++) {
      src = mputprintf(src, "%c \"%s\"",
                       (i ? ',' : '{'), sdef->elements[i].dispname);
    }
    src = mputstr(src, " };\n");

    src = mputprintf(src,
      "const char* %s::fld_name(int p_index) const "
      "{ return fld_names[p_index]; }\n\n", name);

    if (has_optional) {
      def = mputstr(def,
        "static const int optional_indexes[];\n"
        "const int* get_optional_indexes() const;\n\n");
      src = mputprintf(src, "const int %s::optional_indexes[] = { ", name);
      for (i = 0; i < sdef->nElements; i++) {
        if (sdef->elements[i].isOptional) {
          src = mputprintf(src, "%lu, ", (unsigned long)i);
        }
      }
      src = mputstr(src, "-1 };\n");
      src = mputprintf(src,
        "const int* %s::get_optional_indexes() const "
        "{ return optional_indexes; }\n\n", name);
    }

    if (has_optional_asn) {
      def = mputstr(def,
        "static const int optional_indexes_asn[];\n"
        "const int* get_optional_indexes_asn() const;\n\n");
      src = mputprintf(src, "const int %s::optional_indexes_asn[] = { ", name);
      for (i = 0; i < sdef->nElements; i++) {
        if (sdef->elements[i].isOptionalAsn) {
          src = mputprintf(src, "%lu, ", (unsigned long)i);
        }
      }
      src = mputstr(src, "-1 };\n");
      src = mputprintf(src,
        "const int* %s::get_optional_indexes_asn() const "
        "{ return optional_indexes_asn; }\n\n", name);
    }

    if (has_default) {
      def = mputstr(def,
        "static const default_struct default_indexes[];\n"
        "const default_struct* get_default_indexes() const;\n");
      src = mputprintf(src, "const Record_Type::default_struct %s::default_indexes[] = { ", name);
      for (i = 0; i < sdef->nElements; i++) {
        if (sdef->elements[i].isDefault) {
          src = mputprintf(src, "{%lu,&%s}, ",
            (unsigned long)i, sdef->elements[i].defvalname);
        }
      }
      src = mputstr(src, "{-1,NULL} };\n");
      src = mputprintf(src,
        "const Record_Type::default_struct* %s::get_default_indexes() const "
          "{ return default_indexes; }\n", name);
    }

    if (has_sensitive) {
      def = mputstr(def,
        "static const boolean sensitive_fields[];\n"
        "boolean fld_sensitive(int field_index) const;\n\n");
      src = mputprintf(src, "const boolean %s::sensitive_fields[] = ", name);
      for (i = 0; i < sdef->nElements; i++) {
        src = mputprintf(src, "%c %s",
          (i > 0 ? ',' : '{'), sdef->elements[i].sensitive_data ? "TRUE" : "FALSE");
      }
      src = mputstr(src, " };\n");
      src = mputprintf(src,
        "boolean %s::fld_sensitive(int field_index) const "
        "{ return sensitive_fields[field_index]; }\n\n", name);
    }

    if (raw_needed) {
      struct raw_option_struct *raw_options;
      boolean haslengthto, haspointer, hascrosstag, has_ext_bit;
      boolean generate_raw_function = FALSE;

      raw_options = (struct raw_option_struct*)
                    Malloc(sdef->nElements * sizeof(*raw_options));

      set_raw_options(sdef, raw_options, &haslengthto,
                      &haspointer, &hascrosstag, &has_ext_bit);

      /* set the value of generate_raw_function: if the coding/decoding is too
         complex for this type then generate the functions, otherwise generate
         helper functions only which are called by the default RAW enc/dec
         functions */
      if (haslengthto || haspointer || hascrosstag) {
        generate_raw_function = TRUE;
        goto check_generate_end;
      }
      for (i = 0; i < sdef->nElements; i++) {
        if (raw_options[i].lengthto || raw_options[i].lengthof ||
            raw_options[i].lengthoffield || raw_options[i].pointerto ||
            raw_options[i].pointerof || raw_options[i].ptrbase ||
            raw_options[i].extbitgroup || raw_options[i].tag_type ||
            raw_options[i].delayed_decode || raw_options[i].nof_dependent_fields
            || raw_options[i].dependent_fields) {
          generate_raw_function = TRUE;
          goto check_generate_end;
        }
      }
      for (i = 0; i < sdef->nElements; i++) {
        if (raw_options[i].lengthof || raw_options[i].lengthoffield ||
            raw_options[i].nof_dependent_fields ||
            raw_options[i].dependent_fields) {
          generate_raw_function = TRUE;
          goto check_generate_end;
        }
      }
      if (sdef->raw.ext_bit_goup_num || sdef->raw.ext_bit_groups ||
          sdef->raw.lengthto || sdef->raw.lengthindex ||
          sdef->raw.taglist.nElements || sdef->raw.crosstaglist.nElements ||
          sdef->raw.presence.fieldnum || sdef->raw.presence.fields ||
          sdef->raw.member_name) {
        generate_raw_function = TRUE;
        goto check_generate_end;
      }
      for (i = 0; i < sdef->nElements; i++) {
        if (sdef->elements[i].hasRaw) {
          generate_raw_function = TRUE;
          goto check_generate_end;
        }
      }
check_generate_end:

      if (generate_raw_function) {
        def = mputprintf(def,
         "int RAW_encode(const TTCN_Typedescriptor_t&, RAW_enc_tree&) const;\n"
         "virtual int RAW_encode_negtest(const Erroneous_descriptor_t *, const TTCN_Typedescriptor_t&, RAW_enc_tree&) const;\n"
         "int RAW_decode(const TTCN_Typedescriptor_t&, TTCN_Buffer&, "
         "int, raw_order_t, boolean no_err = FALSE, "
         "int sel_field = -1, boolean first_call = TRUE, const RAW_Force_Omit* force_omit = NULL);\n");
        src = generate_raw_coding(src, sdef, raw_options, haspointer,
                                  hascrosstag, has_ext_bit);
      } else { /* generate helper functions for the default RAW enc/dec */
        if (has_ext_bit)
          def = mputstr(def,"boolean raw_has_ext_bit() const { return TRUE; }\n");
      }
      for (i = 0; i < sdef->nElements; i++) {
        Free(raw_options[i].lengthoffield);
        Free(raw_options[i].dependent_fields);
      }
      Free(raw_options);
    } /* if (raw_needed) */

    if (xer_needed) { /* XERSTUFF codegen for record/SEQUENCE in RT2 */
      size_t num_attributes = 0;

      /* XER descriptors needed because of the xer antipattern */
      def = mputstr(def,
        "static const XERdescriptor_t* xer_descriptors[];\n"
        "const XERdescriptor_t* xer_descr(int p_index) const;\n"
        "virtual boolean can_start_v(const char *name, const char *prefix, "
        "XERdescriptor_t const& xd, unsigned int flavor, unsigned int flavor2);\n"
        "static  boolean can_start  (const char *name, const char *prefix, "
        "XERdescriptor_t const& xd, unsigned int flavor, unsigned int flavor2);\n");
      src = mputprintf(src, "const XERdescriptor_t* %s::xer_descriptors[] = ", name);
      for (i = 0; i < sdef->nElements; i++) {
        src = mputprintf(src, "%c &%s_xer_",
                         (i ? ',' : '{'), sdef->elements[i].typegen);
      }
      src = mputstr(src, " };\n");
      src = mputprintf(src,
        "const XERdescriptor_t* %s::xer_descr(int p_index) const "
        "{ return xer_descriptors[p_index]; }\n"
        /* The virtual can_start_v hands off to the static can_start.
         * We must make a virtual call in Record_Type::XER_decode because
         * we don't know the actual type (derived from Record_Type) */
        "boolean %s::can_start_v(const char *p_name, const char *p_uri, "
        "XERdescriptor_t const& p_td, unsigned int p_flavor, unsigned int p_flavor2)\n"
        "{ return can_start(p_name, p_uri, p_td, p_flavor, p_flavor2); }\n"
        "boolean %s::can_start(const char *p_name, const char *p_uri, "
        "XERdescriptor_t const& p_td, unsigned int p_flavor, unsigned int p_flavor2) {\n"
        "  boolean e_xer = is_exer(p_flavor);\n"
        "  if (!e_xer || (!(p_td.xer_bits & UNTAGGED) && !(p_flavor & USE_NIL))) return check_name(p_name, p_td, e_xer) && (!e_xer || check_namespace(p_uri, p_td));\n"
        , name , name, name);
      for (i = 0; i < sdef->nElements; i++) {
        src = mputprintf(src,
          "  else if (%s::can_start(p_name, p_uri, %s_xer_, p_flavor, p_flavor2)) return TRUE;\n"
          , sdef->elements[i].type, sdef->elements[i].typegen);
        if (!sdef->elements[i].isOptional) {
          break;
        }
      }
      for (i = 0; i < sdef->nElements; i++) {
        if (strcmp(sdef->elements[i].type, "UNIVERSAL_CHARSTRING") == 0 &&
            !sdef->elements[i].xerAttribute) {
          src = mputprintf(src,
            "  else if (%s_xer_.xer_bits & ANY_ELEMENT) return TRUE;\n"
            , sdef->elements[i].typegen);
        }
        if (!sdef->elements[i].isOptional && !sdef->elements[i].xerAttribute) {
          break;
        }
      }
      src = mputstr(src,
        "  return FALSE;\n"
        "}\n\n");
      /* end of antipattern */

      /* calculate num_attributes in compile time */
      for ( i=0; i < sdef->nElements; ++i ) {
        if (sdef->elements[i].xerAttribute
          ||(sdef->elements[i].xerAnyKind & ANY_ATTRIB_BIT)) ++num_attributes;
        else if (num_attributes) break;
      }

      /* generate helper virtual functions for XER encdec */
      if (num_attributes) {
        def = mputprintf(def,
          "int get_xer_num_attr() const { return %lu; }\n",
          (unsigned long)num_attributes);
      }
    }
    else { /* XER not needed */
      def = mputstr(def,
        "boolean can_start_v(const char *, const char *, XERdescriptor_t const&, unsigned int, unsigned int)\n"
        "{ return FALSE; }\n"
        );
    } /* if (xer_needed) */
    
    if (json_needed) {
      for (i = 0; i < sdef->nElements; ++i) {
        if (sdef->elements[i].jsonChosen != NULL) {
          /* if any of the fields has the 'chosen' attribute,
             then the JSON decoder needs to be generated,
             otherwise Record_Type::JSON_decode is enough */
          def = mputprintf(def,
            "int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, "
            "boolean, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET);\n");
          src = generate_json_decoder(src, sdef);
          break;
        }
      }
    }
  } /* if (sdef->nElements > 0) */

  /* end of class definition */
  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);
}

void defRecordTemplate2(const struct_def *sdef, output_struct *output)
{
    int i;
    const char *name = sdef->name;
    char *def = NULL, *src = NULL;

    const char* base_class = (sdef->nElements==0) ? "Empty_Record_Template" : "Record_Template";

    boolean has_sensitive = FALSE;
    for (i = 0; i < sdef->nElements; i++) {
      if (sdef->elements[i].sensitive_data) {
        has_sensitive = TRUE;
        break;
      }
    }

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

    /* template class definition */
    def = mputprintf(def, "class %s_template : public %s {\n", name, base_class);

    if (sdef->nElements>0) {
      /* set_specific function (used in field access members) */
      def = mputstr(def, "void set_specific();\n");
      src = mputprintf(src, "void %s_template::set_specific()\n"
        "{\n"
        "if (template_selection != SPECIFIC_VALUE) {\n"
        "%s"
        "clean_up();\n"
        "single_value.n_elements = %lu;\n"
        "single_value.value_elements = (Base_Template**)allocate_pointers(single_value.n_elements);\n"
        "set_selection(SPECIFIC_VALUE);\n",
        name, sdef->nElements ? "boolean was_any = (template_selection == ANY_VALUE || template_selection == ANY_OR_OMIT);\n" : "",
        (unsigned long)sdef->nElements);
      for (i = 0; i < sdef->nElements; i++) {
      src = mputprintf(src,
        "single_value.value_elements[%d] = was_any ? new %s_template(%s) : new %s_template;\n",
        i, sdef->elements[i].type,
        sdef->elements[i].isOptional ? "ANY_OR_OMIT" : "ANY_VALUE",
        sdef->elements[i].type);
      }
      src = mputstr(src,
      "}\n"
      "}\n\n");
    }

    /* default constructor */
    def = mputprintf(def, "public:\n"
	"%s_template(): %s() {}\n", name, base_class);

    if (sdef->nElements==0) {
      /* ctor for value {} */
      def = mputprintf(def, "%s_template(null_type) : "
        "Empty_Record_Template() { set_selection(SPECIFIC_VALUE); }\n",
        name);
    }

    /* constructor t1_template(template_sel other_value) */
    def = mputprintf(def, "%s_template(template_sel other_value): "
                          "%s(other_value) {}\n", name, base_class);

    /* constructor t1_template(const t1& other_value) */
    def = mputprintf(def, "%s_template(const %s& other_value): "
                          "%s() { copy_value(&other_value); }\n",
                          name, name, base_class);

    /* constructor t1_template(const OPTIONAL<t1>& other_value) */
    def = mputprintf(def, "%s_template(const OPTIONAL<%s>& other_value): "
                          "%s() { copy_optional(&other_value); }\n",
                          name, name, base_class);

    /* constructor t1_template(t1_template* p_precondition, t1_template* p_implied_template) */
    def = mputprintf(def, "%s_template(%s_template* p_precondition, %s_template* p_implied_template);\n",
      name, name, name);
    src = mputprintf(src,
      "%s_template::%s_template(%s_template* p_precondition, %s_template* p_implied_template)\n"
      "{\n"
      "template_selection = IMPLICATION_MATCH;\n"
      "implication_.precondition = p_precondition;\n"
      "implication_.implied_template = p_implied_template;\n"
      "}\n", name, name, name, name);

    /* constructor t1_template(Dynamic_Match_Interface<t1>* p_dyn_match) */
    def = mputprintf(def, "%s_template(Dynamic_Match_Interface<%s>* p_dyn_match);\n", name, name);
    src = mputprintf(src,
      "%s_template::%s_template(Dynamic_Match_Interface<%s>* p_dyn_match)\n"
      "{\n"
      "template_selection = DYNAMIC_MATCH;\n"
      "dyn_match = new dynmatch_dummy_struct;\n"
      "dyn_match->ptr = p_dyn_match;\n"
      "dyn_match->ref_count = 1;\n"
      "}\n", name, name, name);

    /* copy constructor */
    def = mputprintf(def, "%s_template(const %s_template& other_value): %s() "
      "{ copy_template(other_value); }\n", name, name, base_class);

    /* destructor */
    def = mputprintf(def, "~%s_template();\n", name);
    src = mputprintf(src, "%s_template::~%s_template()\n"
      "{\n"
      "if (template_selection == DYNAMIC_MATCH && dyn_match->ref_count == 1) {\n"
      "delete (static_cast<Dynamic_Match_Interface<%s>*>(dyn_match->ptr));\n"
      "}\n"
      "}\n\n", name, name, name);

    /* matching function for dynamic templates */
    def = mputstr(def, "boolean match_dynamic(const Base_Type* match_value) const;\n");
    src = mputprintf(src,
      "boolean %s_template::match_dynamic(const Base_Type* match_value) const\n"
      "{\n"
      "const %s* actual_value = dynamic_cast<const %s*>(match_value);\n"
      "return (static_cast<Dynamic_Match_Interface<%s>*>(dyn_match->ptr))->match(*actual_value);\n"
      "}\n\n", name, name, name, name);

    /* assignment operator <- template_sel */
    def = mputprintf(def, "%s_template& operator=(template_sel other_value);\n",
	name);
    src = mputprintf(src,
	"%s_template& %s_template::operator=(template_sel other_value)\n"
	"{\n"
	"check_single_selection(other_value);\n"
	"clean_up();\n"
	"set_selection(other_value);\n"
	"return *this;\n"
	"}\n\n", name, name);

    /* assignment operator <- value */
    def = mputprintf(def, "%s_template& operator=(const %s& other_value);\n",
	name, name);
    src = mputprintf(src,
	"%s_template& %s_template::operator=(const %s& other_value)\n"
	"{\n"
	"clean_up();\n"
	"copy_value(&other_value);\n"
	"return *this;\n"
	"}\n\n", name, name, name);

    /* assignment operator <- optional value */
    def = mputprintf(def, "%s_template& operator=(const OPTIONAL<%s>& "
	"other_value);\n", name, name);
    src = mputprintf(src,
	"%s_template& %s_template::operator=(const OPTIONAL<%s>& other_value)\n"
	"{\n"
	"clean_up();\n"
	"copy_optional(&other_value);\n"
	"return *this;\n"
	"}\n\n", name, name, name);

    /* assignment operator <- template*/
    def = mputprintf(def,
	"%s_template& operator=(const %s_template& other_value);\n",
	name, name);
    src = mputprintf(src,
	"%s_template& %s_template::operator=(const %s_template& other_value)\n"
	"{\n"
	"if (&other_value != this) {\n"
	"clean_up();\n"
	"copy_template(other_value);\n"
	"}\n"
	"return *this;\n"
	"}\n\n", name, name, name);

    if (sdef->nElements==0) {
      /* assignment op for value {} */
      def = mputprintf(def, "%s_template& operator=(null_type other_value);\n",
      name);
      src = mputprintf(src, "%s_template& %s_template::operator=(null_type)\n"
      "{\n"
      "clean_up();\n"
      "set_selection(SPECIFIC_VALUE);\n"
      "return *this;\n"
      "}\n\n", name, name);
    }

    /* match operation (template matching) */
    def = mputprintf(def, "inline boolean match(const %s& other_value, "
      "boolean legacy = FALSE) const "
      "{ return matchv(&other_value, legacy); }\n", name);

    /* log_match */
    def = mputprintf(def, "inline void log_match(const %s& match_value, "
      "boolean legacy = FALSE) const "
      "{ log_matchv(&match_value, legacy); }\n", name);

    /* valueof operation */
    def = mputprintf(def, "%s valueof() const;\n", name);

    src = mputprintf(src, "%s %s_template::valueof() const\n"
	"{\n"
	"%s ret_val;\n"
    "valueofv(&ret_val);\n"
    "return ret_val;\n"
    "}\n\n", name, name, name);

    /* list_item(int) function */

    def = mputprintf(def,
	"inline %s_template& list_item(int list_index) const "
      "{ return *(static_cast<%s_template*>(get_list_item(list_index))); }\n", name, name);

    if (sdef->nElements>0) {
      /* template field access functions (non-const & const) */
      for (i = 0; i < sdef->nElements; i++) {
        def = mputprintf(def,
          "%s_template& %s();\n"
          "const %s_template& %s() const;\n",
          sdef->elements[i].type, sdef->elements[i].name,
          sdef->elements[i].type, sdef->elements[i].name);
        src = mputprintf(src,
          "%s_template& %s_template::%s() { return *(static_cast<%s_template*>(get_at(%d))); }\n"
          "const %s_template& %s_template::%s() const { return *(static_cast<const %s_template*>(get_at(%d))); }\n",
          sdef->elements[i].type, name, sdef->elements[i].name, sdef->elements[i].type, i,
          sdef->elements[i].type, name, sdef->elements[i].name, sdef->elements[i].type, i);
      }
    }

    /* virtual functions */
    def = mputprintf(def,
      "%s* create() const { return new %s_template; }\n"
      "const TTCN_Typedescriptor_t* get_descriptor() const;\n",
      base_class, name);
    src = mputprintf(src,
    "const TTCN_Typedescriptor_t* %s_template::get_descriptor() const { return &%s_descr_; }\n",
    name, name);

    if (sdef->nElements>0) {
      def = mputprintf(def, "const char* fld_name(int p_index) const;\n");
      src = mputprintf(src,
        "const char* %s_template::fld_name(int p_index) const { return %s::fld_names[p_index]; }\n",
        name, name);
    }

    if (has_sensitive) {
      def = mputstr(def,
        "static const boolean sensitive_fields[];\n"
        "boolean fld_sensitive(int field_index) const;\n\n");
      src = mputprintf(src, "const boolean %s_template::sensitive_fields[] = ", name);
      for (i = 0; i < sdef->nElements; i++) {
        src = mputprintf(src, "%c %s",
          (i > 0 ? ',' : '{'), sdef->elements[i].sensitive_data ? "TRUE" : "FALSE");
      }
      src = mputstr(src, " };\n");
      src = mputprintf(src,
        "boolean %s_template::fld_sensitive(int field_index) const "
        "{ return sensitive_fields[field_index]; }\n\n", name);
    }

    if (sdef->nElements > 0) {
      def = mputstr(def,
        "static const boolean optional_fields[];\n"
        "boolean fld_optional(int field_index) const;\n\n");
      src = mputprintf(src, "const boolean %s_template::optional_fields[] = ", name);
      for (i = 0; i < sdef->nElements; i++) {
        src = mputprintf(src, "%c %s",
          (i > 0 ? ',' : '{'), sdef->elements[i].isOptional ? "TRUE" : "FALSE");
      }
      src = mputstr(src, " };\n");
      src = mputprintf(src,
        "boolean %s_template::fld_optional(int field_index) const "
        "{ return optional_fields[field_index]; }\n\n", name);
    }

    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);
}

