Commit | Line | Data |
---|---|---|
970ed795 EL |
1 | /////////////////////////////////////////////////////////////////////////////// |
2 | // Copyright (c) 2000-2014 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 | #include "TTCN3Module.hh" | |
9 | ||
10 | #include "RootType.hh" | |
11 | #include "SimpleType.hh" | |
12 | #include "ComplexType.hh" | |
13 | #include "ImportStatement.hh" | |
14 | #include "Annotation.hh" | |
15 | ||
16 | #include "../common/version_internal.h" | |
17 | ||
18 | #include <ctime> | |
19 | ||
20 | #if defined(WIN32) && !defined(MINGW) | |
21 | #include <cygwin/version.h> | |
22 | #include <sys/cygwin.h> | |
23 | #include <limits.h> | |
24 | #endif | |
25 | ||
26 | extern bool e_flag_used; | |
27 | extern bool t_flag_used; | |
28 | extern bool z_flag_used; | |
29 | ||
30 | TTCN3Module::TTCN3Module(const char * a_filename, XMLParser * a_parser) | |
31 | : parser(a_parser) | |
32 | , schemaname() | |
33 | , modulename() | |
34 | , definedTypes() | |
35 | , actualXsdConstruct(c_unknown) | |
36 | , xsdVersion() | |
37 | , xsdEncoding() | |
38 | , xsdStandalone() | |
39 | , targetNamespace() | |
40 | , targetNamespace_connectedPrefix() | |
41 | , declaredNamespaces() | |
42 | , elementFormDefault(notset) | |
43 | , attributeFormDefault(notset) | |
44 | //, importedModules() | |
45 | , variant() | |
46 | , moduleNotIntoFile(false) | |
47 | , moduleNotIntoNameConversion(false) | |
48 | { | |
49 | #if defined(WIN32) && !defined(MINGW) | |
50 | // Transform a Windows style path: "C:\cygwin\tmp\a.xsd" | |
51 | // into a POSIX style path: "/home/a/xsd", so getValueWithoutPrefix('/') | |
52 | // can chop off the directory path. | |
53 | #if CYGWIN_VERSION_DLL_MAJOR >= 1007 | |
54 | char *posix = NULL; | |
55 | ssize_t needed = cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_RELATIVE, a_filename, NULL, 0); | |
56 | if (needed >= 0) { | |
57 | posix = (char*)Malloc(needed); | |
58 | if (cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_RELATIVE, a_filename, posix, needed)) { | |
59 | posix = NULL; // conversion failed | |
60 | } | |
61 | } | |
62 | Mstring filename(posix ? posix : a_filename); | |
63 | Free(posix); // even if NULL | |
64 | #else // Cygwin 1.5 | |
65 | char posix[PATH_MAX]; | |
66 | int fail = cygwin_conv_to_posix_path(a_filename, posix); | |
67 | Mstring filename(fail ? a_filename : posix); | |
68 | #endif | |
69 | ||
70 | #else | |
71 | Mstring filename(a_filename); | |
72 | #endif | |
73 | schemaname = filename.getValueWithoutPrefix('/'); // excludes the path of the input file | |
74 | } | |
75 | ||
76 | TTCN3Module::~TTCN3Module() | |
77 | { | |
78 | for (List<RootType*>::iterator type = definedTypes.begin(); type; type = type->Next) { | |
79 | delete(type->Data); | |
80 | } | |
81 | } | |
82 | ||
83 | void TTCN3Module::loadValuesFromXMLDeclaration(const char *a_version, | |
84 | const char *a_encoding, int a_standalone) | |
85 | { | |
86 | xsdVersion = a_version; | |
87 | xsdEncoding = a_encoding; | |
88 | xsdStandalone = a_standalone; | |
89 | } | |
90 | ||
91 | void TTCN3Module::loadValuesFromSchemaTag(const Mstring& a_targetNamespace, | |
92 | List<NamespaceType> a_declaredNamespaces, | |
93 | FormValue a_elementFormDefault, FormValue a_attributeFormDefault) | |
94 | { | |
95 | if (a_targetNamespace.empty()) { | |
96 | targetNamespace = "NoTargetNamespace"; | |
97 | } | |
98 | else { | |
99 | if (a_targetNamespace == "http://www.w3.org/2001/XMLSchema") { | |
100 | notIntoFile(); | |
101 | } | |
102 | targetNamespace = a_targetNamespace; | |
103 | } | |
104 | ||
105 | elementFormDefault = a_elementFormDefault; | |
106 | attributeFormDefault = a_attributeFormDefault; | |
107 | ||
108 | declaredNamespaces = a_declaredNamespaces; | |
109 | ||
110 | for (List<NamespaceType>::iterator ns = declaredNamespaces.begin(); ns; ns = ns->Next) | |
111 | { | |
112 | if (ns->Data.uri == targetNamespace) { | |
113 | targetNamespace_connectedPrefix = ns->Data.prefix; | |
114 | break; | |
115 | } | |
116 | } | |
117 | } | |
118 | ||
119 | void TTCN3Module::addMainType(ConstructType typeOfMainType) | |
120 | { | |
121 | switch (typeOfMainType) | |
122 | { | |
123 | case c_simpleType: { | |
124 | SimpleType * new_ST = new SimpleType(parser, this, c_simpleType); | |
125 | definedTypes.push_back(new_ST); | |
126 | new_ST->loadWithValues(); | |
127 | break; } | |
128 | case c_element: { | |
129 | SimpleType * new_ST = new SimpleType(parser, this, c_element); | |
130 | definedTypes.push_back(new_ST); | |
131 | new_ST->loadWithValues(); | |
132 | break; } | |
133 | case c_attribute: { | |
134 | SimpleType * new_ST = new SimpleType(parser, this, c_attribute); | |
135 | definedTypes.push_back(new_ST); | |
136 | new_ST->loadWithValues(); | |
137 | break; } | |
138 | case c_complexType: { | |
139 | ComplexType * new_CT = new ComplexType(parser, this, c_complexType); | |
140 | definedTypes.push_back(new_CT); | |
141 | new_CT->loadWithValues(); | |
142 | break; } | |
143 | case c_group: { | |
144 | ComplexType * new_CT = new ComplexType(parser, this, c_group); | |
145 | definedTypes.push_back(new_CT); | |
146 | new_CT->loadWithValues(); | |
147 | break; } | |
148 | case c_attributeGroup: { | |
149 | ComplexType * new_CT = new ComplexType(parser, this, c_attributeGroup); | |
150 | definedTypes.push_back(new_CT); | |
151 | new_CT->loadWithValues(); | |
152 | break; } | |
153 | case c_include: { | |
154 | ImportStatement * new_INCL = new ImportStatement(parser, this, c_include); | |
155 | definedTypes.push_back(new_INCL); | |
156 | new_INCL->loadWithValues(); | |
157 | break; } | |
158 | case c_import: { | |
159 | ImportStatement * new_IMP = new ImportStatement(parser, this, c_import); | |
160 | definedTypes.push_back(new_IMP); | |
161 | new_IMP->loadWithValues(); | |
162 | break; } | |
163 | case c_annotation: { | |
164 | Annotation * new_ANN = new Annotation(parser, this, c_annotation); | |
165 | definedTypes.push_back(new_ANN); | |
166 | new_ANN->loadWithValues(); | |
167 | break; } | |
168 | case c_unknown: | |
169 | case c_schema: | |
170 | break; | |
171 | } | |
172 | ||
173 | actualXsdConstruct = typeOfMainType; | |
174 | } | |
175 | ||
176 | void TTCN3Module::replaceLastMainType(RootType * t) | |
177 | { | |
178 | delete(definedTypes.back()); | |
179 | definedTypes.pop_back(); | |
180 | definedTypes.push_back(t); | |
181 | actualXsdConstruct = t->getConstruct(); | |
182 | } | |
183 | ||
184 | void TTCN3Module::generate_TTCN3_header(FILE * file) | |
185 | { | |
186 | time_t time_current = time(NULL); | |
187 | fprintf(file, | |
188 | "/*******************************************************************************\n" | |
189 | ); | |
190 | if (t_flag_used) { | |
191 | fprintf(file, | |
192 | "* Copyright Ericsson Telecom AB\n" | |
193 | "*\n" | |
194 | "* XSD to TTCN-3 Translator\n" | |
195 | "*\n" | |
196 | ); | |
197 | } | |
198 | else { | |
199 | fprintf(file, | |
200 | "* Copyright Ericsson Telecom AB %-4d\n" | |
201 | "*\n" | |
202 | "* XSD to TTCN-3 Translator version: %-40s\n" | |
203 | "*\n", | |
204 | 1900 + (localtime(&time_current))->tm_year, | |
205 | PRODUCT_NUMBER | |
206 | ); | |
207 | } | |
208 | fprintf(file, | |
209 | "* All rights reserved. This program and the accompanying materials\n" | |
210 | "* are made available under the terms of the Eclipse Public License v1.0\n" | |
211 | "* which accompanies this distribution, and is available at\n" | |
212 | "* http://www.eclipse.org/legal/epl-v10.html\n" | |
213 | "*******************************************************************************/\n" | |
214 | "//\n" | |
215 | "// File: %s.ttcn\n" | |
216 | "// Description:\n" | |
217 | "// References:\n" | |
218 | "// Rev:\n" | |
219 | "// Prodnr:\n", | |
220 | modulename.c_str() | |
221 | ); | |
222 | if (t_flag_used) { | |
223 | fprintf(file, | |
224 | "// Updated:\n" | |
225 | ); | |
226 | } | |
227 | else { | |
228 | fprintf(file, | |
229 | "// Updated: %s", | |
230 | ctime(&time_current) | |
231 | ); | |
232 | } | |
233 | fprintf(file, | |
234 | "// Contact: http://ttcn.ericsson.se\n" | |
235 | "//\n" | |
236 | "////////////////////////////////////////////////////////////////////////////////\n" | |
237 | ); | |
238 | } | |
239 | ||
240 | void TTCN3Module::generate_TTCN3_fileinfo(FILE * file) | |
241 | { | |
242 | fprintf(file, | |
243 | "//\t- %s\n" | |
244 | "//\t\t\t/* xml ", | |
245 | schemaname.c_str() | |
246 | ); | |
247 | ||
248 | if (!xsdVersion.empty()) { | |
249 | fprintf(file, "version = \"%s\" ", xsdVersion.c_str()); | |
250 | } | |
251 | if (!xsdEncoding.empty()) { | |
252 | fprintf(file, "encoding = \"%s\" ", xsdEncoding.c_str()); | |
253 | } | |
254 | ||
255 | switch (xsdStandalone) | |
256 | { | |
257 | case 0: | |
258 | fprintf(file, "standalone = \"no\" "); | |
259 | break; | |
260 | case 1: | |
261 | fprintf(file, "standalone = \"yes\" "); | |
262 | break; | |
263 | default: | |
264 | break; | |
265 | } | |
266 | ||
267 | fprintf(file, | |
268 | "*/\n" | |
269 | "//\t\t\t/* targetnamespace = \"%s\" */\n", | |
270 | targetNamespace.c_str() | |
271 | ); | |
272 | } | |
273 | ||
274 | void TTCN3Module::generate_TTCN3_modulestart(FILE * file) | |
275 | { | |
276 | fprintf(file, | |
277 | "module %s {\n" | |
278 | "\n" | |
279 | "\n" | |
280 | "import from XSD all;\n" | |
281 | "\n" | |
282 | "\n", | |
283 | modulename.c_str() | |
284 | ); | |
285 | } | |
286 | ||
287 | void TTCN3Module::generate_TTCN3_import_statements(FILE * file) | |
288 | { | |
289 | for (List<RootType*>::iterator type = definedTypes.begin(); type; type = type->Next) | |
290 | { | |
291 | if (type->Data->getConstruct() == c_import) | |
292 | { | |
293 | type->Data->printToFile(file); | |
294 | } | |
295 | } | |
296 | } | |
297 | ||
298 | void TTCN3Module::generate_TTCN3_included_types(FILE * file) | |
299 | { | |
300 | for (List<RootType*>::iterator type = definedTypes.begin(); type; type = type->Next) | |
301 | { | |
302 | if (type->Data->getConstruct() == c_include) | |
303 | { | |
304 | type->Data->printToFile(file); | |
305 | } | |
306 | } | |
307 | } | |
308 | ||
309 | void TTCN3Module::generate_TTCN3_types(FILE * file) | |
310 | { | |
311 | for (List<RootType*>::iterator type = definedTypes.begin(); type; type = type->Next) | |
312 | { | |
313 | if (type->Data->getConstruct() != c_include && type->Data->getConstruct() != c_import) | |
314 | { | |
315 | type->Data->printToFile(file); | |
316 | } | |
317 | } | |
318 | } | |
319 | ||
320 | void TTCN3Module::generate_with_statement(FILE * file, List<NamespaceType> used_namespaces) | |
321 | { | |
322 | if (e_flag_used) return; | |
323 | ||
324 | fprintf(file, | |
325 | "with {\n" | |
326 | "encode \"XML\";\n" | |
327 | ); | |
328 | ||
329 | bool xsi = false; | |
330 | ||
331 | for (List<NamespaceType>::iterator usedNS = used_namespaces.begin(); usedNS; usedNS = usedNS->Next) | |
332 | { | |
333 | if (usedNS->Data.uri == "http://www.w3.org/2001/XMLSchema") { | |
334 | xsi = true; | |
335 | continue; | |
336 | } | |
337 | if (usedNS->Data.uri == "NoTargetNamespace") { | |
338 | continue; | |
339 | } | |
340 | // XXX this inner loop is either redundant now, or it should be elsewhere. | |
341 | // It is quite dodgy to modify(!) namespaces when we are already generating code. | |
342 | for (List<NamespaceType>::iterator usedNS2 = usedNS->Next; usedNS2; usedNS2 = usedNS2->Next) | |
343 | { | |
344 | if (usedNS->Data.uri == usedNS2->Data.uri) { | |
345 | if (usedNS2->Data.prefix.empty()) | |
346 | usedNS2->Data.prefix = usedNS->Data.prefix; | |
347 | break; | |
348 | } | |
349 | } | |
350 | } | |
351 | ||
352 | if (targetNamespace != "NoTargetNamespace") { | |
353 | fprintf(file, "variant \"namespace as \'%s\'", targetNamespace.c_str()); | |
354 | if (!targetNamespace_connectedPrefix.empty()) | |
355 | fprintf(file, " prefix \'%s\'", targetNamespace_connectedPrefix.c_str()); | |
356 | fprintf(file, "\";\n"); | |
357 | } | |
358 | ||
359 | ||
360 | if (xsi) { | |
361 | fprintf(file, | |
362 | "variant \"controlNamespace \'http://www.w3.org/2001/XMLSchema-instance\' prefix \'xsi\'\";\n"); | |
363 | } | |
364 | if (attributeFormDefault == qualified) { | |
365 | fprintf(file, | |
366 | "variant \"attributeFormQualified\";\n"); | |
367 | } | |
368 | if (elementFormDefault == qualified) { | |
369 | fprintf(file, | |
370 | "variant \"elementFormQualified\";\n"); | |
371 | } | |
372 | fprintf(file, | |
373 | "}\n"); | |
374 | } | |
375 | ||
376 | void TTCN3Module::TargetNamespace2ModuleName() | |
377 | { | |
378 | Mstring res(targetNamespace); | |
379 | ||
380 | if (z_flag_used) { | |
381 | if (res.isFound("http://")) | |
382 | for (int i = 0; i != 7; ++i) | |
383 | res.eraseChar(0); | |
384 | if (res.isFound("urn:")) | |
385 | for (int i = 0; i != 4; ++i) | |
386 | res.eraseChar(0); | |
387 | } | |
388 | ||
389 | // the characters ' '(SPACE), '.'(FULL STOP) and '-'(HYPEN-MINUS) | |
390 | // and '/', '#', ':' shall all be replaced by a "_" (LOW LINE) | |
391 | for (size_t i = 0; i != res.size(); ++i) { | |
392 | if ((res[i] == ' ') || | |
393 | (res[i] == '.') || | |
394 | (res[i] == '-') || | |
395 | (res[i] == '/') || | |
396 | (res[i] == '#') || | |
397 | (res[i] == ':')) | |
398 | { | |
399 | res[i] = '_'; | |
400 | } | |
401 | } | |
402 | // any character except "A" to "Z", "a" to "z" or "0" to "9" and "_" shall be removed | |
403 | for (size_t i = 0; i != res.size(); ++i) { | |
404 | if (!isalpha((const unsigned char)res[i]) && !isdigit((const unsigned char)res[i]) && (res[i] != '_')) { | |
405 | res.eraseChar(i); | |
406 | } | |
407 | } | |
408 | // a sequence of two of more "_" (LOW LINE) shall be replaced with a single "_" (LOW LINE) | |
409 | for (size_t i = 1; i < res.size(); ++i) { | |
410 | while (res[i] == '_' && res[i-1] == '_') { | |
411 | res.eraseChar(i--); | |
412 | } | |
413 | } | |
414 | // "_" (LOW LINE) characters occurring at the beginning of the name shall be removed | |
415 | if (res[0] == '_') res.eraseChar(0); | |
416 | // "_" (LOW LINE) characters occurring at the end of the name shall be removed | |
417 | if (res[res.size()-1] == '_') res.eraseChar(res.size()-1); | |
418 | ||
419 | if (res.empty()) { | |
420 | res = "x"; | |
421 | } | |
422 | else if (isdigit((const unsigned char)res[0])) { | |
423 | res.insertChar(0, 'x'); | |
424 | } | |
425 | ||
426 | modulename = res; | |
427 | } | |
428 | ||
429 | void TTCN3Module::dump() const | |
430 | { | |
431 | fprintf(stderr, "Module '%s' at %p (from %s)\n", | |
432 | modulename.c_str(), (const void*)this, schemaname.c_str()); | |
433 | ||
434 | for (List<RootType*>::iterator type = definedTypes.begin(); type; type = type->Next) | |
435 | { | |
436 | type->Data->dump(1); | |
437 | } | |
438 | } |