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