Commit | Line | Data |
---|---|---|
970ed795 | 1 | /////////////////////////////////////////////////////////////////////////////// |
3abe9331 | 2 | // Copyright (c) 2000-2015 Ericsson Telecom AB |
970ed795 EL |
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) | |
3abe9331 | 44 | , blockDefault(not_set) |
970ed795 EL |
45 | //, importedModules() |
46 | , variant() | |
47 | , moduleNotIntoFile(false) | |
3abe9331 | 48 | , moduleNotIntoNameConversion(false) { |
970ed795 EL |
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; | |
3abe9331 | 55 | ssize_t needed = cygwin_conv_path(CCP_WIN_A_TO_POSIX | CCP_RELATIVE, a_filename, NULL, 0); |
970ed795 | 56 | if (needed >= 0) { |
3abe9331 | 57 | posix = (char*) Malloc(needed); |
58 | if (cygwin_conv_path(CCP_WIN_A_TO_POSIX | CCP_RELATIVE, a_filename, posix, needed)) { | |
970ed795 EL |
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 | ||
3abe9331 | 76 | TTCN3Module::~TTCN3Module() { |
970ed795 | 77 | for (List<RootType*>::iterator type = definedTypes.begin(); type; type = type->Next) { |
3abe9331 | 78 | delete type->Data; |
970ed795 EL |
79 | } |
80 | } | |
81 | ||
82 | void TTCN3Module::loadValuesFromXMLDeclaration(const char *a_version, | |
3abe9331 | 83 | const char *a_encoding, int a_standalone) { |
970ed795 EL |
84 | xsdVersion = a_version; |
85 | xsdEncoding = a_encoding; | |
86 | xsdStandalone = a_standalone; | |
87 | } | |
88 | ||
89 | void TTCN3Module::loadValuesFromSchemaTag(const Mstring& a_targetNamespace, | |
90 | List<NamespaceType> a_declaredNamespaces, | |
3abe9331 | 91 | FormValue a_elementFormDefault, FormValue a_attributeFormDefault, |
92 | BlockValue a_blockDefault) { | |
970ed795 EL |
93 | if (a_targetNamespace.empty()) { |
94 | targetNamespace = "NoTargetNamespace"; | |
3abe9331 | 95 | } else { |
970ed795 EL |
96 | if (a_targetNamespace == "http://www.w3.org/2001/XMLSchema") { |
97 | notIntoFile(); | |
98 | } | |
99 | targetNamespace = a_targetNamespace; | |
100 | } | |
101 | ||
102 | elementFormDefault = a_elementFormDefault; | |
103 | attributeFormDefault = a_attributeFormDefault; | |
3abe9331 | 104 | blockDefault = a_blockDefault; |
970ed795 EL |
105 | |
106 | declaredNamespaces = a_declaredNamespaces; | |
107 | ||
3abe9331 | 108 | for (List<NamespaceType>::iterator ns = declaredNamespaces.begin(); ns; ns = ns->Next) { |
970ed795 EL |
109 | if (ns->Data.uri == targetNamespace) { |
110 | targetNamespace_connectedPrefix = ns->Data.prefix; | |
111 | break; | |
112 | } | |
113 | } | |
114 | } | |
115 | ||
3abe9331 | 116 | void TTCN3Module::addMainType(const ConstructType typeOfMainType) { |
117 | switch (typeOfMainType) { | |
118 | case c_simpleType: | |
119 | { | |
120 | SimpleType * new_ST = new SimpleType(parser, this, c_simpleType); | |
121 | definedTypes.push_back(new_ST); | |
122 | new_ST->loadWithValues(); | |
123 | break; | |
124 | } | |
125 | case c_element: | |
126 | { | |
127 | SimpleType * new_ST = new SimpleType(parser, this, c_element); | |
128 | definedTypes.push_back(new_ST); | |
129 | new_ST->loadWithValues(); | |
130 | break; | |
131 | } | |
132 | case c_attribute: | |
133 | { | |
134 | SimpleType * new_ST = new SimpleType(parser, this, c_attribute); | |
135 | definedTypes.push_back(new_ST); | |
136 | new_ST->loadWithValues(); | |
137 | break; | |
138 | } | |
139 | case c_complexType: | |
140 | { | |
141 | ComplexType * new_CT = new ComplexType(parser, this, c_complexType); | |
142 | definedTypes.push_back(new_CT); | |
143 | new_CT->loadWithValues(); | |
144 | break; | |
145 | } | |
146 | case c_group: | |
147 | { | |
148 | ComplexType * new_CT = new ComplexType(parser, this, c_group); | |
149 | definedTypes.push_back(new_CT); | |
150 | new_CT->loadWithValues(); | |
151 | break; | |
152 | } | |
153 | case c_attributeGroup: | |
154 | { | |
155 | ComplexType * new_CT = new ComplexType(parser, this, c_attributeGroup); | |
156 | definedTypes.push_back(new_CT); | |
157 | new_CT->loadWithValues(); | |
158 | break; | |
159 | } | |
160 | case c_include: | |
161 | { | |
162 | ImportStatement * new_INCL = new ImportStatement(parser, this, c_include); | |
163 | definedTypes.push_back(new_INCL); | |
164 | new_INCL->loadWithValues(); | |
165 | break; | |
166 | } | |
167 | case c_import: | |
168 | { | |
169 | ImportStatement * new_IMP = new ImportStatement(parser, this, c_import); | |
170 | definedTypes.push_back(new_IMP); | |
171 | new_IMP->loadWithValues(); | |
172 | break; | |
173 | } | |
174 | case c_annotation: | |
175 | { | |
176 | Annotation * new_ANN = new Annotation(parser, this, c_annotation); | |
177 | definedTypes.push_back(new_ANN); | |
178 | new_ANN->loadWithValues(); | |
179 | break; | |
180 | } | |
181 | case c_idattrib: | |
182 | { | |
183 | Mstring type = empty_string; | |
184 | if (hasDefinedMainType()) { | |
185 | type = getLastMainType().getName().convertedValue; | |
186 | } | |
187 | printWarning(getSchemaname(), type, | |
188 | Mstring("The mapping of ID attribute is not supported.")); | |
189 | TTCN3ModuleInventory::getInstance().incrNumWarnings(); | |
190 | break; | |
191 | } | |
192 | case c_unknown: | |
193 | case c_schema: | |
194 | break; | |
970ed795 EL |
195 | } |
196 | ||
197 | actualXsdConstruct = typeOfMainType; | |
198 | } | |
199 | ||
3abe9331 | 200 | void TTCN3Module::replaceLastMainType(RootType * t) { |
970ed795 EL |
201 | delete(definedTypes.back()); |
202 | definedTypes.pop_back(); | |
203 | definedTypes.push_back(t); | |
204 | actualXsdConstruct = t->getConstruct(); | |
205 | } | |
206 | ||
3abe9331 | 207 | void TTCN3Module::generate_TTCN3_header(FILE * file) { |
970ed795 EL |
208 | time_t time_current = time(NULL); |
209 | fprintf(file, | |
210 | "/*******************************************************************************\n" | |
3abe9331 | 211 | ); |
970ed795 EL |
212 | if (t_flag_used) { |
213 | fprintf(file, | |
214 | "* Copyright Ericsson Telecom AB\n" | |
215 | "*\n" | |
216 | "* XSD to TTCN-3 Translator\n" | |
217 | "*\n" | |
3abe9331 | 218 | ); |
219 | } else { | |
970ed795 | 220 | fprintf(file, |
3abe9331 | 221 | "* Copyright (c) 2000-%-4d Ericsson Telecom AB\n" |
970ed795 EL |
222 | "*\n" |
223 | "* XSD to TTCN-3 Translator version: %-40s\n" | |
224 | "*\n", | |
225 | 1900 + (localtime(&time_current))->tm_year, | |
226 | PRODUCT_NUMBER | |
3abe9331 | 227 | ); |
970ed795 EL |
228 | } |
229 | fprintf(file, | |
230 | "* All rights reserved. This program and the accompanying materials\n" | |
231 | "* are made available under the terms of the Eclipse Public License v1.0\n" | |
232 | "* which accompanies this distribution, and is available at\n" | |
233 | "* http://www.eclipse.org/legal/epl-v10.html\n" | |
234 | "*******************************************************************************/\n" | |
235 | "//\n" | |
236 | "// File: %s.ttcn\n" | |
237 | "// Description:\n" | |
238 | "// References:\n" | |
239 | "// Rev:\n" | |
240 | "// Prodnr:\n", | |
241 | modulename.c_str() | |
3abe9331 | 242 | ); |
970ed795 EL |
243 | if (t_flag_used) { |
244 | fprintf(file, | |
245 | "// Updated:\n" | |
3abe9331 | 246 | ); |
247 | } else { | |
970ed795 EL |
248 | fprintf(file, |
249 | "// Updated: %s", | |
250 | ctime(&time_current) | |
3abe9331 | 251 | ); |
970ed795 EL |
252 | } |
253 | fprintf(file, | |
254 | "// Contact: http://ttcn.ericsson.se\n" | |
255 | "//\n" | |
256 | "////////////////////////////////////////////////////////////////////////////////\n" | |
3abe9331 | 257 | ); |
970ed795 EL |
258 | } |
259 | ||
3abe9331 | 260 | void TTCN3Module::generate_TTCN3_fileinfo(FILE * file) { |
970ed795 EL |
261 | fprintf(file, |
262 | "//\t- %s\n" | |
263 | "//\t\t\t/* xml ", | |
264 | schemaname.c_str() | |
3abe9331 | 265 | ); |
970ed795 EL |
266 | |
267 | if (!xsdVersion.empty()) { | |
268 | fprintf(file, "version = \"%s\" ", xsdVersion.c_str()); | |
269 | } | |
270 | if (!xsdEncoding.empty()) { | |
271 | fprintf(file, "encoding = \"%s\" ", xsdEncoding.c_str()); | |
272 | } | |
273 | ||
3abe9331 | 274 | switch (xsdStandalone) { |
275 | case 0: | |
276 | fprintf(file, "standalone = \"no\" "); | |
277 | break; | |
278 | case 1: | |
279 | fprintf(file, "standalone = \"yes\" "); | |
280 | break; | |
281 | default: | |
282 | break; | |
970ed795 EL |
283 | } |
284 | ||
285 | fprintf(file, | |
286 | "*/\n" | |
287 | "//\t\t\t/* targetnamespace = \"%s\" */\n", | |
288 | targetNamespace.c_str() | |
3abe9331 | 289 | ); |
970ed795 EL |
290 | } |
291 | ||
3abe9331 | 292 | void TTCN3Module::generate_TTCN3_modulestart(FILE * file) { |
970ed795 EL |
293 | fprintf(file, |
294 | "module %s {\n" | |
295 | "\n" | |
296 | "\n" | |
297 | "import from XSD all;\n" | |
298 | "\n" | |
299 | "\n", | |
300 | modulename.c_str() | |
3abe9331 | 301 | ); |
970ed795 EL |
302 | } |
303 | ||
3abe9331 | 304 | void TTCN3Module::generate_TTCN3_import_statements(FILE * file) { |
305 | for (List<RootType*>::iterator type = definedTypes.begin(); type; type = type->Next) { | |
306 | if (type->Data->getConstruct() == c_import) { | |
970ed795 EL |
307 | type->Data->printToFile(file); |
308 | } | |
309 | } | |
310 | } | |
311 | ||
3abe9331 | 312 | void TTCN3Module::generate_TTCN3_included_types(FILE * file) { |
313 | for (List<RootType*>::iterator type = definedTypes.begin(); type; type = type->Next) { | |
314 | if (type->Data->getConstruct() == c_include) { | |
970ed795 EL |
315 | type->Data->printToFile(file); |
316 | } | |
317 | } | |
318 | } | |
319 | ||
3abe9331 | 320 | void TTCN3Module::generate_TTCN3_types(FILE * file) { |
321 | for (List<RootType*>::iterator type = definedTypes.begin(); type; type = type->Next) { | |
322 | if (type->Data->getConstruct() != c_include && type->Data->getConstruct() != c_import) { | |
970ed795 EL |
323 | type->Data->printToFile(file); |
324 | } | |
325 | } | |
326 | } | |
327 | ||
3abe9331 | 328 | void TTCN3Module::generate_with_statement(FILE * file, List<NamespaceType> used_namespaces) { |
329 | if (e_flag_used) { | |
330 | return; | |
331 | } | |
970ed795 EL |
332 | |
333 | fprintf(file, | |
334 | "with {\n" | |
335 | "encode \"XML\";\n" | |
3abe9331 | 336 | ); |
970ed795 EL |
337 | |
338 | bool xsi = false; | |
339 | ||
3abe9331 | 340 | for (List<NamespaceType>::iterator usedNS = used_namespaces.begin(); usedNS; usedNS = usedNS->Next) { |
970ed795 EL |
341 | if (usedNS->Data.uri == "http://www.w3.org/2001/XMLSchema") { |
342 | xsi = true; | |
343 | continue; | |
344 | } | |
345 | if (usedNS->Data.uri == "NoTargetNamespace") { | |
346 | continue; | |
347 | } | |
3abe9331 | 348 | // XXX this inner loop is either redundant now, or it should be elsewhere. |
349 | // It is quite dodgy to modify(!) namespaces when we are already generating code. | |
350 | for (List<NamespaceType>::iterator usedNS2 = usedNS->Next; usedNS2; usedNS2 = usedNS2->Next) { | |
970ed795 EL |
351 | if (usedNS->Data.uri == usedNS2->Data.uri) { |
352 | if (usedNS2->Data.prefix.empty()) | |
353 | usedNS2->Data.prefix = usedNS->Data.prefix; | |
354 | break; | |
355 | } | |
356 | } | |
357 | } | |
358 | ||
359 | if (targetNamespace != "NoTargetNamespace") { | |
360 | fprintf(file, "variant \"namespace as \'%s\'", targetNamespace.c_str()); | |
3abe9331 | 361 | if (!targetNamespace_connectedPrefix.empty()) { |
970ed795 | 362 | fprintf(file, " prefix \'%s\'", targetNamespace_connectedPrefix.c_str()); |
3abe9331 | 363 | } |
970ed795 EL |
364 | fprintf(file, "\";\n"); |
365 | } | |
366 | ||
367 | ||
368 | if (xsi) { | |
369 | fprintf(file, | |
370 | "variant \"controlNamespace \'http://www.w3.org/2001/XMLSchema-instance\' prefix \'xsi\'\";\n"); | |
371 | } | |
372 | if (attributeFormDefault == qualified) { | |
373 | fprintf(file, | |
374 | "variant \"attributeFormQualified\";\n"); | |
375 | } | |
376 | if (elementFormDefault == qualified) { | |
377 | fprintf(file, | |
378 | "variant \"elementFormQualified\";\n"); | |
379 | } | |
380 | fprintf(file, | |
381 | "}\n"); | |
382 | } | |
383 | ||
3abe9331 | 384 | void TTCN3Module::TargetNamespace2ModuleName() { |
970ed795 EL |
385 | Mstring res(targetNamespace); |
386 | ||
387 | if (z_flag_used) { | |
3abe9331 | 388 | char * found; |
389 | found = res.foundAt("http://"); | |
390 | //check if the http:// is at the beginning of the namespace | |
391 | if (found == res.c_str()) { //res.c_str() returns a pointer to the first char | |
392 | for (int i = 0; i != 7; ++i) { | |
970ed795 | 393 | res.eraseChar(0); |
3abe9331 | 394 | } |
395 | } | |
396 | found = res.foundAt("urn:"); | |
397 | //check if the urn: is at the beginning of the namespace | |
398 | if (found == res.c_str()) { //res.c_str() returns a pointer to the first char | |
399 | for (int i = 0; i != 4; ++i) { | |
970ed795 | 400 | res.eraseChar(0); |
3abe9331 | 401 | } |
402 | } | |
970ed795 EL |
403 | } |
404 | ||
405 | // the characters ' '(SPACE), '.'(FULL STOP) and '-'(HYPEN-MINUS) | |
406 | // and '/', '#', ':' shall all be replaced by a "_" (LOW LINE) | |
407 | for (size_t i = 0; i != res.size(); ++i) { | |
408 | if ((res[i] == ' ') || | |
409 | (res[i] == '.') || | |
410 | (res[i] == '-') || | |
411 | (res[i] == '/') || | |
412 | (res[i] == '#') || | |
3abe9331 | 413 | (res[i] == ':')) { |
970ed795 EL |
414 | res[i] = '_'; |
415 | } | |
416 | } | |
417 | // any character except "A" to "Z", "a" to "z" or "0" to "9" and "_" shall be removed | |
418 | for (size_t i = 0; i != res.size(); ++i) { | |
3abe9331 | 419 | if (!isalpha((const unsigned char) res[i]) && !isdigit((const unsigned char) res[i]) && (res[i] != '_')) { |
970ed795 | 420 | res.eraseChar(i); |
3abe9331 | 421 | i--; |
970ed795 EL |
422 | } |
423 | } | |
424 | // a sequence of two of more "_" (LOW LINE) shall be replaced with a single "_" (LOW LINE) | |
425 | for (size_t i = 1; i < res.size(); ++i) { | |
3abe9331 | 426 | if (res[i] == '_' && res[i - 1] == '_') { |
427 | res.eraseChar(i); | |
428 | i--; | |
429 | } | |
430 | } | |
431 | ||
432 | if (!res.empty()) { | |
433 | // "_" (LOW LINE) characters occurring at the beginning of the name shall be removed | |
434 | if (res[0] == '_') { | |
435 | res.eraseChar(0); | |
436 | } | |
437 | } | |
438 | if (!res.empty()) { | |
439 | // "_" (LOW LINE) characters occurring at the end of the name shall be removed | |
440 | if (res[res.size() - 1] == '_') { | |
441 | res.eraseChar(res.size() - 1); | |
970ed795 EL |
442 | } |
443 | } | |
970ed795 EL |
444 | |
445 | if (res.empty()) { | |
446 | res = "x"; | |
3abe9331 | 447 | } else if (isdigit((const unsigned char) res[0])) { |
970ed795 EL |
448 | res.insertChar(0, 'x'); |
449 | } | |
450 | ||
3abe9331 | 451 | //Postfix with _i if the targetnamespace is different |
452 | bool postfixing = false; | |
453 | for (List<TTCN3Module*>::iterator mod = TTCN3ModuleInventory::getInstance().getModules().begin(); mod; mod = mod->Next) { | |
454 | if (mod->Data != this && mod->Data->getModulename() == res && mod->Data->getTargetNamespace() != targetNamespace) { | |
455 | postfixing = true; | |
456 | break; | |
457 | } | |
458 | } | |
459 | ||
460 | if(postfixing){ | |
461 | bool found; | |
462 | int counter = 1; | |
463 | expstring_t tmpname = NULL; | |
464 | do { | |
465 | found = false; | |
466 | Free(tmpname); | |
467 | tmpname = mprintf("%s_%d", res.c_str(), counter); | |
468 | for(List<TTCN3Module*>::iterator mod = TTCN3ModuleInventory::getInstance().getModules().begin(); mod; mod = mod->Next){ | |
469 | if(mod->Data != this && mod->Data->getModulename() == Mstring(tmpname)){ | |
470 | found = true; | |
471 | break; | |
472 | } | |
473 | } | |
474 | counter++; | |
475 | } while (found); | |
476 | res = Mstring(tmpname); | |
477 | Free(tmpname); | |
478 | } | |
479 | ||
970ed795 EL |
480 | modulename = res; |
481 | } | |
482 | ||
3abe9331 | 483 | void TTCN3Module::dump() const { |
970ed795 | 484 | fprintf(stderr, "Module '%s' at %p (from %s)\n", |
3abe9331 | 485 | modulename.c_str(), (const void*) this, schemaname.c_str()); |
970ed795 | 486 | |
3abe9331 | 487 | for (List<RootType*>::iterator type = definedTypes.begin(); type; type = type->Next) { |
970ed795 EL |
488 | type->Data->dump(1); |
489 | } | |
490 | } |