Sync with 5.4.2
[deliverable/titan.core.git] / regression_test / ttcn2json / f_ext_import_schema.cc
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 ******************************************************************************/
8
9 #include "f_ext_import_schema.hh"
10 #include <stdio.h>
11 #include <string.h>
12
13 namespace CompareSchemas {
14
15 #define IMPORT_FORMAT_ERROR(cond, comment) \
16 if (cond) { \
17 TTCN_error("Invalid format for file '%s' (%s). JSON schema could not be imported.", file_name, comment); \
18 }
19
20 ElemKey get_elem_key(const char* value, size_t value_len, const char* file_name)
21 {
22 if (4 == value_len && 0 == strncmp(value, "$ref", value_len)) {
23 return ElemKey::Ref;
24 }
25 if (4 == value_len && 0 == strncmp(value, "type", value_len)) {
26 return ElemKey::Type;
27 }
28 if (7 == value_len && 0 == strncmp(value, "subType", value_len)) {
29 return ElemKey::SubType;
30 }
31 if (7 == value_len && 0 == strncmp(value, "pattern", value_len)) {
32 return ElemKey::Pattern;
33 }
34 if (12 == value_len && 0 == strncmp(value, "originalName", value_len)) {
35 return ElemKey::OriginalName;
36 }
37 if (11 == value_len && 0 == strncmp(value, "unusedAlias", value_len)) {
38 return ElemKey::UnusedAlias;
39 }
40 if (20 == value_len && 0 == strncmp(value, "additionalProperties", value_len)) {
41 return ElemKey::AdditionalProperties;
42 }
43 if (10 == value_len && 0 == strncmp(value, "omitAsNull", value_len)) {
44 return ElemKey::OmitAsNull;
45 }
46 if (7 == value_len && 0 == strncmp(value, "default", value_len)) {
47 return ElemKey::Default;
48 }
49 if (8 == value_len && 0 == strncmp(value, "minItems", value_len)) {
50 return ElemKey::MinItems;
51 }
52 if (8 == value_len && 0 == strncmp(value, "maxItems", value_len)) {
53 return ElemKey::MaxItems;
54 }
55 if (4 == value_len && 0 == strncmp(value, "enum", value_len)) {
56 return ElemKey::Enum;
57 }
58 if (13 == value_len && 0 == strncmp(value, "numericValues", value_len)) {
59 return ElemKey::NumericValues;
60 }
61 if (5 == value_len && 0 == strncmp(value, "items", value_len)) {
62 return ElemKey::Items;
63 }
64 if (5 == value_len && 0 == strncmp(value, "anyOf", value_len)) {
65 return ElemKey::AnyOf;
66 }
67 if (8 == value_len && 0 == strncmp(value, "required", value_len)) {
68 return ElemKey::Required;
69 }
70 if (10 == value_len && 0 == strncmp(value, "fieldOrder", value_len)) {
71 return ElemKey::FieldOrder;
72 }
73 if (10 == value_len && 0 == strncmp(value, "properties", value_len)) {
74 return ElemKey::Properties;
75 }
76 if (9 == value_len && 0 == strncmp(value, "minLength", value_len)) {
77 return ElemKey::MinLength;
78 }
79 if (9 == value_len && 0 == strncmp(value, "maxLength", value_len)) {
80 return ElemKey::MaxLength;
81 }
82 if (7 == value_len && 0 == strncmp(value, "minimum", value_len)) {
83 return ElemKey::Minimum;
84 }
85 if (7 == value_len && 0 == strncmp(value, "maximum", value_len)) {
86 return ElemKey::Maximum;
87 }
88 if (16 == value_len && 0 == strncmp(value, "exclusiveMinimum", value_len)) {
89 return ElemKey::Maximum;
90 }
91 if (16 == value_len && 0 == strncmp(value, "exclusiveMaximum", value_len)) {
92 return ElemKey::Maximum;
93 }
94 if (5 == value_len && 0 == strncmp(value, "allOf", value_len)) {
95 return ElemKey::AllOf;
96 }
97 if (9 == value_len && 0 == strncmp(value, "valueList", value_len)) {
98 return ElemKey::ValueList;
99 }
100 // it's an extension if none of them matched
101 return ElemKey::Extension;
102 }
103
104 // just a forward declaration
105 AnyValue extract_any_value(JSON_Tokenizer& json, const char* file_name);
106
107 ObjectValue extract_object_value(JSON_Tokenizer& json, const char* file_name)
108 {
109 json_token_t token = JSON_TOKEN_NONE;
110 char* value = NULL;
111 size_t value_len = 0;
112 ObjectValue object_value;
113
114 int field_index = 0;
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);
121
122 // next field
123 ++field_index;
124 json.get_next_token(&token, &value, &value_len);
125 }
126
127 // object end
128 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END != token, "missing object value end");
129 return object_value;
130 }
131
132 ArrayValue extract_array_value(JSON_Tokenizer& json, const char* file_name)
133 {
134 ArrayValue array_value;
135 size_t nof_values = 0;
136 while(true) {
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;
141 ++nof_values;
142 }
143 else {
144 // array end token reached (signalled by the unbound value)
145 break;
146 }
147 }
148 return array_value;
149 }
150
151 AnyValue extract_any_value(JSON_Tokenizer& json, const char* file_name)
152 {
153 json_token_t token = JSON_TOKEN_NONE;
154 char* value = NULL;
155 size_t value_len = 0;
156 AnyValue any_value;
157
158 json.get_next_token(&token, &value, &value_len);
159 switch (token) {
160 case JSON_TOKEN_NUMBER:
161 case JSON_TOKEN_STRING: {
162 CHARSTRING str_val(value_len, value);
163 any_value.strVal() = str_val;
164 break; }
165 case JSON_TOKEN_LITERAL_NULL:
166 any_value.strVal() = "null";
167 break;
168 case JSON_TOKEN_LITERAL_TRUE:
169 any_value.boolVal() = TRUE;
170 break;
171 case JSON_TOKEN_LITERAL_FALSE:
172 any_value.boolVal() = FALSE;
173 break;
174 case JSON_TOKEN_OBJECT_START:
175 any_value.objectVal() = extract_object_value(json, file_name);
176 break;
177 case JSON_TOKEN_ARRAY_START:
178 any_value.arrayVal() = extract_array_value(json, file_name);
179 break;
180 case JSON_TOKEN_ARRAY_END:
181 // signal the end of an array by returning an unbound AnyValue
182 break;
183 default:
184 IMPORT_FORMAT_ERROR(TRUE, "missing JSON value");
185 }
186 return any_value;
187 }
188
189 TypeSchema extract_type_schema(JSON_Tokenizer& json, const char* file_name)
190 {
191 json_token_t token = JSON_TOKEN_NONE;
192 char* value = NULL;
193 size_t value_len = 0;
194
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");
198
199 // type schema elements
200 TypeSchema type_schema;
201 int elem_index = 0;
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()) {
206 case ElemKey::Ref:
207 case ElemKey::Type:
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);
223 switch (token) {
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;
228 break; }
229 case JSON_TOKEN_LITERAL_FALSE:
230 type_schema[elem_index].val().boolVal() = FALSE;
231 break;
232 case JSON_TOKEN_LITERAL_TRUE:
233 type_schema[elem_index].val().boolVal() = TRUE;
234 break;
235 default:
236 IMPORT_FORMAT_ERROR(JSON_TOKEN_LITERAL_FALSE != token, "string, number or boolean value expected");
237 }
238 break; }
239
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;
251
252 // next string
253 ++string_index;
254 json.get_next_token(&token, &value, &value_len);
255 }
256
257 // string array end
258 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_END != token, "missing string array end");
259 break; }
260
261 case ElemKey::Items: {
262 // type schema value
263 type_schema[elem_index].val().typeVal() = extract_type_schema(json, file_name);
264 break; }
265
266 case ElemKey::AnyOf:
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");
271 int type_index = 0;
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);
278
279 // next type schema
280 ++type_index;
281 buf_pos = json.get_buf_pos();
282 json.get_next_token(&token, NULL, NULL);
283 }
284
285 // type schema array end
286 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_END != token, "missing type array end");
287 break; }
288
289 case ElemKey::Properties: {
290 // field set value
291 json.get_next_token(&token, NULL, NULL);
292 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_START != token, "missing field set start");
293 int field_index = 0;
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);
300
301 // next field
302 ++field_index;
303 json.get_next_token(&token, &value, &value_len);
304 }
305
306 // field set value end
307 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END != token, "missing field set end");
308 break; }
309 case ElemKey::Extension: {
310 // extension value
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;
319 break; }
320
321 case ElemKey::Enum:
322 case ElemKey::ValueList: {
323 // array value
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);
327 break; }
328 default:
329 break;
330 }
331
332 // next schema element
333 ++elem_index;
334 json.get_next_token(&token, &value, &value_len);
335 }
336
337 // type schema object end
338 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END != token, "missing type object end");
339 return type_schema;
340 }
341
342 EncDecData extract_enc_dec_data(JSON_Tokenizer& json, const char* file_name)
343 {
344 // initialize (error behavior and printing data might not appear in the schema)
345 json_token_t token = JSON_TOKEN_NONE;
346 char* value = NULL;
347 size_t value_len = 0;
348 EncDecData data;
349 data.eb() = OMIT_VALUE;
350 data.printing() = OMIT_VALUE;
351
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);
362 int pt_index = 0;
363 while(JSON_TOKEN_STRING == token) {
364 CHARSTRING pt_str(value_len, value);
365 data.prototype()[pt_index] = pt_str;
366
367 // next prototype element
368 ++pt_index;
369 json.get_next_token(&token, &value, &value_len);
370 }
371
372 // prototype end
373 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_END != token, "missing prototype array end");
374 }
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);
380 int eb_index = 0;
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;
389
390 // next pair
391 ++eb_index;
392 json.get_next_token(&token, &value, &value_len);
393 }
394
395 // error behavior end
396 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END != token, "missing error behavior end");
397 }
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;
403 }
404 else {
405 // invalid key
406 IMPORT_FORMAT_ERROR(true, "invalid enc/dec data key");
407 }
408
409 // next key-value pair
410 json.get_next_token(&token, &value, &value_len);
411 }
412
413 // enc/dec data end
414 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END != token, "missing enc/dec data end");
415 return data;
416 }
417
418 RefSchema extract_ref_schema(JSON_Tokenizer& json, const char* file_name)
419 {
420 // initialize (set optional fields to "omit")
421 json_token_t token = JSON_TOKEN_NONE;
422 char* value = NULL;
423 size_t value_len = 0;
424 RefSchema ref_schema;
425 ref_schema.enc() = OMIT_VALUE;
426 ref_schema.dec() = OMIT_VALUE;
427
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)) {
432 // reference
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;
437 }
438 else if (8 == value_len && 0 == strncmp(value, "encoding", value_len)) {
439 // encoding function
440 ref_schema.enc()() = extract_enc_dec_data(json, file_name);
441 }
442 else if (8 == value_len && 0 == strncmp(value, "decoding", value_len)) {
443 // encoding function
444 ref_schema.dec()() = extract_enc_dec_data(json, file_name);
445 }
446 else {
447 // invalid key
448 IMPORT_FORMAT_ERROR(true, "invalid reference/function data key");
449 }
450
451 // next key-value pair
452 json.get_next_token(&token, &value, &value_len);
453 }
454
455 // reference & function info end
456 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END != token, "missing reference/function data end");
457 return ref_schema;
458 }
459
460 void f__ext__import__schema(const CHARSTRING& file, JsonSchema& schema)
461 {
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");
465 try {
466 if (NULL == fp) {
467 TTCN_error("Could not open file '%s' for reading. JSON schema could not be imported.", file_name);
468 }
469
470 // get the file size
471 fseek(fp, 0, SEEK_END);
472 int file_size = ftell(fp);
473 rewind(fp);
474
475 // read the entire file into a character buffer
476 char* buffer = (char*)Malloc(file_size);
477 fread(buffer, 1, file_size, fp);
478 fclose(fp);
479
480 // initialize a JSON tokenizer with the buffer
481 JSON_Tokenizer json(buffer, file_size);
482 Free(buffer);
483
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;
487 char* value = NULL;
488 size_t value_len = 0;
489
490 // top level object
491 json.get_next_token(&token, NULL, NULL);
492 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_START != token, "missing top level object start");
493
494 // schema header
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");
502
503 // definitions
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");
507
508 // module list
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);
512 int type_index = 0;
513 while (JSON_TOKEN_NAME == token) {
514 // extract module name, it will be inserted later
515 CHARSTRING module_name(value_len, value);
516
517 // type list
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) {
522 // extract type name
523 CHARSTRING type_name(value_len, value);
524
525 // extract type schema
526 TypeSchema type_schema = extract_type_schema(json, file_name);
527
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;
532
533 // next type
534 ++type_index;
535 json.get_next_token(&token, &value, &value_len);
536 }
537
538 // end of type list
539 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END != token, "missing type list end");
540
541 // next module
542 json.get_next_token(&token, &value, &value_len);
543 }
544
545 // end of module list
546 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END != token, "missing module list end");
547
548 if (0 == type_index) {
549 // no definitions, don't leave the field unbound
550 schema.defs().set_size(0);
551 }
552
553 // top level anyOf
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");
557
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);
562 int ref_index = 0;
563 while (JSON_TOKEN_OBJECT_START == token) {
564
565 // extract reference schema
566 schema.refs()[ref_index] = extract_ref_schema(json, file_name);
567
568 // next reference
569 ++ref_index;
570 json.get_next_token(&token, NULL, NULL);
571 }
572
573 // end of reference & function info array
574 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_END != token, "missing reference/function list end");
575
576 if (0 == ref_index) {
577 // no references, don't leave the field unbound
578 schema.refs().set_size(0);
579 }
580
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");
584
585 // end of the schema
586 json.get_next_token(&token, NULL, NULL);
587 IMPORT_FORMAT_ERROR(JSON_TOKEN_NONE != token, "expected end of file");
588 }
589 catch (...) {
590 Free(file_name);
591 throw;
592 }
593 Free(file_name);
594 }
595
596 }
597
This page took 0.044046 seconds and 5 git commands to generate.