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