1 /******************************************************************************
2 * Copyright (c) 2000-2015 Ericsson Telecom AB
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 ******************************************************************************/
9 #include "f_ext_import_schema.hh"
13 namespace CompareSchemas
{
15 #define IMPORT_FORMAT_ERROR(cond, comment) \
17 TTCN_error("Invalid format for file '%s' (%s). JSON schema could not be imported.", file_name, comment); \
20 ElemKey
get_elem_key(const char* value
, size_t value_len
, const char* file_name
)
22 if (4 == value_len
&& 0 == strncmp(value
, "$ref", value_len
)) {
25 if (4 == value_len
&& 0 == strncmp(value
, "type", value_len
)) {
28 if (7 == value_len
&& 0 == strncmp(value
, "subType", value_len
)) {
29 return ElemKey::SubType
;
31 if (7 == value_len
&& 0 == strncmp(value
, "pattern", value_len
)) {
32 return ElemKey::Pattern
;
34 if (12 == value_len
&& 0 == strncmp(value
, "originalName", value_len
)) {
35 return ElemKey::OriginalName
;
37 if (11 == value_len
&& 0 == strncmp(value
, "unusedAlias", value_len
)) {
38 return ElemKey::UnusedAlias
;
40 if (20 == value_len
&& 0 == strncmp(value
, "additionalProperties", value_len
)) {
41 return ElemKey::AdditionalProperties
;
43 if (10 == value_len
&& 0 == strncmp(value
, "omitAsNull", value_len
)) {
44 return ElemKey::OmitAsNull
;
46 if (7 == value_len
&& 0 == strncmp(value
, "default", value_len
)) {
47 return ElemKey::Default
;
49 if (8 == value_len
&& 0 == strncmp(value
, "minItems", value_len
)) {
50 return ElemKey::MinItems
;
52 if (8 == value_len
&& 0 == strncmp(value
, "maxItems", value_len
)) {
53 return ElemKey::MaxItems
;
55 if (4 == value_len
&& 0 == strncmp(value
, "enum", value_len
)) {
58 if (13 == value_len
&& 0 == strncmp(value
, "numericValues", value_len
)) {
59 return ElemKey::NumericValues
;
61 if (5 == value_len
&& 0 == strncmp(value
, "items", value_len
)) {
62 return ElemKey::Items
;
64 if (5 == value_len
&& 0 == strncmp(value
, "anyOf", value_len
)) {
65 return ElemKey::AnyOf
;
67 if (8 == value_len
&& 0 == strncmp(value
, "required", value_len
)) {
68 return ElemKey::Required
;
70 if (10 == value_len
&& 0 == strncmp(value
, "fieldOrder", value_len
)) {
71 return ElemKey::FieldOrder
;
73 if (10 == value_len
&& 0 == strncmp(value
, "properties", value_len
)) {
74 return ElemKey::Properties
;
76 if (9 == value_len
&& 0 == strncmp(value
, "minLength", value_len
)) {
77 return ElemKey::MinLength
;
79 if (9 == value_len
&& 0 == strncmp(value
, "maxLength", value_len
)) {
80 return ElemKey::MaxLength
;
82 if (7 == value_len
&& 0 == strncmp(value
, "minimum", value_len
)) {
83 return ElemKey::Minimum
;
85 if (7 == value_len
&& 0 == strncmp(value
, "maximum", value_len
)) {
86 return ElemKey::Maximum
;
88 if (16 == value_len
&& 0 == strncmp(value
, "exclusiveMinimum", value_len
)) {
89 return ElemKey::Maximum
;
91 if (16 == value_len
&& 0 == strncmp(value
, "exclusiveMaximum", value_len
)) {
92 return ElemKey::Maximum
;
94 if (5 == value_len
&& 0 == strncmp(value
, "allOf", value_len
)) {
95 return ElemKey::AllOf
;
97 if (9 == value_len
&& 0 == strncmp(value
, "valueList", value_len
)) {
98 return ElemKey::ValueList
;
100 // it's an extension if none of them matched
101 return ElemKey::Extension
;
104 // just a forward declaration
105 AnyValue
extract_any_value(JSON_Tokenizer
& json
, const char* file_name
);
107 ObjectValue
extract_object_value(JSON_Tokenizer
& json
, const char* file_name
)
109 json_token_t token
= JSON_TOKEN_NONE
;
111 size_t value_len
= 0;
112 ObjectValue object_value
;
115 json
.get_next_token(&token
, &value
, &value_len
);
116 while(JSON_TOKEN_NAME
== token
) {
117 // extract fields until an object end token is found
118 CHARSTRING
field_name(value_len
, value
);
119 object_value
[field_index
].key() = field_name
;
120 object_value
[field_index
].val() = extract_any_value(json
, file_name
);
124 json
.get_next_token(&token
, &value
, &value_len
);
128 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END
!= token
, "missing object value end");
132 ArrayValue
extract_array_value(JSON_Tokenizer
& json
, const char* file_name
)
134 ArrayValue array_value
;
135 size_t nof_values
= 0;
137 // extract values until the array's end is reached
138 AnyValue val
= extract_any_value(json
, file_name
);
139 if (val
.is_bound()) {
140 array_value
[nof_values
] = val
;
144 // array end token reached (signalled by the unbound value)
151 AnyValue
extract_any_value(JSON_Tokenizer
& json
, const char* file_name
)
153 json_token_t token
= JSON_TOKEN_NONE
;
155 size_t value_len
= 0;
158 json
.get_next_token(&token
, &value
, &value_len
);
160 case JSON_TOKEN_NUMBER
:
161 case JSON_TOKEN_STRING
: {
162 CHARSTRING
str_val(value_len
, value
);
163 any_value
.strVal() = str_val
;
165 case JSON_TOKEN_LITERAL_NULL
:
166 any_value
.strVal() = "null";
168 case JSON_TOKEN_LITERAL_TRUE
:
169 any_value
.boolVal() = TRUE
;
171 case JSON_TOKEN_LITERAL_FALSE
:
172 any_value
.boolVal() = FALSE
;
174 case JSON_TOKEN_OBJECT_START
:
175 any_value
.objectVal() = extract_object_value(json
, file_name
);
177 case JSON_TOKEN_ARRAY_START
:
178 any_value
.arrayVal() = extract_array_value(json
, file_name
);
180 case JSON_TOKEN_ARRAY_END
:
181 // signal the end of an array by returning an unbound AnyValue
184 IMPORT_FORMAT_ERROR(TRUE
, "missing JSON value");
189 TypeSchema
extract_type_schema(JSON_Tokenizer
& json
, const char* file_name
)
191 json_token_t token
= JSON_TOKEN_NONE
;
193 size_t value_len
= 0;
195 // type schema object start
196 json
.get_next_token(&token
, NULL
, NULL
);
197 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_START
!= token
, "missing type object start");
199 // type schema elements
200 TypeSchema type_schema
;
202 json
.get_next_token(&token
, &value
, &value_len
);
203 while(JSON_TOKEN_NAME
== token
) {
204 type_schema
[elem_index
].key() = get_elem_key(value
, value_len
, file_name
);
205 switch(type_schema
[elem_index
].key()) {
208 case ElemKey::SubType
:
209 case ElemKey::Pattern
:
210 case ElemKey::OriginalName
:
211 case ElemKey::UnusedAlias
:
212 case ElemKey::MinItems
:
213 case ElemKey::MaxItems
:
214 case ElemKey::AdditionalProperties
:
215 case ElemKey::OmitAsNull
:
216 case ElemKey::Default
:
217 case ElemKey::MinLength
:
218 case ElemKey::MaxLength
:
219 case ElemKey::Minimum
:
220 case ElemKey::Maximum
: {
221 // string or boolean value
222 json
.get_next_token(&token
, &value
, &value_len
);
224 case JSON_TOKEN_STRING
:
225 case JSON_TOKEN_NUMBER
: {
226 CHARSTRING
str_val(value_len
, value
);
227 type_schema
[elem_index
].val().strVal() = str_val
;
229 case JSON_TOKEN_LITERAL_FALSE
:
230 type_schema
[elem_index
].val().boolVal() = FALSE
;
232 case JSON_TOKEN_LITERAL_TRUE
:
233 type_schema
[elem_index
].val().boolVal() = TRUE
;
236 IMPORT_FORMAT_ERROR(JSON_TOKEN_LITERAL_FALSE
!= token
, "string, number or boolean value expected");
240 case ElemKey::NumericValues
:
241 case ElemKey::Required
:
242 case ElemKey::FieldOrder
: {
243 // string array value
244 json
.get_next_token(&token
, NULL
, NULL
);
245 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_START
!= token
, "missing string array start");
246 json
.get_next_token(&token
, &value
, &value_len
);
247 int string_index
= 0;
248 while (JSON_TOKEN_STRING
== token
|| JSON_TOKEN_NUMBER
== token
) {
249 CHARSTRING
str_val(value_len
, value
);
250 type_schema
[elem_index
].val().strArrayVal()[string_index
] = str_val
;
254 json
.get_next_token(&token
, &value
, &value_len
);
258 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_END
!= token
, "missing string array end");
261 case ElemKey::Items
: {
263 type_schema
[elem_index
].val().typeVal() = extract_type_schema(json
, file_name
);
267 case ElemKey::AllOf
: {
268 // type schema array value
269 json
.get_next_token(&token
, NULL
, NULL
);
270 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_START
!= token
, "missing type array start");
272 size_t buf_pos
= json
.get_buf_pos();
273 json
.get_next_token(&token
, NULL
, NULL
);
274 while (JSON_TOKEN_OBJECT_START
== token
) {
275 // revert this extraction, the type schema extractor will read the "{" again
276 json
.set_buf_pos(buf_pos
);
277 type_schema
[elem_index
].val().typeArrayVal()[type_index
] = extract_type_schema(json
, file_name
);
281 buf_pos
= json
.get_buf_pos();
282 json
.get_next_token(&token
, NULL
, NULL
);
285 // type schema array end
286 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_END
!= token
, "missing type array end");
289 case ElemKey::Properties
: {
291 json
.get_next_token(&token
, NULL
, NULL
);
292 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_START
!= token
, "missing field set start");
294 json
.get_next_token(&token
, &value
, &value_len
);
295 while(JSON_TOKEN_NAME
== token
) {
296 // store field name and schema
297 CHARSTRING
field_name(value_len
, value
);
298 type_schema
[elem_index
].val().fieldSetVal()[field_index
].fieldName() = field_name
;
299 type_schema
[elem_index
].val().fieldSetVal()[field_index
].schema() = extract_type_schema(json
, file_name
);
303 json
.get_next_token(&token
, &value
, &value_len
);
306 // field set value end
307 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END
!= token
, "missing field set end");
309 case ElemKey::Extension
: {
311 // store the field name (already extracted)
312 CHARSTRING
str_key(value_len
, value
);
313 type_schema
[elem_index
].val().extVal().key() = str_key
;
314 // store the string value
315 json
.get_next_token(&token
, &value
, &value_len
);
316 IMPORT_FORMAT_ERROR(JSON_TOKEN_STRING
!= token
, "string value expected");
317 CHARSTRING
str_val(value_len
, value
);
318 type_schema
[elem_index
].val().extVal().val() = str_key
;
322 case ElemKey::ValueList
: {
324 json
.get_next_token(&token
, NULL
, NULL
);
325 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_START
!= token
, "missing array value start");
326 type_schema
[elem_index
].val().arrayVal() = extract_array_value(json
, file_name
);
332 // next schema element
334 json
.get_next_token(&token
, &value
, &value_len
);
337 // type schema object end
338 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END
!= token
, "missing type object end");
342 EncDecData
extract_enc_dec_data(JSON_Tokenizer
& json
, const char* file_name
)
344 // initialize (error behavior and printing data might not appear in the schema)
345 json_token_t token
= JSON_TOKEN_NONE
;
347 size_t value_len
= 0;
349 data
.eb() = OMIT_VALUE
;
350 data
.printing() = OMIT_VALUE
;
352 // enc/dec data start
353 json
.get_next_token(&token
, NULL
, NULL
);
354 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_START
!= token
, "missing enc/dec data start");
355 json
.get_next_token(&token
, &value
, &value_len
);
356 while(JSON_TOKEN_NAME
== token
) {
357 if (9 == value_len
&& 0 == strncmp(value
, "prototype", value_len
)) {
358 // prototype (string array)
359 json
.get_next_token(&token
, NULL
, NULL
);
360 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_START
!= token
, "missing prototype array start");
361 json
.get_next_token(&token
, &value
, &value_len
);
363 while(JSON_TOKEN_STRING
== token
) {
364 CHARSTRING
pt_str(value_len
, value
);
365 data
.prototype()[pt_index
] = pt_str
;
367 // next prototype element
369 json
.get_next_token(&token
, &value
, &value_len
);
373 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_END
!= token
, "missing prototype array end");
375 else if (13 == value_len
&& 0 == strncmp(value
, "errorBehavior", value_len
)) {
376 // error behavior (object)
377 json
.get_next_token(&token
, NULL
, NULL
);
378 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_START
!= token
, "missing error behavior start");
379 json
.get_next_token(&token
, &value
, &value_len
);
381 while(JSON_TOKEN_NAME
== token
) {
382 // store the key-value pair
383 CHARSTRING
error_type(value_len
, value
);
384 json
.get_next_token(&token
, &value
, &value_len
);
385 IMPORT_FORMAT_ERROR(JSON_TOKEN_STRING
!= token
, "expected error behavior string");
386 CHARSTRING
error_behavior(value_len
, value
);
387 data
.eb()()[eb_index
].errorType() = error_type
;
388 data
.eb()()[eb_index
].errorBehavior() = error_behavior
;
392 json
.get_next_token(&token
, &value
, &value_len
);
395 // error behavior end
396 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END
!= token
, "missing error behavior end");
398 else if (8 == value_len
&& 0 == strncmp(value
, "printing", value_len
)) {
399 json
.get_next_token(&token
, &value
, &value_len
);
400 IMPORT_FORMAT_ERROR(JSON_TOKEN_STRING
!= token
, "expected printing string");
401 CHARSTRING
printing_str(value_len
, value
);
402 data
.printing()() = printing_str
;
406 IMPORT_FORMAT_ERROR(true, "invalid enc/dec data key");
409 // next key-value pair
410 json
.get_next_token(&token
, &value
, &value_len
);
414 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END
!= token
, "missing enc/dec data end");
418 RefSchema
extract_ref_schema(JSON_Tokenizer
& json
, const char* file_name
)
420 // initialize (set optional fields to "omit")
421 json_token_t token
= JSON_TOKEN_NONE
;
423 size_t value_len
= 0;
424 RefSchema ref_schema
;
425 ref_schema
.enc() = OMIT_VALUE
;
426 ref_schema
.dec() = OMIT_VALUE
;
428 // read reference data
429 json
.get_next_token(&token
, &value
, &value_len
);
430 while(JSON_TOKEN_NAME
== token
) {
431 if (4 == value_len
&& 0 == strncmp(value
, "$ref", value_len
)) {
433 json
.get_next_token(&token
, &value
, &value_len
);
434 IMPORT_FORMAT_ERROR(JSON_TOKEN_STRING
!= token
, "missing reference string");
435 CHARSTRING
ref_str(value_len
, value
);
436 ref_schema
.ref() = ref_str
;
438 else if (8 == value_len
&& 0 == strncmp(value
, "encoding", value_len
)) {
440 ref_schema
.enc()() = extract_enc_dec_data(json
, file_name
);
442 else if (8 == value_len
&& 0 == strncmp(value
, "decoding", value_len
)) {
444 ref_schema
.dec()() = extract_enc_dec_data(json
, file_name
);
448 IMPORT_FORMAT_ERROR(true, "invalid reference/function data key");
451 // next key-value pair
452 json
.get_next_token(&token
, &value
, &value_len
);
455 // reference & function info end
456 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END
!= token
, "missing reference/function data end");
460 void f__ext__import__schema(const CHARSTRING
& file
, JsonSchema
& schema
)
462 // need a null-terminated string for fopen
463 char* file_name
= mcopystrn((const char*)file
, file
.lengthof());
464 FILE* fp
= fopen(file_name
, "r");
467 TTCN_error("Could not open file '%s' for reading. JSON schema could not be imported.", file_name
);
471 fseek(fp
, 0, SEEK_END
);
472 int file_size
= ftell(fp
);
475 // read the entire file into a character buffer
476 char* buffer
= (char*)Malloc(file_size
);
477 fread(buffer
, 1, file_size
, fp
);
480 // initialize a JSON tokenizer with the buffer
481 JSON_Tokenizer
json(buffer
, file_size
);
484 // extract tokens and store the schema in the JsonSchema parameter
485 // throw a DTE if the file format is invalid
486 json_token_t token
= JSON_TOKEN_NONE
;
488 size_t value_len
= 0;
491 json
.get_next_token(&token
, NULL
, NULL
);
492 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_START
!= token
, "missing top level object start");
495 json
.get_next_token(&token
, &value
, &value_len
);
496 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME
!= token
|| value_len
!= 7 ||
497 0 != strncmp(value
, "$schema", value_len
), "missing $schema key");
498 json
.get_next_token(&token
, &value
, &value_len
);
499 IMPORT_FORMAT_ERROR(JSON_TOKEN_STRING
!= token
|| value_len
!= 41 ||
500 0 != strncmp(value
, "\"http://json-schema.org/draft-04/schema#\"", value_len
),
501 "missing $schema value");
504 json
.get_next_token(&token
, &value
, &value_len
);
505 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME
!= token
|| value_len
!= 11 ||
506 0 != strncmp(value
, "definitions", value_len
), "missing definitions key");
509 json
.get_next_token(&token
, NULL
, NULL
);
510 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_START
!= token
, "missing module list start");
511 json
.get_next_token(&token
, &value
, &value_len
);
513 while (JSON_TOKEN_NAME
== token
) {
514 // extract module name, it will be inserted later
515 CHARSTRING
module_name(value_len
, value
);
518 json
.get_next_token(&token
, NULL
, NULL
);
519 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_START
!= token
, "missing type list start");
520 json
.get_next_token(&token
, &value
, &value_len
);
521 while (JSON_TOKEN_NAME
== token
) {
523 CHARSTRING
type_name(value_len
, value
);
525 // extract type schema
526 TypeSchema type_schema
= extract_type_schema(json
, file_name
);
528 // store definition data
529 schema
.defs()[type_index
].moduleName() = module_name
;
530 schema
.defs()[type_index
].typeName() = type_name
;
531 schema
.defs()[type_index
].schema() = type_schema
;
535 json
.get_next_token(&token
, &value
, &value_len
);
539 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END
!= token
, "missing type list end");
542 json
.get_next_token(&token
, &value
, &value_len
);
545 // end of module list
546 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END
!= token
, "missing module list end");
548 if (0 == type_index
) {
549 // no definitions, don't leave the field unbound
550 schema
.defs().set_size(0);
554 json
.get_next_token(&token
, &value
, &value_len
);
555 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME
!= token
|| value_len
!= 5 ||
556 0 != strncmp(value
, "anyOf", value_len
), "missing anyOf key");
558 // reference & function info array
559 json
.get_next_token(&token
, NULL
, NULL
);
560 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_START
!= token
, "missing reference/function list start");
561 json
.get_next_token(&token
, NULL
, NULL
);
563 while (JSON_TOKEN_OBJECT_START
== token
) {
565 // extract reference schema
566 schema
.refs()[ref_index
] = extract_ref_schema(json
, file_name
);
570 json
.get_next_token(&token
, NULL
, NULL
);
573 // end of reference & function info array
574 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_END
!= token
, "missing reference/function list end");
576 if (0 == ref_index
) {
577 // no references, don't leave the field unbound
578 schema
.refs().set_size(0);
581 // end of top level object
582 json
.get_next_token(&token
, NULL
, NULL
);
583 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END
!= token
, "missing top level object end");
586 json
.get_next_token(&token
, NULL
, NULL
);
587 IMPORT_FORMAT_ERROR(JSON_TOKEN_NONE
!= token
, "expected end of file");
This page took 0.044046 seconds and 5 git commands to generate.