diff --git a/compiler2/Type.cc b/compiler2/Type.cc index e1c83c7415b81ef8b0dbbd68e043ba43eb2fff26..9cd8d936ad329fd61f59dcc8d9bfaf545b96b059 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 de9bfd29fccecfce82004533a37ad620d257f552..cd3ac9e58ee34bd4fc87d9360a480aa3e252f844 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 a67fc78ae74cec56bca0ac6ea780f3bfa1e8038a..f93130c376b33133b41c0cb2b133cb3dd7316d4e 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 8add56f5df3e926b7d89362143f05bb7f4eb5420..2e78678510499ee7b3e3bfbb080d3428e66e80f6 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 2c641816cc594e34b85c913e86faf19be1c6ba8b..c4a679e1ebe998f27576534f06221fd23e742a2c 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 f85fd171c07bba4a59328a41df50d405fe9019a6..a37a9a08ef10b3ea88f0047092a7a94df97709a4 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 a46403366449568a53f7368e79c79700c921b943..52a96ef665b9189f698f2ac3349d5a98290ccdb5 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 7e963abc89b75c4e15b453929fcb8ff194f92832..79099bf7064f1dd03114cf8a05a6f654e6827986 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 35dc461e2ff36fe4e2a85bb966d841e21e54ae2f..a66dadee4d2cccd50d4ebfa8dd6913d66011af3c 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 32b7333a7409ed95e8c46b5d3e8704babc8aac93..0a2a7cd8b8da1a210aaf4fe98909468a2198370d 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 e73f383e5b197bc5f0d7913dda39a77ec9f3e225..82d13e359a1cb4ea06a509711319cf6aa3fd49a2 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 eea501822e968854645a124c19bab1795759fe6e..5ae3cbfe5a13e735870e478a4eae26a00e0dcd60 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 4a60952f1673bfb62839cc95aee326cda83a6569..d08ef58c6f2bd57484d94768893dec9739d5a43f 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 75928146491e53b6ad225891c128622cbddea0aa..5ddba1c2328edee9eb773c789dea503668ed5af7 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 94baf56fc181c788a64c42410e32dbf34d21c4d1..bab7ef37aa34c38dfe6380e256884fbc805c76e2 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 73fdbaa4941f38ebb72013c6f744a01ab6ac3d62..7f2cab25c724561250466b245b4f55a5ce7defa1 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 4023b471336fbc73e621e64a4a6f37fd88249352..e4f0603e0ab1870944955ba97fe9b29f81c863be 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 0c3bdccd58fd89494f87a0e86b241c2d34a76421..02f27fd1c81a92bd79ab4ab01419d914a43398b5 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 3f68f318081251be78d633b5f7eac1c9c9e5fae2..19b42fbbcf835fb687b3eb5cd20cf3e28ec04e8d 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 006d5d0815dc2c3ffbb4bb2a4a59e3ba5c614dcd..b56e6a2b23b2667aebc8ae5ae5fc74f0a8ccab85 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 342983d2570a61762ed0ed6008786208f2b92efa..921915b61e669fa14d7c0c9da735a461d8eb037f 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 cf22a7ef543cd5d1a596160ede269a2974a21616..66cba68ba5dfa019f90ddf9400b2efe53ff544f4 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 adf323630ed97eba65ced544d24fcd63b421cb35..a9b615706e6477d2e002a983c3f453a861fe73ee 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 6c5154703cb6a32c986ecb3ad5032a652a231c67..afe3e89bb0390cefd93394eda79b9654df331197 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 3572ae5f7465f361b1d512e62609e23c5be5f912..32b3aa59611655b927c0b78b7783d0ba6c39b23b 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 d52476cfecccfee7fdad5d895e58a75116c55e54..62df05246439a5ad7286361d258c3437162f70a7 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 2a87aadb032a3c074161b3b45b74484d5d594cba..04246b9c4df873028bbc528a210be8660aeed726 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 114b804aa3a3e749f4578d0934bc052034d98f99..1a50d464c18d3c9d5ed921957f2b543492be2d8e 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 e790b04795d913c1208128a6255e468b054d297d..18bcb737f13a1522fd3a966e33b7522936a21625 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 169946af78c2ae92eb60a6ad6b291edec04d6369..73a17365ea6b80f5642c3f1d7dd84abf2fb95bec 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 d09f5ddbcfbdebf363c77c32796cf95969b407be..6b41443f77e2095d22986348872f280f99fa5290 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 5326d8bfb0ca05faed95f8e52b256c293c2e667f..1b414a8bba1cd9f88e308048709cf1b2ce7a2a07 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 4cc53d6fba34c7f1e9ea360fb2df43d82b3ec8cf..1301fae04ca553d91451ca17139327ac6cb97db3 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 a538db16f53b7065729dbbf9c2282aec7e0f0821..32f68f11b06ffbb2a51c544f1acebb29d845c1e9 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 050df32201b2f08230b811674f156f2192cfa3b4..9b8b74757b89fe0ee42a756c16758b6cda0f3612 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 6e125186f5aa279213ac7e34b1878f68813f514b..1a494470676d085cee5146a099f506644c70844e 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 db7901d784b0e3723e585f38a24d5afa9f804d0e..12735b6a06430e1dc56aedf5c0e5eea668979d7c 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 bb50c2239d2159ba7357873605b4d43dd90a1b36..b4da029c0a032a8177a48d5884585c0af5d09d89 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 6ecc3089b33feabb529b404c69d4c1850a60b91f..d261be4f445d2d3ab5de53787e5d29b459e415cb 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 cbddc0bca123fcf1bc09b35736ad770c3946468c..c914e79b1e39ec9e4d122adf624242dbcbc47500 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 4f40830f7d09afef3a0b9cf3c8ef4cd1c96133e4..e85261a2e4124fa90d2fa895f8c5895026e84154 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 ea0cc03072bd1c7dab98e198d36331c495e74ad8..1cc00d377804b5a33474900784c4728591155e41 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 3888721fda2823d21bd24a5d1423ff1218d0c052..0249e4a0fb5d56b283144fe349a9135dbb7d5810 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 d87c2561838d9562db621ce85becd02ed577c055..7091f428859a22a59e0d0ba512f6294e27830836 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 097df87cb2f7e02773e64d5b8c6096f28b3ccb83..719779215c15bdb8477cb248296914f12a192242 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 f9e669021916ffe16f8d4ac9a7349e55a038b279..00132ca1e66189aed42566492884f1f9423d6596 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 5f3a543ebb7fdfe6b8d1ab328e36d4b14c694bed..a573d2527a496bb070cefc95ca173568fa9cf8ec 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 0000000000000000000000000000000000000000..e2d293255e6d2f314e34950e486a48630d873491 --- /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 0000000000000000000000000000000000000000..0812d87a54d960dc72a51ca08e0cdabace837aae --- /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 0000000000000000000000000000000000000000..3f369041a92da0cee70b4c0706e820636e205fde --- /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 0000000000000000000000000000000000000000..3a4b58ec16cf2f1390a36c7a92f8823e3b94b425 --- /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 b034d359806ec66439fbbee1f774557b1f4a7e20..1b6141fff5dcc81c2a6b0e999477cede6a6d29df 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 9bf007dd4a85500a409ea5583ea7612535c37e6a..edb639f0896bb5306ff337e4a8397c738a15f1c0 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 e802689d21e618c6edf978ce34d0585e58ada78c..5ce1cd3d65cf2f22a607c897eb48b773d35436d7 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";