Commit | Line | Data |
---|---|---|
3abe9331 | 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 | ||
970ed795 EL |
9 | #include "Ttcn2Json.hh" |
10 | ||
11 | #include "compiler.h" | |
12 | ||
13 | #include "../AST.hh" | |
14 | #include "../../common/JSON_Tokenizer.hh" | |
3f84031e | 15 | #include "../Value.hh" |
16 | #include "../Valuestuff.hh" | |
970ed795 EL |
17 | |
18 | // forward declarations | |
19 | namespace Common { | |
20 | class Type; | |
21 | } | |
22 | ||
23 | namespace Ttcn { | |
24 | ||
25 | Ttcn2Json::Ttcn2Json(Common::Modules* p_modules, const char* p_schema_name) | |
26 | : modules(p_modules) | |
27 | { | |
28 | boolean is_temporary; | |
29 | FILE* file = open_output_file(p_schema_name, &is_temporary); | |
30 | ||
31 | JSON_Tokenizer json(true); | |
32 | ||
33 | create_schema(json); | |
34 | ||
35 | fprintf(file, "%s\n", json.get_buffer()); | |
36 | ||
37 | close_output_file(p_schema_name, file, is_temporary, 0); | |
38 | } | |
39 | ||
40 | void Ttcn2Json::create_schema(JSON_Tokenizer& json) | |
41 | { | |
42 | // top-level object start | |
43 | json.put_next_token(JSON_TOKEN_OBJECT_START, NULL); | |
44 | ||
3abe9331 | 45 | // insert the schema header |
46 | json.put_next_token(JSON_TOKEN_NAME, "$schema"); | |
47 | json.put_next_token(JSON_TOKEN_STRING, "\"http://json-schema.org/draft-04/schema#\""); | |
48 | ||
970ed795 EL |
49 | // start of type definitions |
50 | json.put_next_token(JSON_TOKEN_NAME, "definitions"); | |
51 | json.put_next_token(JSON_TOKEN_OBJECT_START, NULL); | |
52 | ||
af710487 | 53 | // insert module names and schemas for types; gather references to types and |
54 | // JSON encoding/decoding function information | |
55 | map<Common::Type*, JSON_Tokenizer> json_refs; | |
56 | modules->generate_json_schema(json, json_refs); | |
970ed795 EL |
57 | |
58 | // end of type definitions | |
59 | json.put_next_token(JSON_TOKEN_OBJECT_END, NULL); | |
af710487 | 60 | |
61 | if (!json_refs.empty()) { | |
62 | // top-level "anyOf" structure containing references to the types the schema validates | |
63 | // don't insert an empty "anyOf" if there are no references | |
64 | json.put_next_token(JSON_TOKEN_NAME, "anyOf"); | |
65 | json.put_next_token(JSON_TOKEN_ARRAY_START, NULL); | |
66 | ||
67 | // close schema segments and add them to the main schema | |
68 | for (size_t i = 0; i < json_refs.size(); ++i) { | |
69 | JSON_Tokenizer* segment = json_refs.get_nth_elem(i); | |
70 | segment->put_next_token(JSON_TOKEN_OBJECT_END, NULL); | |
71 | insert_schema(json, *segment); | |
72 | delete segment; | |
73 | } | |
74 | json_refs.clear(); | |
75 | ||
76 | // end of the "anyOf" structure | |
77 | json.put_next_token(JSON_TOKEN_ARRAY_END, NULL); | |
970ed795 | 78 | } |
970ed795 EL |
79 | |
80 | // top-level object end | |
81 | json.put_next_token(JSON_TOKEN_OBJECT_END, NULL); | |
82 | } | |
83 | ||
84 | void Ttcn2Json::insert_schema(JSON_Tokenizer& to, JSON_Tokenizer& from) | |
85 | { | |
86 | json_token_t token = JSON_TOKEN_NONE; | |
87 | char* value_str = NULL; | |
88 | size_t value_len = 0; | |
89 | char temp = 0; | |
90 | ||
91 | do { | |
92 | from.get_next_token(&token, &value_str, &value_len); | |
93 | ||
94 | if (token == JSON_TOKEN_ERROR) { | |
95 | FATAL_ERROR("Ttcn2Json::insert_schema"); | |
96 | } | |
97 | ||
98 | if (value_str != NULL) { | |
99 | // put_next_token expects a null terminated string, save the next character | |
100 | // and set it to null | |
101 | temp = value_str[value_len]; | |
102 | value_str[value_len] = 0; | |
103 | } | |
104 | ||
105 | to.put_next_token(token, value_str); | |
106 | ||
107 | if (value_str != NULL) { | |
108 | // put the original character back to its place | |
109 | value_str[value_len] = temp; | |
110 | } | |
111 | } while (token != JSON_TOKEN_NONE); | |
112 | } | |
113 | ||
3f84031e | 114 | JsonOmitCombination::JsonOmitCombination(Common::Value* p_top_level) |
115 | { | |
116 | parse_value(p_top_level->get_value_refd_last()); | |
117 | } | |
118 | ||
119 | JsonOmitCombination::~JsonOmitCombination() | |
120 | { | |
121 | for (size_t i = 0; i < combinations.size(); ++i) { | |
122 | delete combinations.get_nth_elem(i); | |
123 | } | |
124 | combinations.clear(); | |
125 | } | |
126 | ||
127 | bool JsonOmitCombination::next(int current_value /* = 0 */) | |
128 | { | |
129 | if ((size_t)current_value == combinations.size()) { | |
130 | return false; | |
131 | } | |
132 | Common::Value* val = combinations.get_nth_key(current_value); | |
133 | int* omitted_fields = combinations.get_nth_elem(current_value); | |
134 | for (size_t i = 0; i < val->get_nof_comps(); ++i) { | |
135 | if (omitted_fields[i] == OMITTED_ABSENT) { | |
136 | // ABSENT (zero bit) found, set it to NULL (one bit) and reset all previous | |
137 | // NULLs (ones) to ABSENTs (zeros), first in this value ... | |
138 | omitted_fields[i] = OMITTED_NULL; | |
139 | for (size_t j = 0; j < i; ++j) { | |
140 | if (omitted_fields[j] == OMITTED_NULL) { | |
141 | omitted_fields[j] = OMITTED_ABSENT; | |
142 | } | |
143 | } | |
144 | // ... and then in all previous record values | |
145 | reset_previous(current_value); | |
146 | return true; | |
147 | } | |
148 | } | |
149 | // no new combinations in this value, check the next one | |
150 | return next(current_value + 1); | |
151 | } | |
152 | ||
153 | JsonOmitCombination::omit_state_t JsonOmitCombination::get_state( | |
154 | Common::Value* p_rec_value, size_t p_comp) const | |
155 | { | |
156 | return (JsonOmitCombination::omit_state_t)combinations[p_rec_value][p_comp]; | |
157 | } | |
158 | ||
159 | void JsonOmitCombination::parse_value(Common::Value* p_value) | |
160 | { | |
161 | switch (p_value->get_valuetype()) { | |
162 | case Common::Value::V_SEQ: | |
163 | case Common::Value::V_SET: { | |
164 | size_t len = p_value->get_nof_comps(); | |
165 | int* omitted_fields = new int[len]; // stores one combination | |
166 | for (size_t i = 0; i < len; ++i) { | |
167 | Common::Value* val = p_value->get_se_comp_byIndex(i)->get_value(); | |
168 | if (val->get_valuetype() == Common::Value::V_OMIT) { | |
169 | // all omitted fields are absent in the first combination | |
170 | omitted_fields[i] = OMITTED_ABSENT; | |
171 | } | |
172 | else { | |
173 | omitted_fields[i] = NOT_OMITTED; | |
174 | } | |
175 | parse_value(val->get_value_refd_last()); | |
176 | } | |
177 | combinations.add(p_value, omitted_fields); | |
178 | break; } | |
179 | case Common::Value::V_SEQOF: | |
180 | case Common::Value::V_SETOF: | |
181 | for (size_t i = 0; i < p_value->get_nof_comps(); ++i) { | |
182 | parse_value(p_value->get_comp_byIndex(i)->get_value_refd_last()); | |
183 | } | |
184 | break; | |
185 | case Common::Value::V_CHOICE: | |
186 | parse_value(p_value->get_alt_value()->get_value_refd_last()); | |
187 | default: | |
188 | break; | |
189 | } | |
190 | } | |
191 | ||
192 | void JsonOmitCombination::reset_previous(int value_count) | |
193 | { | |
194 | for (int i = 0; i < value_count; ++i) { | |
195 | Common::Value* val = combinations.get_nth_key(i); | |
196 | int* omitted_fields = combinations.get_nth_elem(i); | |
197 | for (size_t j = 0; j < val->get_nof_comps(); ++j) { | |
198 | if (omitted_fields[j] == OMITTED_NULL) { | |
199 | omitted_fields[j] = OMITTED_ABSENT; | |
200 | } | |
201 | } | |
202 | } | |
203 | } | |
204 | ||
970ed795 EL |
205 | } // namespace |
206 |