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