From f19a8e12b93ee29aa1e6585a2670ca42c5a8322d Mon Sep 17 00:00:00 2001 From: Botond Baranyi <botond.baranyi@ericsson.com> Date: Thu, 1 Mar 2018 13:15:30 +0100 Subject: [PATCH] Added JSON attribute 'chosen' (bug 528465) Change-Id: Id3d31c8e61e23465c21b18ab204a714709b5cd60 Signed-off-by: Botond Baranyi <botond.baranyi@ericsson.com> --- compiler2/Type.cc | 84 +++ compiler2/Type.hh | 3 +- compiler2/Type_chk.cc | 1 + compiler2/Type_codegen.cc | 75 ++- compiler2/Value.cc | 1 + compiler2/datatypes.h | 1 + compiler2/encdec.c | 3 +- compiler2/enum.c | 2 +- compiler2/record.c | 510 +++++++++++------- compiler2/record_of.c | 4 +- compiler2/ttcn3/JsonAST.cc | 27 + compiler2/ttcn3/JsonAST.hh | 2 + compiler2/ttcn3/RawAST.hh | 4 +- compiler2/ttcn3/rawAST.l | 2 + compiler2/ttcn3/rawAST.y | 44 +- compiler2/ttcn3/rawASTspec.h | 2 +- compiler2/union.c | 19 +- core/ASN_Null.cc | 2 +- core/ASN_Null.hh | 2 +- core/Array.hh | 4 +- core/Basetype.cc | 2 +- core/Basetype.hh | 9 +- core/Bitstring.cc | 2 +- core/Bitstring.hh | 2 +- core/Boolean.cc | 2 +- core/Boolean.hh | 2 +- core/Charstring.cc | 2 +- core/Charstring.hh | 2 +- core/Float.cc | 2 +- core/Float.hh | 2 +- core/Hexstring.cc | 2 +- core/Hexstring.hh | 2 +- core/Integer.cc | 2 +- core/Integer.hh | 2 +- core/JSON.hh | 8 + core/Objid.cc | 2 +- core/Objid.hh | 2 +- core/Octetstring.cc | 2 +- core/Octetstring.hh | 2 +- core/Optional.hh | 25 +- core/TTCN3.hh | 2 +- core/Universal_charstring.cc | 2 +- core/Universal_charstring.hh | 2 +- core/Verdicttype.cc | 2 +- core/Verdicttype.hh | 2 +- core2/Basetype2.cc | 6 +- .../Semantic_Analyser/Makefile.semantic | 3 +- .../Semantic_Analyser/json/.gitignore | 2 + .../Semantic_Analyser/json/JsonChosen_SE.ttcn | 38 ++ function_test/Semantic_Analyser/json/Makefile | 12 + function_test/Semantic_Analyser/json/t | 9 + regression_test/json/AttributeTestcases.ttcn | 108 ++++ regression_test/json/Functions.ttcn | 3 + regression_test/json/Types.ttcn | 33 ++ 54 files changed, 839 insertions(+), 253 deletions(-) create mode 100644 function_test/Semantic_Analyser/json/.gitignore create mode 100644 function_test/Semantic_Analyser/json/JsonChosen_SE.ttcn create mode 100644 function_test/Semantic_Analyser/json/Makefile create mode 100755 function_test/Semantic_Analyser/json/t diff --git a/compiler2/Type.cc b/compiler2/Type.cc index e1c83c741..9cd8d936a 100644 --- a/compiler2/Type.cc +++ b/compiler2/Type.cc @@ -52,6 +52,7 @@ #include "ttcn3/TtcnTemplate.hh" #include "ttcn3/Templatestuff.hh" #include "ttcn3/RawAST.hh" +#include "ttcn3/JsonAST.hh" #include "../common/static_check.h" #include "PredefFunc.hh" @@ -2155,6 +2156,10 @@ namespace Common { size_t fieldnum; for(int c=0;c<rawattrib->taglist.nElements;c++) { // check TAG Identifier *idf=rawattrib->taglist.tag[c].fieldName; + if (idf == NULL) { + error("Field member in RAW parameter TAG cannot be 'omit'"); + continue; + } if(!has_comp_withName(*idf)){ error("Invalid field name `%s' in RAW parameter TAG " "for type `%s'", idf->get_dispname().c_str(), @@ -2488,6 +2493,10 @@ namespace Common { ,field_id.get_dispname().c_str()); break; } + if (idf == NULL) { + error("Field member in RAW parameter CROSSTAG cannot be 'omit'"); + break; + } if(!field_type_last->has_comp_withName(*idf)){ error("Invalid fieldmember name in RAW parameter CROSSTAG" " for field %s: %s" @@ -2971,6 +2980,10 @@ namespace Common { error("Invalid attribute, 'as number' is only allowed for enumerated " "types"); } + + if (NULL != jsonattrib->tag_list) { + chk_json_tag_list(); + } } } @@ -3178,6 +3191,77 @@ namespace Common { } } + void Type::chk_json_tag_list() + { + Type* last = get_type_refd_last(); + Type* parent = get_parent_type(); + if (parent == NULL || last->get_typetype_ttcn3() != T_CHOICE_T || + (parent->typetype != T_SEQ_T && parent->typetype != T_SET_T)) { + error("Invalid attribute, 'chosen' is only allowed for fields of records " + "and sets of union type"); + return; + } + + rawAST_tag_list* tag_list = jsonattrib->tag_list; + for (int i = 0; i < tag_list->nElements; ++i) { + Identifier* union_field_id = tag_list->tag[i].fieldName; + if (union_field_id == NULL) { + if (!is_optional_field()) { + error("Target of JSON attribute 'chosen' is a mandatory field and " + "cannot be set to 'omit'"); + continue; + } + } + else if (!has_comp_withName(*union_field_id)) { + error("Reference to invalid union field name `%s' for type `%s', in JSON " + "attribute 'chosen'", + union_field_id->get_dispname().c_str(), get_typename().c_str()); + continue; + } + + for (int j = 0; j < tag_list->tag[i].nElements; ++j) { + bool erroneous = false; + Type* current_type = parent; // the first field name refers to the parent type + for (int k = 0; k < tag_list->tag[i].keyList[j].keyField->nElements; ++k) { + if (!current_type->is_secho()) { + error("Too many field references in JSON attribute 'chosen'. " + "Type `%s' doesn't have fields.", + current_type->get_typename().c_str()); + erroneous = true; + break; + } + Identifier* current_field_id = tag_list->tag[i].keyList[j].keyField->names[k]; + if (!current_type->has_comp_withName(*current_field_id)) { + error("Reference to invalid field name `%s' for type `%s', " + "in JSON attribute 'chosen'", + current_field_id->get_dispname().c_str(), + current_type->get_typename().c_str()); + erroneous = true; + break; + } + CompField* current_field = current_type->get_comp_byName(*current_field_id); + current_type = current_field->get_type()->get_type_refd_last(); + } + if (!erroneous) { + Error_Context cntx(this, "In JSON attribute 'choice'"); + Value* value = tag_list->tag[i].keyList[j].v_value; + value->set_my_scope(get_my_scope()); + value->set_my_governor(current_type); + current_type->chk_this_value_ref(value); + current_type->chk_this_value(value, 0, EXPECTED_CONSTANT, + INCOMPLETE_NOT_ALLOWED, OMIT_NOT_ALLOWED, SUB_CHK); + + Value::valuetype_t value_type = value->get_valuetype(); + if (value_type == Value::V_ENUM || value_type == Value::V_REFD) { + Free(tag_list->tag[i].keyList[j].value); + tag_list->tag[i].keyList[j].value = + mcopystr(value->get_single_expr().c_str()); + } + } + } + } + } + void Type::force_json() { if (!jsonattrib) diff --git a/compiler2/Type.hh b/compiler2/Type.hh index de9bfd29f..cd3ac9e58 100644 --- a/compiler2/Type.hh +++ b/compiler2/Type.hh @@ -36,12 +36,12 @@ #include "ttcn3/rawASTspec.h" #include "ttcn3/TextAST.hh" #include "ttcn3/BerAST.hh" -#include "ttcn3/JsonAST.hh" #include "ttcn3/OerAST.hh" #include <float.h> class XerAttributes; class RawAST; +class JsonAST; enum namedbool { INCOMPLETE_NOT_ALLOWED = 0, INCOMPLETE_ALLOWED = 1, WARNING_FOR_INCOMPLETE = 2, NO_SUB_CHK = 0, SUB_CHK = 3, OMIT_NOT_ALLOWED = 0, OMIT_ALLOWED = 4, @@ -897,6 +897,7 @@ namespace Common { void chk_json(); void chk_json_default(); + void chk_json_tag_list(); /** If the type does not have a jsonattrib, create one. */ void force_json(); diff --git a/compiler2/Type_chk.cc b/compiler2/Type_chk.cc index a67fc78ae..f93130c37 100644 --- a/compiler2/Type_chk.cc +++ b/compiler2/Type_chk.cc @@ -50,6 +50,7 @@ #include "asn1/Tag.hh" #include "XerAttributes.hh" #include "ttcn3/RawAST.hh" +#include "ttcn3/JsonAST.hh" #include <ctype.h> #include <stdlib.h> // for qsort diff --git a/compiler2/Type_codegen.cc b/compiler2/Type_codegen.cc index 8add56f5d..2e7867851 100644 --- a/compiler2/Type_codegen.cc +++ b/compiler2/Type_codegen.cc @@ -37,6 +37,7 @@ #include "ttcn3/signature.h" #include "XerAttributes.hh" #include "ttcn3/RawAST.hh" +#include "ttcn3/JsonAST.hh" #include "asn1/TableConstraint.hh" #include "asn1/Object.hh" @@ -1071,7 +1072,7 @@ void Type::generate_code_jsondescriptor(output_struct *target) , get_genname_own().c_str() , jsonattrib->omit_as_null ? "TRUE" : "FALSE" , alias ? alias : "NULL" - , jsonattrib->as_value ? "TRUE" : "FALSE" + , (jsonattrib->as_value || jsonattrib->tag_list != NULL) ? "TRUE" : "FALSE" , def_val ? def_val : "NULL" , jsonattrib->metainfo_unbound ? "TRUE" : "FALSE" , jsonattrib->as_number ? "TRUE" : "FALSE"); @@ -1797,6 +1798,67 @@ void Type::generate_code_Se(output_struct *target) cur.jsonAlias = type->jsonattrib->alias; cur.jsonDefaultValue = type->jsonattrib->default_value; cur.jsonMetainfoUnbound = type->jsonattrib->metainfo_unbound; + if (type->jsonattrib->tag_list != NULL) { + rawAST_tag_list* tag_list = type->jsonattrib->tag_list; + sdef.elements[i].jsonChosen = (rawAST_coding_taglist_list*) + Malloc(sizeof(rawAST_coding_taglist_list)); + sdef.elements[i].jsonChosen->nElements = tag_list->nElements; + sdef.elements[i].jsonChosen->list = (rawAST_coding_taglist*) + Malloc(tag_list->nElements * sizeof(rawAST_coding_taglist)); + for (int c = 0; c < tag_list->nElements; ++c) { + if (tag_list->tag[c].nElements != 0) { + sdef.elements[i].jsonChosen->list[c].fields = + (rawAST_coding_field_list*)Malloc(tag_list->tag[c].nElements * + sizeof(rawAST_coding_field_list)); + } + else { + sdef.elements[i].jsonChosen->list[c].fields = NULL; + } + sdef.elements[i].jsonChosen->list[c].nElements = + tag_list->tag[c].nElements; + Identifier* union_field_id = tag_list->tag[c].fieldName; + sdef.elements[i].jsonChosen->list[c].fieldName = union_field_id != NULL ? + union_field_id->get_name().c_str() : NULL; // TODO: currently unused + sdef.elements[i].jsonChosen->list[c].fieldnum = union_field_id != NULL ? + type->get_type_refd_last()->get_comp_index_byName( + *tag_list->tag[c].fieldName) : -2; + for (int a = 0; a <tag_list->tag[c].nElements; ++a) { + rawAST_coding_field_list* key = + sdef.elements[i].jsonChosen->list[c].fields + a; + key->nElements = tag_list->tag[c].keyList[a].keyField->nElements; + key->value = tag_list->tag[c].keyList[a].value; + key->fields = (rawAST_coding_fields*) + Malloc(key->nElements * sizeof(rawAST_coding_fields)); + Type *t = this; + for (int b = 0; b < key->nElements; ++b) { + Identifier* current_field_id = + tag_list->tag[c].keyList[a].keyField->names[b]; + size_t current_field_index = t->get_comp_index_byName(*current_field_id); + CompField* current_field = t->get_comp_byIndex(current_field_index); + key->fields[b].nthfield = current_field_index; + key->fields[b].nthfieldname = current_field_id->get_name().c_str(); + if (t->typetype == T_CHOICE_T) { + key->fields[b].fieldtype = UNION_FIELD; + } + else if (current_field->get_is_optional()) { + key->fields[b].fieldtype = OPTIONAL_FIELD; + } + else { + key->fields[b].fieldtype = MANDATORY_FIELD; + } + Type *field_type = current_field->get_type(); + key->fields[b].type = + pool.add(field_type->get_genname_value(my_scope)); + key->fields[b].typedescr = + pool.add(field_type->get_genname_typedescriptor(my_scope)); + t = field_type->get_type_refd_last(); + } + } + } + } + else { + sdef.elements[i].jsonChosen = NULL; + } } // if jsonattrib } // next element @@ -2085,6 +2147,17 @@ void Type::generate_code_Se(output_struct *target) for(size_t i = 0; i < sdef.totalElements; i++) { // free the array but not the strings if (sdef.elements[i].xerAnyNum > 0) Free(sdef.elements[i].xerAnyUris); + + if (sdef.elements[i].jsonChosen != NULL) { + for (int j = 0; j < sdef.elements[i].jsonChosen->nElements; ++j) { + for (int k = 0; k < sdef.elements[i].jsonChosen->list[j].nElements; ++k) { + Free(sdef.elements[i].jsonChosen->list[j].fields[k].fields); + } + Free(sdef.elements[i].jsonChosen->list[j].fields); + } + Free(sdef.elements[i].jsonChosen->list); + Free(sdef.elements[i].jsonChosen); + } } // next i if (sdef.hasRaw) { diff --git a/compiler2/Value.cc b/compiler2/Value.cc index 2c641816c..c4a679e1e 100644 --- a/compiler2/Value.cc +++ b/compiler2/Value.cc @@ -53,6 +53,7 @@ #include "ttcn3/Statement.hh" #include "ttcn3/Attributes.hh" +#include "ttcn3/JsonAST.hh" #include "../common/JSON_Tokenizer.hh" #include "ttcn3/Ttcn2Json.hh" diff --git a/compiler2/datatypes.h b/compiler2/datatypes.h index f85fd171c..a37a9a08e 100644 --- a/compiler2/datatypes.h +++ b/compiler2/datatypes.h @@ -87,6 +87,7 @@ typedef struct { boolean jsonMetainfoUnbound; const char* jsonAlias; const char* jsonDefaultValue; + rawAST_coding_taglist_list* jsonChosen; /** true if the field is a record-of or set-of with optimized memory allocation */ boolean optimizedMemAlloc; XSD_types xsd_type; diff --git a/compiler2/encdec.c b/compiler2/encdec.c index a46403366..52a96ef66 100644 --- a/compiler2/encdec.c +++ b/compiler2/encdec.c @@ -81,7 +81,8 @@ void def_encdec(const char *p_classname, if(json) { def = mputprintf(def, "int JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&) const;\n" - "int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean);\n"); + "int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, " + "int p_chosen_field = CHOSEN_FIELD_UNSET);\n"); } if(oer) { def = mputprintf(def, diff --git a/compiler2/enum.c b/compiler2/enum.c index 7e963abc8..79099bf70 100644 --- a/compiler2/enum.c +++ b/compiler2/enum.c @@ -809,7 +809,7 @@ void defEnumClass(const enum_def *edef, output_struct *output) // JSON decode src = mputprintf(src, - "int %s::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent)\n" + "int %s::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int)\n" "{\n" " json_token_t token = JSON_TOKEN_NONE;\n" " char* value = 0;\n" diff --git a/compiler2/record.c b/compiler2/record.c index 35dc461e2..a66dadee4 100644 --- a/compiler2/record.c +++ b/compiler2/record.c @@ -3188,6 +3188,294 @@ void gen_xer(const struct_def *sdef, char **pdef, char **psrc) *psrc = src; } +char* generate_json_decoder(char* src, const struct_def* sdef) +{ + src = mputprintf(src, + "int %s::JSON_decode(const TTCN_Typedescriptor_t&%s, JSON_Tokenizer& p_tok, " + "boolean p_silent, int)\n" + "{\n", sdef->name, (sdef->nElements == 1 && !sdef->jsonAsValue) ? " p_td" : ""); + + if (sdef->nElements == 1) { + if (!sdef->jsonAsValue) { + src = mputstr(src, " if (NULL != p_td.json && p_td.json->as_value) {\n"); + } + src = mputprintf(src, " %sreturn field_%s.JSON_decode(%s_descr_, p_tok, p_silent);\n", + sdef->jsonAsValue ? "" : " ", 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" + " 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) { + 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" + " %s_found = TRUE;\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 + , 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) { + /* 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); + } + } + src = mputprintf(src, + " int ret_val = field_%s.JSON_decode(%s_descr_, p_tok, p_silent%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 + " 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); + } + src = mputprintf(src, + "if (!%s_found) {\n" + , sdef->elements[i].name); + if (sdef->elements[i].jsonDefaultValue) { + src = mputprintf(src, + " field_%s.JSON_decode(%s_descr_, DUMMY_BUFFER, p_silent);\n" + , sdef->elements[i].name, sdef->elements[i].typedescrname); + } + else 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; + for (j = 0; j < sdef->elements[i].jsonChosen->nElements; j++) { + if (sdef->elements[i].jsonChosen->list[j].nElements == 0) { + has_otherwise = TRUE; + break; + } + } + boolean first_found = FALSE; + for (j = 0; j < sdef->elements[i].jsonChosen->nElements; j++) { + if ((!has_otherwise && sdef->elements[i].jsonChosen->list[j].fieldnum != -2) || + (has_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); + } + } + 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 "); + } + src = mputstr(src, + "\n return (int)dec_len;\n"); + } + return mputstr(src, "}\n\n"); +} + void defRecordClass1(const struct_def *sdef, output_struct *output) { size_t i; @@ -4447,209 +4735,7 @@ void defRecordClass1(const struct_def *sdef, output_struct *output) src = mputstr(src, "}\n\n"); // JSON decode, RT1 - src = mputprintf(src, - "int %s::JSON_decode(const TTCN_Typedescriptor_t&%s, JSON_Tokenizer& p_tok, boolean p_silent)\n" - "{\n", name, (sdef->nElements == 1 && !sdef->jsonAsValue) ? " p_td" : ""); - - if (sdef->nElements == 1) { - if (!sdef->jsonAsValue) { - src = mputstr(src, " if (NULL != p_td.json && p_td.json->as_value) {\n"); - } - src = mputprintf(src, " %sreturn field_%s.JSON_decode(%s_descr_, p_tok, p_silent);\n", - sdef->jsonAsValue ? "" : " ", 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" - " 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 (i = 0; i < sdef->nElements; ++i) { - 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 (i = 0; i < sdef->nElements; ++i) { - src = mputprintf(src, - // check field name - "if (%d == name_len && 0 == strncmp(fld_name, \"%s\", name_len)) {\n" - " %s_found = TRUE;\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 - , 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"); - } - } - src = mputprintf(src, - " int ret_val = field_%s.JSON_decode(%s_descr_, p_tok, p_silent);\n" - " if (0 > ret_val) {\n" - " if (JSON_ERROR_INVALID_TOKEN == ret_val) {\n" - , sdef->elements[i].name, sdef->elements[i].typedescrname); - 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 - " 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 (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); - } - src = mputprintf(src, - " if (!%s_found) {\n" - , sdef->elements[i].name); - if (sdef->elements[i].jsonDefaultValue) { - src = mputprintf(src, - " field_%s.JSON_decode(%s_descr_, DUMMY_BUFFER, p_silent);\n" - , sdef->elements[i].name, sdef->elements[i].typedescrname); - } - else if (sdef->elements[i].isOptional) { - 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"); - } - src = mputstr(src, - "\n return (int)dec_len;\n"); - } - src = mputstr(src, "}\n\n"); + src = generate_json_decoder(src, sdef); } if (oer_needed) { @@ -6541,7 +6627,7 @@ static void defEmptyRecordClass(const struct_def *sdef, // JSON decode, RT1 src = mputprintf(src, - "int %s::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent)\n" + "int %s::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int)\n" "{\n" " if (NULL != p_td.json->default_value && 0 == p_tok.get_buffer_length()) {\n" // use the default value @@ -7099,6 +7185,7 @@ void defRecordClass2(const struct_def *sdef, output_struct *output) 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_default = FALSE; @@ -7485,6 +7572,21 @@ check_generate_end: "{ 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, int p_chosen_field = CHOSEN_FIELD_UNSET);\n"); + src = generate_json_decoder(src, sdef); + break; + } + } + } } /* if (sdef->nElements > 0) */ /* end of class definition */ diff --git a/compiler2/record_of.c b/compiler2/record_of.c index 32b7333a7..0a2a7cd8b 100644 --- a/compiler2/record_of.c +++ b/compiler2/record_of.c @@ -1567,7 +1567,7 @@ void defRecordOfClass1(const struct_of_def *sdef, output_struct *output) // JSON decode, RT1 src = mputprintf(src, - "int %s::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent)\n" + "int %s::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int)\n" "{\n" " if (NULL != p_td.json->default_value && 0 == p_tok.get_buffer_length()) {\n" // use the default value (currently only the empty array can be set as @@ -3083,7 +3083,7 @@ void defRecordOfClassMemAllocOptimized(const struct_of_def *sdef, output_struct // JSON decode, RT1, mem. alloc. optimised src = mputprintf(src, - "int %s::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent)\n" + "int %s::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int)\n" "{\n" " json_token_t token = JSON_TOKEN_NONE;\n" " size_t dec_len = p_tok.get_next_token(&token, NULL, NULL);\n" diff --git a/compiler2/ttcn3/JsonAST.cc b/compiler2/ttcn3/JsonAST.cc index e73f383e5..82d13e359 100644 --- a/compiler2/ttcn3/JsonAST.cc +++ b/compiler2/ttcn3/JsonAST.cc @@ -35,6 +35,7 @@ void JsonAST::init_JsonAST() default_value = NULL; metainfo_unbound = false; as_number = false; + tag_list = NULL; } JsonAST::JsonAST(const JsonAST *other_val) @@ -61,6 +62,10 @@ JsonAST::~JsonAST() delete schema_extensions[i]; } schema_extensions.clear(); + if (tag_list != NULL) { + free_rawAST_tag_list(tag_list); + delete tag_list; + } } void JsonAST::print_JsonAST() const @@ -92,4 +97,26 @@ void JsonAST::print_JsonAST() const if (metainfo_unbound) { printf("Metainfo for unbound field(s)\n\r"); } + if (tag_list != NULL) { + printf("Chosen union fields:\n\r"); + printf(" Number of rules: %d\n\r", tag_list->nElements); + for (int i = 0; i < tag_list->nElements; ++i) { + printf(" Rule #%d:\n\r", i); + printf(" Chosen field: %s\n\r", tag_list->tag[i].fieldName != NULL ? + tag_list->tag[i].fieldName->get_name().c_str() : "omit"); + printf(" Number of conditions: %d\n\r", tag_list->tag[i].nElements); + for (int j = 0; j < tag_list->tag[i].nElements; ++j) { + printf(" Condition #%d:\n\r", j); + printf(" Value: %s\n\r", tag_list->tag[i].keyList[j].value); + printf(" Field: "); + for (int k = 0; k < tag_list->tag[i].keyList[j].keyField->nElements; ++k) { + if (k != 0) { + printf("."); + } + printf("%s", tag_list->tag[i].keyList[j].keyField->names[k]->get_name().c_str()); + } + printf("\n\r"); + } + } + } } diff --git a/compiler2/ttcn3/JsonAST.hh b/compiler2/ttcn3/JsonAST.hh index eea501822..5ae3cbfe5 100644 --- a/compiler2/ttcn3/JsonAST.hh +++ b/compiler2/ttcn3/JsonAST.hh @@ -15,6 +15,7 @@ #include "../datatypes.h" #include "../vector.hh" +#include "RawAST.hh" class JsonSchemaExtension { private: @@ -41,6 +42,7 @@ class JsonAST { vector<JsonSchemaExtension> schema_extensions; boolean metainfo_unbound; boolean as_number; + rawAST_tag_list* tag_list; JsonAST() { init_JsonAST(); } JsonAST(const JsonAST *other_val); diff --git a/compiler2/ttcn3/RawAST.hh b/compiler2/ttcn3/RawAST.hh index 4a60952f1..d08ef58c6 100644 --- a/compiler2/ttcn3/RawAST.hh +++ b/compiler2/ttcn3/RawAST.hh @@ -51,9 +51,9 @@ typedef struct { } rawAST_tag_field_value; typedef struct { - Common::Identifier* fieldName; + Common::Identifier* fieldName; // NULL == omit int nElements; - rawAST_tag_field_value* keyList; + rawAST_tag_field_value* keyList; // NULL == otherwise/OTHERWISE } rawAST_single_tag; typedef struct { diff --git a/compiler2/ttcn3/rawAST.l b/compiler2/ttcn3/rawAST.l index 759281464..5ddba1c23 100644 --- a/compiler2/ttcn3/rawAST.l +++ b/compiler2/ttcn3/rawAST.l @@ -497,6 +497,8 @@ extend { BEGIN(jsoncodec); RETURN(XKWextend); } metainfo RETURN(XKWmetainfo); for RETURN(XKWfor); unbound RETURN(XKWunbound); +chosen RETURN(XChosenKeyword); +otherwise RETURN(XJsonOtherwise); } <INITIAL>{ diff --git a/compiler2/ttcn3/rawAST.y b/compiler2/ttcn3/rawAST.y index 94baf56fc..bab7ef37a 100644 --- a/compiler2/ttcn3/rawAST.y +++ b/compiler2/ttcn3/rawAST.y @@ -294,6 +294,8 @@ static void yyprint(FILE *file, int type, const YYSTYPE& value); %token XJsonValueEnd ")" %token XJsonValueSegment "JSON value" %token XAsValueKeyword "asValue" +%token XChosenKeyword "chosen" +%token XJsonOtherwise "otherwise" %type <enumval> @@ -618,7 +620,9 @@ XAssocList: if ($3.nElements > 0) { /* the otherwise element is never merged */ for (int i = 0; i < $1.nElements; i++) - if (*$1.tag[i].fieldName == *$3.fieldName) { + if (($1.tag[i].fieldName == NULL && $3.fieldName == NULL) || + ($1.tag[i].fieldName != NULL && $3.fieldName != NULL && + *$1.tag[i].fieldName == *$3.fieldName)) { dupl_id_index = i; break; } @@ -667,6 +671,30 @@ XAssocElement: $$.nElements = 0; $$.keyList = NULL; } +| XIdentifier ',' XJsonOtherwise // JSON version + { + $$.fieldName = $1; + $$.nElements = 0; + $$.keyList = NULL; + } +| XOmitKeyword ',' XKeyIdOrIdList + { + $$.fieldName = NULL; + $$.nElements = $3.nElements; + $$.keyList = $3.keyList; + } +| XOmitKeyword ',' XOtherwise + { + $$.fieldName = NULL; + $$.nElements = 0; + $$.keyList = NULL; + } +| XOmitKeyword ',' XJsonOtherwise // JSON version + { + $$.fieldName = NULL; + $$.nElements = 0; + $$.keyList = NULL; + } ; XKeyIdOrIdList: @@ -1760,6 +1788,7 @@ JSONattribute: | JExtend | JMetainfoForUnbound | JAsNumber +| JChosen ; JOmitAsNull: @@ -1787,6 +1816,19 @@ JAsNumber: XKWas XKWnumber { jsonstruct->as_number = true; } ; +JChosen: + XChosenKeyword '(' XAssocList XoptSemiColon ')' + { + if (jsonstruct->tag_list == NULL) { + jsonstruct->tag_list = new rawAST_tag_list; + } + else { + free_rawAST_tag_list(jsonstruct->tag_list); + } + link_rawAST_tag_list(jsonstruct->tag_list, &$3); + } +; + %% /* parse_rawAST(), which calls our rawAST_parse, is over in rawASST.l */ diff --git a/compiler2/ttcn3/rawASTspec.h b/compiler2/ttcn3/rawASTspec.h index 73fdbaa49..7f2cab25c 100644 --- a/compiler2/ttcn3/rawASTspec.h +++ b/compiler2/ttcn3/rawASTspec.h @@ -58,7 +58,7 @@ typedef struct{ typedef struct{ const char* fieldName; - int fieldnum; + int fieldnum; /* -2 == omit (for JSON only) */ int nElements; rawAST_coding_field_list* fields; }rawAST_coding_taglist; diff --git a/compiler2/union.c b/compiler2/union.c index 4023b4713..e4f0603e0 100644 --- a/compiler2/union.c +++ b/compiler2/union.c @@ -2178,11 +2178,26 @@ void defUnionClass(struct_def const *sdef, output_struct *output) // JSON decode src = mputprintf(src, - "int %s::JSON_decode(const TTCN_Typedescriptor_t&%s, JSON_Tokenizer& p_tok, boolean p_silent)\n" + "int %s::JSON_decode(const TTCN_Typedescriptor_t&%s, JSON_Tokenizer& p_tok, " + "boolean p_silent, int p_chosen_field)\n" "{\n" , name, sdef->nElements > 0 && !sdef->jsonAsValue ? " p_td" : ""); if (sdef->nElements > 0) { - src = mputstr(src, " json_token_t j_token = JSON_TOKEN_NONE;\n"); + src = mputprintf(src, + " if (0 <= p_chosen_field && %d > p_chosen_field) {\n" + " switch (p_chosen_field) {\n" + ,(int)sdef->nElements); + for (i = 0; i < sdef->nElements; ++i) { + src = mputprintf(src, + " case %d:\n" + " return %s%s().JSON_decode(%s_descr_, p_tok, TRUE);\n" + , (int)i, at_field, sdef->elements[i].name + , sdef->elements[i].typedescrname); + } + src = mputstr(src, + " }\n" + " }\n" + " json_token_t j_token = JSON_TOKEN_NONE;\n"); if (!sdef->jsonAsValue) { src = mputstr(src, " if (NULL != p_td.json && p_td.json->as_value) {\n"); diff --git a/core/ASN_Null.cc b/core/ASN_Null.cc index 0c3bdccd5..02f27fd1c 100644 --- a/core/ASN_Null.cc +++ b/core/ASN_Null.cc @@ -318,7 +318,7 @@ int ASN_NULL::JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok) c return p_tok.put_next_token(JSON_TOKEN_LITERAL_NULL); } -int ASN_NULL::JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok, boolean p_silent) +int ASN_NULL::JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok, boolean p_silent, int) { json_token_t token = JSON_TOKEN_NONE; size_t dec_len = p_tok.get_next_token(&token, NULL, NULL); diff --git a/core/ASN_Null.hh b/core/ASN_Null.hh index 3f68f3180..19b42fbbc 100644 --- a/core/ASN_Null.hh +++ b/core/ASN_Null.hh @@ -93,7 +93,7 @@ public: /** Decodes accordingly to the JSON decoding rules. * Returns the length of the encoded data. */ - int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean); + int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET); /** Encodes accordingly to the OER encoding rules. * Returns the length of the encoded data. */ diff --git a/core/Array.hh b/core/Array.hh index 006d5d081..b56e6a2b2 100644 --- a/core/Array.hh +++ b/core/Array.hh @@ -967,7 +967,7 @@ public: /** Decodes accordingly to the JSON encoding rules. * Returns the length of the decoded data. */ - int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean); + int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET); // alt-status priority: ALT_YES (return immediately) > ALT_REPEAT > ALT_MAYBE > ALT_NO alt_status done(VERDICTTYPE* value_redirect, Index_Redirect* index_redirect) const @@ -1355,7 +1355,7 @@ int VALUE_ARRAY<T_type,array_size,index_offset>::JSON_encode( template <typename T_type, unsigned int array_size, int index_offset> int VALUE_ARRAY<T_type,array_size,index_offset>::JSON_decode( - const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent) + const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int) { json_token_t token = JSON_TOKEN_NONE; size_t dec_len = p_tok.get_next_token(&token, NULL, NULL); diff --git a/core/Basetype.cc b/core/Basetype.cc index 342983d25..921915b61 100644 --- a/core/Basetype.cc +++ b/core/Basetype.cc @@ -1026,7 +1026,7 @@ int Base_Type::JSON_encode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer&) c return 0; } -int Base_Type::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer&, boolean) +int Base_Type::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer&, boolean, int) { TTCN_error("JSON decoding requested for type '%s' which has no" " JSON decoding method.", p_td.name); diff --git a/core/Basetype.hh b/core/Basetype.hh index cf22a7ef5..66cba68ba 100644 --- a/core/Basetype.hh +++ b/core/Basetype.hh @@ -25,6 +25,7 @@ #define BASETYPE_HH #include "Types.h" +#include "JSON.hh" #include "Encdec.hh" #include "RInt.hh" #include "JSON_Tokenizer.hh" @@ -613,7 +614,7 @@ public: /** Decode JSON. * @return decoded length * @note Basetype::JSON_decode throws an error. */ - VIRTUAL_IF_RUNTIME_2 int JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer&, boolean); + VIRTUAL_IF_RUNTIME_2 int JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET); /** Encode OER. * @return encoded length @@ -865,7 +866,7 @@ public: /** Decodes accordingly to the JSON encoding rules. * Returns the length of the decoded data. */ - int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean); + int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET); /** Encodes accordingly to the OER encoding rules. * Returns the length of the encoded data. */ @@ -1028,7 +1029,7 @@ public: /** Decodes accordingly to the JSON encoding rules. * Returns the length of the decoded data. */ - int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean); + int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET); /** Encodes accordingly to the JSON encoding rules. * Returns the length of the encoded data. */ @@ -1107,7 +1108,7 @@ public: /** Decodes accordingly to the JSON encoding rules. * Returns the length of the decoded data. */ - virtual int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean); + virtual int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET); /** Encodes accordingly to the OER encoding rules. * Returns the length of the encoded data. */ diff --git a/core/Bitstring.cc b/core/Bitstring.cc index adf323630..a9b615706 100644 --- a/core/Bitstring.cc +++ b/core/Bitstring.cc @@ -1209,7 +1209,7 @@ int BITSTRING::JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok) return enc_len; } -int BITSTRING::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent) +int BITSTRING::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int) { json_token_t token = JSON_TOKEN_NONE; char* value = 0; diff --git a/core/Bitstring.hh b/core/Bitstring.hh index 6c5154703..afe3e89bb 100644 --- a/core/Bitstring.hh +++ b/core/Bitstring.hh @@ -208,7 +208,7 @@ public: /** Decodes accordingly to the JSON decoding rules. * Returns the length of the encoded data. */ - int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean); + int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET); /** Encodes accordingly to the OER encoding rules. * Returns the length of the encoded data. */ diff --git a/core/Boolean.cc b/core/Boolean.cc index 3572ae5f7..32b3aa596 100644 --- a/core/Boolean.cc +++ b/core/Boolean.cc @@ -758,7 +758,7 @@ int BOOLEAN::JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok) co return p_tok.put_next_token((boolean_value) ? JSON_TOKEN_LITERAL_TRUE : JSON_TOKEN_LITERAL_FALSE, NULL); } -int BOOLEAN::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent) +int BOOLEAN::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int) { json_token_t token = JSON_TOKEN_NONE; size_t dec_len = 0; diff --git a/core/Boolean.hh b/core/Boolean.hh index d52476cfe..62df05246 100644 --- a/core/Boolean.hh +++ b/core/Boolean.hh @@ -129,7 +129,7 @@ public: /** Decodes accordingly to the JSON encoding rules. * Returns the length of the decoded data. */ - int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean); + int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET); /** Encodes accordingly to the OER encoding rules. * Returns the length of the encoded data. */ diff --git a/core/Charstring.cc b/core/Charstring.cc index 2a87aadb0..04246b9c4 100644 --- a/core/Charstring.cc +++ b/core/Charstring.cc @@ -1797,7 +1797,7 @@ int CHARSTRING::JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok) return enc_len; } -int CHARSTRING::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent) +int CHARSTRING::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int) { json_token_t token = JSON_TOKEN_NONE; char* value = 0; diff --git a/core/Charstring.hh b/core/Charstring.hh index 114b804aa..1a50d464c 100644 --- a/core/Charstring.hh +++ b/core/Charstring.hh @@ -278,7 +278,7 @@ public: /** Decodes accordingly to the JSON encoding rules. * Returns the length of the decoded data. */ - int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean); + int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET); int OER_encode(const TTCN_Typedescriptor_t&, TTCN_Buffer&) const; diff --git a/core/Float.cc b/core/Float.cc index e790b0479..18bcb737f 100644 --- a/core/Float.cc +++ b/core/Float.cc @@ -1103,7 +1103,7 @@ int FLOAT::JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok) cons return enc_len; } -int FLOAT::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent) +int FLOAT::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int) { bound_flag = FALSE; json_token_t token = JSON_TOKEN_NONE; diff --git a/core/Float.hh b/core/Float.hh index 169946af7..73a17365e 100644 --- a/core/Float.hh +++ b/core/Float.hh @@ -160,7 +160,7 @@ public: /** Decodes accordingly to the JSON encoding rules. * Returns the length of the decoded data. */ - int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean); + int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET); /** Encodes accordingly to the OER encoding rules. * Returns the length of the encoded data. */ diff --git a/core/Hexstring.cc b/core/Hexstring.cc index d09f5ddbc..6b41443f7 100644 --- a/core/Hexstring.cc +++ b/core/Hexstring.cc @@ -1085,7 +1085,7 @@ int HEXSTRING::JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok) return enc_len; } -int HEXSTRING::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent) +int HEXSTRING::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int) { json_token_t token = JSON_TOKEN_NONE; char* value = 0; diff --git a/core/Hexstring.hh b/core/Hexstring.hh index 5326d8bfb..1b414a8bb 100644 --- a/core/Hexstring.hh +++ b/core/Hexstring.hh @@ -161,7 +161,7 @@ public: /** Decodes accordingly to the JSON encoding rules. * Returns the length of the decoded data. */ - int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean); + int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET); }; class HEXSTRING_ELEMENT { diff --git a/core/Integer.cc b/core/Integer.cc index 4cc53d6fb..1301fae04 100644 --- a/core/Integer.cc +++ b/core/Integer.cc @@ -1741,7 +1741,7 @@ int INTEGER::JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok) co return enc_len; } -int INTEGER::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent) +int INTEGER::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int) { json_token_t token = JSON_TOKEN_NONE; char* value = 0; diff --git a/core/Integer.hh b/core/Integer.hh index a538db16f..32f68f11b 100644 --- a/core/Integer.hh +++ b/core/Integer.hh @@ -191,7 +191,7 @@ public: /** Decodes accordingly to the JSON encoding rules. * Returns the length of the decoded data. */ - int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean); + int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET); /** Encodes accordingly to the OER encoding rules. * Returns the length of the encoded data. */ diff --git a/core/JSON.hh b/core/JSON.hh index 050df3220..9b8b74757 100644 --- a/core/JSON.hh +++ b/core/JSON.hh @@ -125,6 +125,11 @@ enum json_metainfo_t { JSON_METAINFO_UNBOUND }; +enum json_chosen_field_t { + CHOSEN_FIELD_UNSET = -1, + CHOSEN_FIELD_OMITTED = -2 +}; + // JSON decoding error messages #define JSON_DEC_BAD_TOKEN_ERROR "Failed to extract valid token, invalid JSON format%s" #define JSON_DEC_FORMAT_ERROR "Invalid JSON %s format, expecting %s value" @@ -141,6 +146,9 @@ enum json_metainfo_t { #define JSON_DEC_METAINFO_NAME_ERROR "Meta info provided for non-existent field '%.*s'" #define JSON_DEC_METAINFO_VALUE_ERROR "Invalid meta info for field '%s'" #define JSON_DEC_METAINFO_NOT_APPLICABLE "Meta info not applicable to field '%s'" +#define JSON_DEC_CHOSEN_FIELD_NOT_NULL "Invalid JSON token, expecting 'null' (as indicated by a condition in attribute 'chosen')%s" +#define JSON_DEC_CHOSEN_FIELD_OMITTED "Field '%s' cannot be omitted (as indicated by a condition in attribute 'chosen')" +#define JSON_DEC_CHOSEN_FIELD_OMITTED_NULL "Field cannot be omitted (as indicated by a condition in attribute 'chosen')%s" // Functions for conversion between json and cbor and vice versa diff --git a/core/Objid.cc b/core/Objid.cc index 6e125186f..1a4944706 100644 --- a/core/Objid.cc +++ b/core/Objid.cc @@ -631,7 +631,7 @@ int OBJID::JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok) cons return enc_len; } -int OBJID::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent) +int OBJID::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int) { json_token_t token = JSON_TOKEN_NONE; char* value = 0; diff --git a/core/Objid.hh b/core/Objid.hh index db7901d78..12735b6a0 100644 --- a/core/Objid.hh +++ b/core/Objid.hh @@ -114,7 +114,7 @@ public: /** Decodes accordingly to the JSON decoding rules. * Returns the length of the encoded data. */ - int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean); + int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET); /** Encodes accordingly to the OER encoding rules. * Returns the length of the encoded data. */ diff --git a/core/Octetstring.cc b/core/Octetstring.cc index bb50c2239..b4da029c0 100644 --- a/core/Octetstring.cc +++ b/core/Octetstring.cc @@ -1331,7 +1331,7 @@ int OCTETSTRING::JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok return enc_len; } -int OCTETSTRING::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent) +int OCTETSTRING::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int) { json_token_t token = JSON_TOKEN_NONE; char* value = 0; diff --git a/core/Octetstring.hh b/core/Octetstring.hh index 6ecc3089b..d261be4f4 100644 --- a/core/Octetstring.hh +++ b/core/Octetstring.hh @@ -192,7 +192,7 @@ public: /** Decodes accordingly to the JSON encoding rules. * Returns the length of the decoded data. */ - int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean); + int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET); /** Encodes accordingly to the OER encoding rules. * Returns the length of the encoded data. */ diff --git a/core/Optional.hh b/core/Optional.hh index cbddc0bca..c914e79b1 100644 --- a/core/Optional.hh +++ b/core/Optional.hh @@ -340,7 +340,7 @@ public: /** Decodes accordingly to the JSON encoding rules. * Returns the length of the decoded data. */ - int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean); + int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET); /** Encodes accordingly to the OER encoding rules. * Returns the length of the encoded data. */ @@ -871,12 +871,28 @@ int OPTIONAL<T_type>::JSON_encode_negtest(const Erroneous_descriptor_t* p_err_de #endif template<typename T_type> -int OPTIONAL<T_type>::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent) +int OPTIONAL<T_type>::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int p_chosen_field) { // try the optional value first set_to_present(); size_t buf_pos = p_tok.get_buf_pos(); - int dec_len = optional_value->JSON_decode(p_td, p_tok, p_silent); + int dec_len = 0; + if (CHOSEN_FIELD_OMITTED == p_chosen_field) { + // the attribute 'chosen' says that this field has to be omitted + json_token_t token = JSON_TOKEN_NONE; + dec_len = p_tok.get_next_token(&token, NULL, NULL); + if (JSON_TOKEN_LITERAL_NULL == token) { + set_to_omit(); + return dec_len; + } + else { + JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_CHOSEN_FIELD_NOT_NULL, ""); + // if this is set to warning, return to the beginning of the value and + // decode it as normal + p_tok.set_buf_pos(buf_pos); + } + } + dec_len = optional_value->JSON_decode(p_td, p_tok, p_silent, p_chosen_field); if (JSON_ERROR_FATAL == dec_len) { if (p_silent) { clean_up(); @@ -892,6 +908,9 @@ int OPTIONAL<T_type>::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokeni json_token_t token = JSON_TOKEN_NONE; dec_len = p_tok.get_next_token(&token, NULL, NULL); if (JSON_TOKEN_LITERAL_NULL == token) { + if (0 <= p_chosen_field) { + JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_CHOSEN_FIELD_OMITTED_NULL, ""); + } set_to_omit(); } else { diff --git a/core/TTCN3.hh b/core/TTCN3.hh index 4f40830f7..e85261a2e 100644 --- a/core/TTCN3.hh +++ b/core/TTCN3.hh @@ -41,6 +41,7 @@ #include "version.h" #include <cversion.h> +#include "JSON.hh" #include "Vector.hh" #include "Basetype.hh" #include "Template.hh" @@ -87,7 +88,6 @@ #include "RAW.hh" #include "TEXT.hh" #include "XER.hh" -#include "JSON.hh" #include "OER.hh" #include "Error.hh" #include "XmlReader.hh" diff --git a/core/Universal_charstring.cc b/core/Universal_charstring.cc index ea0cc0307..1cc00d377 100644 --- a/core/Universal_charstring.cc +++ b/core/Universal_charstring.cc @@ -2584,7 +2584,7 @@ int UNIVERSAL_CHARSTRING::JSON_encode(const TTCN_Typedescriptor_t& /*p_td*/, JSO return enc_len; } -int UNIVERSAL_CHARSTRING::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent) +int UNIVERSAL_CHARSTRING::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int) { json_token_t token = JSON_TOKEN_NONE; char* value = 0; diff --git a/core/Universal_charstring.hh b/core/Universal_charstring.hh index 3888721fd..0249e4a0f 100644 --- a/core/Universal_charstring.hh +++ b/core/Universal_charstring.hh @@ -403,7 +403,7 @@ public: /** Decodes accordingly to the JSON encoding rules. * Returns the length of the decoded data. */ - int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean); + int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET); int OER_encode(const TTCN_Typedescriptor_t&, TTCN_Buffer&) const; diff --git a/core/Verdicttype.cc b/core/Verdicttype.cc index d87c25618..7091f4288 100644 --- a/core/Verdicttype.cc +++ b/core/Verdicttype.cc @@ -382,7 +382,7 @@ int VERDICTTYPE::JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok return enc_len; } -int VERDICTTYPE::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent) +int VERDICTTYPE::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int) { json_token_t token = JSON_TOKEN_NONE; char* value = 0; diff --git a/core/Verdicttype.hh b/core/Verdicttype.hh index 097df87cb..719779215 100644 --- a/core/Verdicttype.hh +++ b/core/Verdicttype.hh @@ -108,7 +108,7 @@ public: /** Decodes accordingly to the JSON encoding rules. * Returns the length of the decoded data. */ - int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean); + int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET); }; inline boolean operator==(verdicttype par_value, verdicttype other_value) diff --git a/core2/Basetype2.cc b/core2/Basetype2.cc index f9e669021..00132ca1e 100644 --- a/core2/Basetype2.cc +++ b/core2/Basetype2.cc @@ -1587,7 +1587,7 @@ int Record_Of_Type::JSON_encode_negtest(const Erroneous_descriptor_t* p_err_desc return enc_len; } -int Record_Of_Type::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent) +int Record_Of_Type::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int) { if (NULL != p_td.json->default_value && 0 == p_tok.get_buffer_length()) { // use the default value (currently only the empty array can be set as @@ -6081,7 +6081,7 @@ int Record_Type::JSON_encode_negtest(const Erroneous_descriptor_t* p_err_descr, return enc_len; } -int Record_Type::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent) +int Record_Type::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int) { if (NULL != p_td.json && p_td.json->as_value) { // if 'as value' is set, then the record/set has only one field, @@ -7312,7 +7312,7 @@ int Empty_Record_Type::JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok.put_next_token(JSON_TOKEN_OBJECT_END, NULL); } -int Empty_Record_Type::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent) +int Empty_Record_Type::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int) { if (NULL != p_td.json->default_value && 0 == p_tok.get_buffer_length()) { // use the default value diff --git a/function_test/Semantic_Analyser/Makefile.semantic b/function_test/Semantic_Analyser/Makefile.semantic index 5f3a543eb..a573d2527 100644 --- a/function_test/Semantic_Analyser/Makefile.semantic +++ b/function_test/Semantic_Analyser/Makefile.semantic @@ -15,7 +15,8 @@ include ../../Makefile.personal SADIRS := ver param template any_from pattern_ref float recof_index \ -port_translation mtc_and_system_clause port_map_connect deterministic +port_translation mtc_and_system_clause port_map_connect deterministic \ +json ifdef RT2 SADIRS += deprecated erroneous_attributes template_concat endif diff --git a/function_test/Semantic_Analyser/json/.gitignore b/function_test/Semantic_Analyser/json/.gitignore new file mode 100644 index 000000000..e2d293255 --- /dev/null +++ b/function_test/Semantic_Analyser/json/.gitignore @@ -0,0 +1,2 @@ +!Makefile +!*.ttcn diff --git a/function_test/Semantic_Analyser/json/JsonChosen_SE.ttcn b/function_test/Semantic_Analyser/json/JsonChosen_SE.ttcn new file mode 100644 index 000000000..0812d87a5 --- /dev/null +++ b/function_test/Semantic_Analyser/json/JsonChosen_SE.ttcn @@ -0,0 +1,38 @@ +/****************************************************************************** + * Copyright (c) 2000-2017 Ericsson Telecom AB + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Baranyi, Botond + * + ******************************************************************************/ +module JsonChosen_SE { //^In TTCN-3 module// + +type union Uni { + integer f1, + charstring f2, + octetstring f3 +} + +type record Rec { //^In type definition// + integer num, + charstring str, + octetstring field1, //Invalid attribute, 'chosen' is only allowed for fields of records and sets of union type// + Uni field2, //Reference to invalid union field name `nonexistent' for type `@JsonChosen_SE.Uni', in JSON attribute 'chosen'// //^In JSON attribute 'choice'// + Uni field3, //Target of JSON attribute 'chosen' is a mandatory field and cannot be set to 'omit'// + Uni field4 optional //Too many field references in JSON attribute 'chosen'. Type `integer' doesn't have fields// //Reference to invalid field name `nonexistent' for type `@JsonChosen_SE.Uni', in JSON attribute 'chosen'// +} +with { + variant (field1) "chosen (nonexistent, num = 1)"; + variant (field2) "chosen (nonexistent, num = 1; f1, num = 100; f2, { str = 1, str = \"a\" })"; //character string value was expected// + variant (field3) "chosen (omit, num = 1)"; + variant (field4) "chosen (f1, num = 4; f2, field2.f1.toomany = 22; f3, field3.nonexistent = 1111)"; +} + +} +with { + encode "JSON"; +} diff --git a/function_test/Semantic_Analyser/json/Makefile b/function_test/Semantic_Analyser/json/Makefile new file mode 100644 index 000000000..3f369041a --- /dev/null +++ b/function_test/Semantic_Analyser/json/Makefile @@ -0,0 +1,12 @@ +############################################################################## +# Copyright (c) 2000-2017 Ericsson Telecom AB +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Baranyi, Botond +# +############################################################################## +include ../common.mk diff --git a/function_test/Semantic_Analyser/json/t b/function_test/Semantic_Analyser/json/t new file mode 100755 index 000000000..3a4b58ec1 --- /dev/null +++ b/function_test/Semantic_Analyser/json/t @@ -0,0 +1,9 @@ +#!/usr/bin/perl +# note this is called through "perl -w" +use strict; + +my $self = $0; +$self =~ s!/t!!; + +exec('make check --no-print-directory -s -C ' . $self); + diff --git a/regression_test/json/AttributeTestcases.ttcn b/regression_test/json/AttributeTestcases.ttcn index b034d3598..1b6141fff 100644 --- a/regression_test/json/AttributeTestcases.ttcn +++ b/regression_test/json/AttributeTestcases.ttcn @@ -521,6 +521,112 @@ testcase tc_attribute_default_struct() runs on MTC { } } +// Testing the attribute 'chosen' +testcase tc_attribute_chosen() runs on MTC { + // one test for each chosen field + var PduWithIdList val := { + { protocolId := 11, field1 := { type1 := { num := 1, str := "abc" } }, field2 := { type2 := { num := 2, str := "xyz" } } }, + { protocolId := 2, field1 := { type2 := { num := 1, str := "abc" } }, field2 := { type1 := { num := 2, str := "xyz" } } }, + { protocolId := 3, field1 := omit, field2 := { type3 := { num := 2.0, str := "xyz" } } }, + { protocolId := 65536, field1 := { type3 := { num := 1.0, str := "abc" } }, field2 := omit } + }; + var universal charstring enc_exp := "[" & + "{\"protocolId\":11,\"field1\":{\"num\":1,\"str\":\"abc\"},\"field2\":{\"num\":2,\"str\":\"xyz\"}}," & + "{\"protocolId\":2,\"field1\":{\"num\":1,\"str\":\"abc\"},\"field2\":{\"num\":2,\"str\":\"xyz\"}}," & + "{\"protocolId\":3,\"field2\":{\"num\":2.000000,\"str\":\"xyz\"}}," & + "{\"protocolId\":65536,\"field1\":{\"num\":1.000000,\"str\":\"abc\"}}" & + "]"; + var universal charstring enc := encvalue_unichar(val); + if (enc != enc_exp) { + setverdict(fail, "Encoding failed. Expected: ", enc_exp, ", got: ", enc); + } + var PduWithIdList dec; + var integer res := decvalue_unichar(enc_exp, dec); + if (res != 0) { + setverdict(fail, "Decoding #1 failed. Result: ", res); + } + else if (dec != val) { + setverdict(fail, "Decoding #1 failed. Decoded value: ", dec, ", expected: ", val); + } + + // these should also be correctly decoded to the initial values + var universal charstring buff := "[" & + "{\"protocolId\":65536,\"field1\":{\"num\":1,\"str\":\"abc\"},\"field2\":null}," & + "{\"protocolId\":3,\"field1\":null,\"field2\":{\"num\":2,\"str\":\"xyz\"}}" & + "]"; + var PduWithIdList dec2; + var PduWithIdList dec2_exp := { val[3], val[2] }; + res := decvalue_unichar(buff, dec2); + if (res != 0) { + setverdict(fail, "Decoding #2 failed. Result: ", res); + } + else if (dec2 != dec2_exp) { + setverdict(fail, "Decoding #2 failed. Decoded value: ", dec2, ", expected: ", dec2_exp); + } + setverdict(pass); +} + +// Testing JSON decoding when field selections of optional fields conflict with +// the settings in attribute 'chosen' +testcase tc_attribute_chosen_negtest() runs on MTC { + // Test #1: protocolId indicates 'omit' (for field1), but the field is not omitted + @try { + var octetstring buff := char2oct("{\"protocolId\":3,\"field1\":{\"num\":1,\"str\":\"abc\"},\"field2\":{\"num\":2,\"str\":\"xyz\"}}"); + var PduWithId dec := f_dec_pduwithid(buff); + setverdict(fail, "Test #1. Expected decoding failure."); + } + @catch (msg) { + var template charstring tmp := + pattern "*Invalid JSON token, expecting 'null' \(as indicated by a condition in attribute 'chosen'\)"; + if (not match(msg, tmp)) { + setverdict(fail, "Test #1. Invalid error message received: ", msg); + } + } + + // Test #2: protocolId indicates a union field (type1, for field1), but the field is omitted (with the JSON value 'null') + @try { + var octetstring buff := char2oct("{\"protocolId\":1,\"field1\":null}"); + var PduWithId dec := f_dec_pduwithid(buff); + setverdict(fail, "Test #2. Expected decoding failure."); + } + @catch (msg) { + var template charstring tmp := + pattern "*Field cannot be omitted \(as indicated by a condition in attribute 'chosen'\)"; + if (not match(msg, tmp)) { + setverdict(fail, "Test #2. Invalid error message received: ", msg); + } + } + + // Test #3: protocolId indicates a union field (type1, for field1), but the field is omitted (with no JSON name-value pair) + @try { + var octetstring buff := char2oct("{\"protocolId\":1}"); + var PduWithId dec := f_dec_pduwithid(buff); + setverdict(fail, "Test #3. Expected decoding failure."); + } + @catch (msg) { + var template charstring tmp := + pattern "*Field 'field1' cannot be omitted \(as indicated by a condition in attribute 'chosen'\)"; + if (not match(msg, tmp)) { + setverdict(fail, "Test #3. Invalid error message received: ", msg); + } + } + + // Test #4: protocolId indicates a union field (type3, for field2), but the field is omitted (with no JSON name-value pair) + @try { + var octetstring buff := char2oct("{\"protocolId\":3}"); + var PduWithId dec := f_dec_pduwithid(buff); + setverdict(fail, "Test #3. Expected decoding failure."); + } + @catch (msg) { + var template charstring tmp := + pattern "*Field 'field2' cannot be omitted \(as indicated by a condition in attribute 'chosen'\)"; + if (not match(msg, tmp)) { + setverdict(fail, "Test #3. Invalid error message received: ", msg); + } + } + setverdict(pass); +} + control { execute(tc_NoAttributeOnUpperLevel()) @@ -547,5 +653,7 @@ control { execute(tc_attribute_as_number()); execute(tc_attribute_as_number_negtest()); execute(tc_attribute_default_struct()); + execute(tc_attribute_chosen()); + execute(tc_attribute_chosen_negtest()); } } diff --git a/regression_test/json/Functions.ttcn b/regression_test/json/Functions.ttcn index 9bf007dd4..edb639f08 100644 --- a/regression_test/json/Functions.ttcn +++ b/regression_test/json/Functions.ttcn @@ -426,6 +426,9 @@ external function f_dec_obj(in octetstring x) return Object external function f_dec_null(in octetstring x) return HasNull with { extension "prototype(convert) decode(JSON)" } + +external function f_dec_pduwithid(in octetstring x) return PduWithId + with { extension "prototype(convert) decode(JSON)" } //============== Internal Functions ==================== diff --git a/regression_test/json/Types.ttcn b/regression_test/json/Types.ttcn index e802689d2..5ce1cd3d6 100644 --- a/regression_test/json/Types.ttcn +++ b/regression_test/json/Types.ttcn @@ -318,6 +318,39 @@ with { variant (so) "JSON: default({})"; } +type record PduWithId { + integer protocolId, + Choices field1 optional, + Choices field2 optional +} +with { + variant (field1) "chosen (type1, { protocolId = 1, protocolId = 11 }; type2, protocolId = 2; omit, protocolId = 3; type3, otherwise)"; + variant (field2) "chosen (type1, protocolId = 2; type2, { protocolId = 10, protocolId = 11 }; type3, protocolId = 3)"; // no otherwise +} + +type union Choices { + StructType1 type1, + StructType2 type2, + StructType3 type3 +} + +type record StructType1 { + integer num, + charstring str +} + +type record StructType2 { + integer num, + universal charstring str +} + +type record StructType3 { + float num, + charstring str +} + +type record of PduWithId PduWithIdList; + } with { encode "JSON"; extension "anytype integer, charstring, R, RoI, Thing"; -- GitLab