X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=compiler2%2Fsubtype.cc;h=814f8e769cf534596a2c34ba74fc08f153f8d9af;hb=3a70874a1ed2d6b0038e93704acf65b443e488b4;hp=29b0232209e81b958d1b3af0aa73370d5751a592;hpb=af7104877b9819f9e11f3ca6109eda961e971478;p=deliverable%2Ftitan.core.git diff --git a/compiler2/subtype.cc b/compiler2/subtype.cc index 29b0232..814f8e7 100644 --- a/compiler2/subtype.cc +++ b/compiler2/subtype.cc @@ -1,10 +1,27 @@ -/////////////////////////////////////////////////////////////////////////////// -// Copyright (c) 2000-2014 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 -/////////////////////////////////////////////////////////////////////////////// +/****************************************************************************** + * Copyright (c) 2000-2016 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: + * Baji, Laszlo + * Balasko, Jeno + * Baranyi, Botond + * Cserveni, Akos + * Delic, Adam + * Feher, Csaba + * Forstner, Matyas + * Kovacs, Ferenc + * Raduly, Csaba + * Szabados, Kristof + * Szabo, Janos Zoltan – initial implementation + * Szalai, Gabor + * Tatarka, Gabor + * Zalanyi, Balazs Andor + * + ******************************************************************************/ #include "subtype.hh" #include "../common/dbgnew.hh" #include "Identifier.hh" @@ -17,6 +34,8 @@ #include "ttcn3/Templatestuff.hh" #include "ttcn3/PatternString.hh" #include "Constraint.hh" +#include "../common/JSON_Tokenizer.hh" +#include "ttcn3/Ttcn2Json.hh" #include @@ -1930,6 +1949,9 @@ void SubType::chk_this_template(Template *templ) case Template::VALUE_RANGE: /* Should be canonical before */ break; + case Template::ALL_FROM: + case Template::VALUE_LIST_ALL_FROM: + break; case Template::SUPERSET_MATCH: case Template::SUBSET_MATCH: if (subtype!=ST_SETOF){ @@ -3037,4 +3059,287 @@ void SubType::generate_code(output_struct &) if (checked!=STC_YES) FATAL_ERROR("SubType::generate_code()"); } +void SubType::generate_json_schema(JSON_Tokenizer& json, + bool allow_special_float /* = true */) +{ + bool has_value_list = false; + size_t nof_ranges = 0; + for (size_t i = 0; i < parsed->size(); ++i) { + SubTypeParse *parse = (*parsed)[i]; + switch (parse->get_selection()) { + case SubTypeParse::STP_SINGLE: + // single values will be added later, all at once + has_value_list = true; + break; + case SubTypeParse::STP_RANGE: + ++nof_ranges; + break; + case SubTypeParse::STP_LENGTH: { + Ttcn::LengthRestriction* len_res = parse->Length(); + Value* min_val = len_res->get_is_range() ? len_res->get_lower_value() : + len_res->get_single_value(); + Value* max_val = len_res->get_is_range() ? len_res->get_upper_value() : + len_res->get_single_value(); + const char* json_min = NULL; + const char* json_max = NULL; + switch (subtype) { + case ST_RECORDOF: + case ST_SETOF: + // use minItems and maxItems for record of/set of + json_min = "minItems"; + json_max = "maxItems"; + break; + case ST_BITSTRING: + case ST_HEXSTRING: + case ST_OCTETSTRING: + case ST_CHARSTRING: + case ST_UNIVERSAL_CHARSTRING: + // use minLength and maxLength for string types + json_min = "minLength"; + json_max = "maxLength"; + break; + default: + FATAL_ERROR("SubType::generate_json_schema - length %d", subtype); + } + json.put_next_token(JSON_TOKEN_NAME, json_min); + min_val->generate_json_value(json); + if (max_val != NULL) { + json.put_next_token(JSON_TOKEN_NAME, json_max); + max_val->generate_json_value(json); + } + break; } + case SubTypeParse::STP_PATTERN: { + json.put_next_token(JSON_TOKEN_NAME, "pattern"); + char* json_pattern = parse->Pattern()->convert_to_json(); + json.put_next_token(JSON_TOKEN_STRING, json_pattern); + Free(json_pattern); + break; } + default: + break; + } + } + + bool need_anyOf = (subtype == ST_INTEGER || subtype == ST_FLOAT) && + (nof_ranges + (has_value_list ? 1 : 0) > 1); + if (need_anyOf) { + // there are multiple value range/value list restrictions, + // they need to be grouped in an 'anyOf' structure + json.put_next_token(JSON_TOKEN_NAME, "anyOf"); + json.put_next_token(JSON_TOKEN_ARRAY_START); + json.put_next_token(JSON_TOKEN_OBJECT_START); + } + if (has_value_list) { + // generate the value list into an enum + json.put_next_token(JSON_TOKEN_NAME, "enum"); + json.put_next_token(JSON_TOKEN_ARRAY_START); + generate_json_schema_value_list(json, allow_special_float, false); + json.put_next_token(JSON_TOKEN_ARRAY_END); + if (my_owner->has_as_value_union()) { + // the original value list cannot always be recreated from the generated + // JSON value list in case of "as value" unions (because there are no field + // names) + // the list needs to be regenerated with field names (as if it was a regular + // union) under a new keyword (valueList) + json.put_next_token(JSON_TOKEN_NAME, "valueList"); + json.put_next_token(JSON_TOKEN_ARRAY_START); + generate_json_schema_value_list(json, allow_special_float, true); + json.put_next_token(JSON_TOKEN_ARRAY_END); + } + } + if (need_anyOf && has_value_list) { + // end of the value list and beginning of the first value range + json.put_next_token(JSON_TOKEN_OBJECT_END); + json.put_next_token(JSON_TOKEN_OBJECT_START); + } + if (nof_ranges > 0) { + switch (subtype) { + case ST_INTEGER: + case ST_FLOAT: + generate_json_schema_number_ranges(json); + break; + case ST_CHARSTRING: + case ST_UNIVERSAL_CHARSTRING: { + // merge all string range restrictions into one JSON schema pattern + char* pattern_str = mcopystrn("\"^[", 3); + pattern_str = generate_json_schema_string_ranges(pattern_str); + pattern_str = mputstrn(pattern_str, "]*$\"", 4); + json.put_next_token(JSON_TOKEN_NAME, "pattern"); + json.put_next_token(JSON_TOKEN_STRING, pattern_str); + Free(pattern_str); + break; } + default: + FATAL_ERROR("SubType::generate_json_schema - range %d", subtype); + } + } + if (need_anyOf) { + // end of the 'anyOf' structure + json.put_next_token(JSON_TOKEN_OBJECT_END); + json.put_next_token(JSON_TOKEN_ARRAY_END); + } +} + +void SubType::generate_json_schema_value_list(JSON_Tokenizer& json, + bool allow_special_float, + bool union_value_list) +{ + for (size_t i = 0; i < parsed->size(); ++i) { + SubTypeParse *parse = (*parsed)[i]; + if (parse->get_selection() == SubTypeParse::STP_SINGLE) { + if (parse->Single()->get_valuetype() == Value::V_REFD) { + Common::Assignment* ass = parse->Single()->get_reference()->get_refd_assignment(); + if (ass->get_asstype() == Common::Assignment::A_TYPE) { + // it's a reference to another subtype, insert its value list here + ass->get_Type()->get_sub_type()->generate_json_schema_value_list(json, + allow_special_float, union_value_list); + } + } + else { + Ttcn::JsonOmitCombination omit_combo(parse->Single()); + do { + parse->Single()->generate_json_value(json, allow_special_float, + union_value_list, &omit_combo); + } // only generate the first combination for the unions' "valueList" keyword + while (!union_value_list && omit_combo.next()); + } + } + } +} + +bool SubType::generate_json_schema_number_ranges(JSON_Tokenizer& json, bool first /* = true */) +{ + for (size_t i = 0; i < parsed->size(); ++i) { + SubTypeParse *parse = (*parsed)[i]; + if (parse->get_selection() == SubTypeParse::STP_SINGLE) { + if (parse->Single()->get_valuetype() == Value::V_REFD) { + Common::Assignment* ass = parse->Single()->get_reference()->get_refd_assignment(); + if (ass->get_asstype() == Common::Assignment::A_TYPE) { + // it's a reference to another subtype, insert its value ranges here + first = ass->get_Type()->get_sub_type()->generate_json_schema_number_ranges(json, first); + } + } + } + else if (parse->get_selection() == SubTypeParse::STP_RANGE) { + if (!first) { + // the ranges are in an 'anyOf' structure, they need to be placed in an object + json.put_next_token(JSON_TOKEN_OBJECT_END); + json.put_next_token(JSON_TOKEN_OBJECT_START); + } + else { + first = false; + } + // add the minimum and/or maximum values as numbers + if (parse->Min() != NULL) { + json.put_next_token(JSON_TOKEN_NAME, "minimum"); + parse->Min()->generate_json_value(json); + json.put_next_token(JSON_TOKEN_NAME, "exclusiveMinimum"); + json.put_next_token(parse->MinExclusive() ? JSON_TOKEN_LITERAL_TRUE : JSON_TOKEN_LITERAL_FALSE); + } + if (parse->Max() != NULL) { + json.put_next_token(JSON_TOKEN_NAME, "maximum"); + parse->Max()->generate_json_value(json); + json.put_next_token(JSON_TOKEN_NAME, "exclusiveMaximum"); + json.put_next_token(parse->MaxExclusive() ? JSON_TOKEN_LITERAL_TRUE : JSON_TOKEN_LITERAL_FALSE); + } + } + } + return first; +} + +char* SubType::generate_json_schema_string_ranges(char* pattern_str) +{ + for (size_t i = 0; i < parsed->size(); ++i) { + SubTypeParse *parse = (*parsed)[i]; + if (parse->get_selection() == SubTypeParse::STP_SINGLE) { + if (parse->Single()->get_valuetype() == Value::V_REFD) { + Common::Assignment* ass = parse->Single()->get_reference()->get_refd_assignment(); + if (ass->get_asstype() == Common::Assignment::A_TYPE) { + // it's a reference to another subtype, insert its string ranges here + pattern_str = ass->get_Type()->get_sub_type()->generate_json_schema_string_ranges(pattern_str); + } + } + } + else if (parse->get_selection() == SubTypeParse::STP_RANGE) { + // insert the string range into the pattern string + string lower_str = (subtype == ST_CHARSTRING) ? parse->Min()->get_val_str() : + ustring_to_uft8(parse->Min()->get_val_ustr()); + string upper_str = (subtype == ST_CHARSTRING) ? parse->Max()->get_val_str() : + ustring_to_uft8(parse->Max()->get_val_ustr()); + pattern_str = mputprintf(pattern_str, "%s-%s", lower_str.c_str(), upper_str.c_str()); + } + } + return pattern_str; +} + +void SubType::generate_json_schema_float(JSON_Tokenizer& json) +{ + bool has_nan = float_st->is_element(make_ttcn3float(REAL_NAN)); + bool has_pos_inf = float_st->is_element(make_ttcn3float(REAL_INFINITY)); + bool has_neg_inf = float_st->is_element(make_ttcn3float(-REAL_INFINITY)); + bool has_special = has_nan || has_pos_inf || has_neg_inf; + bool has_number = false; + for (size_t i = 0; i < parsed->size() && !has_number; ++i) { + // go through the restrictions and check if at least one number is allowed + SubTypeParse *parse = (*parsed)[i]; + switch (parse->get_selection()) { + case SubTypeParse::STP_SINGLE: { + Real r = parse->Single()->get_val_Real(); + if (r == r && r != REAL_INFINITY && r != -REAL_INFINITY) { + // a single value other than NaN, INF and -INF is a number + has_number = true; + } + break; } + case SubTypeParse::STP_RANGE: { + if (parse->Min() != NULL) { + if (parse->Min()->get_val_Real() != REAL_INFINITY) { + // a minimum value other than INF means a number is allowed + has_number = true; + } + } + if (parse->Max() != NULL) { + // a maximum value other than -INF means a number is allowed + if (parse->Max()->get_val_Real() != -REAL_INFINITY) { + has_number = true; + } + } + break; } + default: + break; + } + } + if (has_number && has_special) { + json.put_next_token(JSON_TOKEN_NAME, "anyOf"); + json.put_next_token(JSON_TOKEN_ARRAY_START); + json.put_next_token(JSON_TOKEN_OBJECT_START); + } + if (has_number) { + json.put_next_token(JSON_TOKEN_NAME, "type"); + json.put_next_token(JSON_TOKEN_STRING, "\"number\""); + // generate the restrictions' schema elements here + // (the 2nd parameter makes sure that NaN, INF and -INF are ignored) + generate_json_schema(json, false); + } + if (has_number && has_special) { + json.put_next_token(JSON_TOKEN_OBJECT_END); + json.put_next_token(JSON_TOKEN_OBJECT_START); + } + if (has_special) { + json.put_next_token(JSON_TOKEN_NAME, "enum"); + json.put_next_token(JSON_TOKEN_ARRAY_START); + if (has_nan) { + json.put_next_token(JSON_TOKEN_STRING, "\"not_a_number\""); + } + if (has_pos_inf) { + json.put_next_token(JSON_TOKEN_STRING, "\"infinity\""); + } + if (has_neg_inf) { + json.put_next_token(JSON_TOKEN_STRING, "\"-infinity\""); + } + json.put_next_token(JSON_TOKEN_ARRAY_END); + } + if (has_number && has_special) { + json.put_next_token(JSON_TOKEN_OBJECT_END); + json.put_next_token(JSON_TOKEN_ARRAY_END); + } +} + } // namespace Common