1 ///////////////////////////////////////////////////////////////////////////////
2 // Copyright (c) 2000-2015 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 "GeneralFunctions.hh"
9 #include "SimpleType.hh"
10 #include "TTCN3Module.hh"
12 #include <cctype> // for using "toupper" function
18 extern bool w_flag_used
;
20 // XSDName2TTCN3Name function:
22 // in - input string - XSD name
23 // used_names - set of previously defined types, used field names etc.
24 // type_of_the_name - mode of the function behaviour
25 // res - generated result
26 // variant - generated variant string for TTCN-3
29 void XSDName2TTCN3Name(const Mstring
& in
, QualifiedNames
& used_names
, modeType type_of_the_name
,
30 Mstring
& res
, Mstring
& variant
, bool no_replace
) {
31 static const char* TTCN3_reserved_words
[] = {
32 "action", "activate", "address", "alive", "all", "alt", "altstep", "and", "and4b", "any", "anytype", "apply",
33 "bitstring", "boolean", "break",
34 "call", "case", "catch", "char", "charstring", "check", "clear", "complement", "component", "connect",
35 "const", "continue", "control", "create",
36 "deactivate", "default", "derefers", "disconnect", "display", "do", "done",
37 "else", "encode", "enumerated", "error", "except", "exception", "execute", "extends", "extension",
39 "fail", "false", "float", "for", "friend", "from", "function",
40 "getverdict", "getcall", "getreply", "goto", "group",
42 "if", "if present", "import", "in", "inconc", "infinity", "inout", "integer", "interleave",
44 "label", "language", "length", "log",
45 "map", "match", "message", "mixed", "mod", "modifies", "module", "modulepar", "mtc",
46 "noblock", "none", "not", "not4b", "nowait", "null",
47 "objid", "octetstring", "of", "omit", "on", "optional", "or", "or4b", "out", "override",
48 "param", "pass", "pattern", "permutation", "port", "present", "private", "procedure", "public",
49 "raise", "read", "receive", "record", "recursive", "refers", "rem", "repeat", "reply", "return", "running", "runs",
50 "select", "self", "send", "sender", "set", "setverdict", "signature", "start", "stop", "subset",
52 "template", "testcase", "timeout", "timer", "to", "trigger", "true", "type",
53 "union", "universal", "unmap",
54 "value", "valueof", "var", "variant", "verdicttype",
59 static const char* TTCN3_predefined_functions
[] = {
60 "bit2int", "bit2hex", "bit2oct", "bit2str",
61 "char2int", "char2oct",
63 "encvalue", "enum2int",
64 "float2int", "float2str",
65 "hex2bit", "hex2int", "hex2oct", "hex2str",
66 "int2bit", "int2char", "int2enum", "int2float", "int2hex", "int2oct", "int2str", "int2unichar",
67 "isvalue", "ischosen", "ispresent",
68 "lengthof", "log2str",
69 "oct2bit", "oct2char", "oct2hex", "oct2int", "oct2str", "oct2unichar"
70 "regexp", "replace", "rnd", "remove_bom", "get_stringencoding",
71 "sizeof", "str2bit", "str2float", "str2hex", "str2int", "str2oct", "substr",
73 "unichar2int", "unichar2char", "unichar2oct",
76 static const char* ASN1_reserved_words
[] = {
77 "ABSENT", "ABSTRACT-SYNTAX", "ALL", "APPLICATION", "AUTOMATIC",
78 "BEGIN", "BIT", "BMPString", "BOOLEAN", "BY",
79 "CHARACTER", "CHOICE", "CLASS", "COMPONENT", "COMPONENTS", "CONSTRAINED", "CONTAINING",
80 "DEFAULT", "DEFINITIONS",
81 "EMBEDDED", "ENCODED", "END", "ENUMERATED", "EXCEPT", "EXPLICIT", "EXPORTS", "EXTENSIBILITY",
84 "GeneralizedTime", "GeneralString", "GraphicString",
85 "IA5String", "IDENTIFIER", "IMPLICIT", "IMPLIED", "IMPORTS", "INCLUDES", "INSTANCE", "INTEGER",
86 "INTERSECTION", "ISO646String",
87 "MAX", "MIN", "MINUS-INFINITY",
88 "NULL", "NumericString",
89 "OBJECT", "ObjectDescriptor", "OCTET", "OF", "OPTIONAL",
90 "PATTERN", "PDV", "PLUS-INFINITY", "PRESENT", "PrintableString", "PRIVATE",
91 "REAL", "RELATIVE-OID",
92 "SEQUENCE", "SET", "SIZE", "STRING", "SYNTAX",
93 "T61String", "TAGS", "TeletexString", "TRUE", "TYPE-IDENTIFIER",
94 "UNION", "UNIQUE", "UNIVERSAL", "UniversalString", "UTCTime", "UTF8String",
95 "VideotexString", "VisibleString",
100 Mstring
ns_uri(variant
);
106 if (res
.size() > 0) {
107 /********************************************************
108 * STEP 0 - recognizing XSD built-in types
110 * ******************************************************/
111 // If the type or field reference name is an XSD built-in type then it will be capitalized and get a prefix "XSD."
112 // if (type_of_the_name == type_reference_name || type_of_the_name == field_reference_name) {
114 if (type_of_the_name
== type_reference_name
) {
115 if (isBuiltInType(res
)) {
116 res
[0] = toupper(res
[0]);
120 if (res
== "record" ||
127 if (type_of_the_name
== enum_id_name
) {
129 for (QualifiedNames::iterator used
= used_names
.begin(); used
; used
= used
->Next
) {
130 QualifiedName
tmp(empty_string
, res
);
131 if (tmp
.nsuri
== used
->Data
.nsuri
&& tmp
.orig_name
== used
->Data
.orig_name
) {
138 /********************************************************
139 * STEP 1 - character changes
142 * clause 5.2.2.1 - Generic name transformation rules
143 * ******************************************************/
144 // the characters ' '(SPACE), '.'(FULL STOP) and '-'(HYPEN-MINUS)shall all be replaced by a "_" (LOW LINE)
145 for (size_t i
= 0; i
!= res
.size(); ++i
) {
146 if ((res
[i
] == ' ') ||
147 (res
[i
] == '.' && !no_replace
) ||
152 // any character except "A" to "Z", "a" to "z" or "0" to "9" and "_" shall be removed
153 for (size_t i
= 0; i
!= res
.size(); ++i
) {
154 if (!isalpha((const unsigned char) res
[i
]) && !isdigit((const unsigned char) res
[i
]) && (res
[i
] != '_')) {
155 if (!no_replace
&& res
[i
] != '.') {
161 // a sequence of two of more "_" (LOW LINE) shall be replaced with a single "_" (LOW LINE)
162 for (size_t i
= 1; i
< res
.size(); ++i
) {
163 if (res
[i
] == '_' && res
[i
- 1] == '_') {
168 // "_" (LOW LINE) characters occurring at the end of the name shall be removed
169 if (!res
.empty() && res
[res
.size() - 1] == '_') {
170 res
.eraseChar(res
.size() - 1);
172 // "_" (LOW LINE) characters occurring at the beginning of the name shall be removed
173 if (!res
.empty() && res
[0] == '_') {
178 switch (type_of_the_name
) {
179 case type_reference_name
:
184 if (islower((const unsigned char) res
[0])) {
185 res
.setCapitalized();
186 } else if (isdigit((const unsigned char) res
[0])) {
187 res
.insertChar(0, 'X');
196 if (isupper((const unsigned char) res
[0])) res
.setUncapitalized();
197 else if (isdigit((const unsigned char) res
[0])) res
.insertChar(0, 'x');
201 /********************************************************
202 * STEP 2 - process if the generated name is
206 * and after any change is made
208 * clause 5.2.2.2 - Succeeding rules
209 * ******************************************************/
210 /********************************************************
211 * according to paragraph a)
212 * ******************************************************/
213 bool postfixing
= false;
214 QualifiedName
qual_name(ns_uri
, res
, in
);
217 switch (type_of_the_name
) {
218 // Do not use "res" in this switch; only qual_name
221 for (int k
= 0; ASN1_reserved_words
[k
]; k
++) {
222 if (qual_name
.name
== ASN1_reserved_words
[k
]) {
228 for (QualifiedNames::iterator used
= used_names
.begin(); used
; used
= used
->Next
) {
229 if (qual_name
== used
->Data
) {
238 expstring_t tmpname
= NULL
;
242 tmpname
= mprintf("%s_%d", qual_name
.name
.c_str(), counter
);
243 for (QualifiedNames::iterator used
= used_names
.begin(); used
; used
= used
->Next
) {
244 if (QualifiedName(/* empty_string ? */ ns_uri
, Mstring(tmpname
)) == used
->Data
) {
251 qual_name
.name
= tmpname
; // NULL will result in an empty string
259 for (int k
= 0; TTCN3_reserved_words
[k
]; k
++) {
260 if (qual_name
.name
== TTCN3_reserved_words
[k
]) postfixing
= true;
262 for (int k
= 0; TTCN3_predefined_functions
[k
]; k
++) {
263 if (qual_name
.name
== TTCN3_predefined_functions
[k
]) postfixing
= true;
266 qual_name
.name
+= "_";
270 for (QualifiedNames::iterator used
= used_names
.begin(); used
; used
= used
->Next
) {
271 if (qual_name
== used
->Data
) postfixing
= true;
277 if (qual_name
.name
[qual_name
.name
.size() - 1] == '_')
278 qual_name
.name
.eraseChar(qual_name
.name
.size() - 1);
279 expstring_t tmpname
= mprintf("%s_%d", qual_name
.name
.c_str(), counter
);
284 tmpname
= mprintf("%s_%d", qual_name
.name
.c_str(), counter
);
286 for (QualifiedNames::iterator used
= used_names
.begin(); used
; used
= used
->Next
) {
287 if (QualifiedName(/* empty_string ? */ns_uri
, Mstring(tmpname
)) == used
->Data
) {
294 qual_name
.name
= tmpname
;
303 res
= qual_name
.name
;
305 /********************************************************
306 * STEP 3 - the defined name is put into the set of "not_av_names"
307 * ******************************************************/
308 // Finally recently defined name will be put into the set of "set<string> not_av_names"
309 if (type_of_the_name
!= type_reference_name
) {
311 for (QualifiedNames::iterator used
= used_names
.begin(); used
; used
= used
->Next
) {
312 if (qual_name
== used
->Data
) {
319 used_names
.push_back(qual_name
);
322 /********************************************************
325 * ******************************************************/
326 if (in
== "sequence" ||
328 in
== "sequence_list" ||
329 in
== "choice_list") {
332 /********************************************************
333 * STEP 5 - if "input name - in" and "generated name - res"
335 * then a "variant string" has to be generated
336 * ******************************************************/
337 // If the final generated type name, field name or enumeration identifier (res) is different from the original one (in) then ...
341 tmp1
.setUncapitalized();
342 tmp2
.setUncapitalized();
343 switch (type_of_the_name
) {
345 if (tmp1
== tmp2
) { // If the only difference is the case of the first letter
346 if (isupper(in
[0])) {
347 variant
+= "\"name as capitalized\"";
349 variant
+= "\"name as uncapitalized\"";
351 } else { // Otherwise if other letters have changed too
352 variant
+= "\"name as '" + in
+ "'\"";
356 // Creating a variant string from a field of a complex type needs to write out the path of the fieldname
357 if (tmp1
== tmp2
) { // If the only difference is the case of the first letter
358 if (isupper(in
[0])) {
359 variant
+= "\"name as capitalized\"";
361 variant
+= "\"name as uncapitalized\"";
363 } else { // Otherwise if other letters have changed too
364 variant
+= "\"name as '" + in
+ "'\"";
368 if (tmp1
== tmp2
) { // If the only difference is the case of the first letter
369 if (isupper(in
[0])) {
370 variant
+= "\"text \'" + res
+ "\' as capitalized\"";
372 variant
+= "\"text \'" + res
+ "\' as uncapitalized\"";
374 } else { // Otherwise if other letters have changed too
375 variant
+= "\"text \'" + res
+ "\' as '" + in
+ "'\"";
384 bool isBuiltInType(const Mstring
& in
) {
385 static const char* XSD_built_in_types
[] = {
386 "string", "normalizedString", "token", "Name", "NMTOKEN", "NCName", "ID", "IDREF", "ENTITY",
387 "hexBinary", "base64Binary", "anyURI", "language", "integer", "positiveInteger", "nonPositiveInteger",
388 "negativeInteger", "nonNegativeInteger", "long", "unsignedLong", "int", "unsignedInt", "short",
389 "unsignedShort", "byte", "unsignedByte", "decimal", "float", "double", "duration", "dateTime", "time",
390 "date", "gYearMonth", "gYear", "gMonthDay", "gDay", "gMonth", "NMTOKENS", "IDREFS", "ENTITIES",
391 "QName", "boolean", "anyType", "anySimpleType", NULL
393 const Mstring
& name
= in
.getValueWithoutPrefix(':');
394 for (int i
= 0; XSD_built_in_types
[i
]; ++i
) {
395 if (name
== XSD_built_in_types
[i
]) {
402 bool isStringType(const Mstring
& in
) {
403 static const char* string_types
[] = {
404 "string", "normalizedString", "token", "Name", "NMTOKEN", "NCName", "ID", "IDREF", "ENTITY",
405 "hexBinary", "base64Binary", "anyURI", "language", NULL
407 const Mstring
& name
= in
.getValueWithoutPrefix(':');
408 for (int i
= 0; string_types
[i
]; ++i
) {
409 if (name
== string_types
[i
]) {
416 bool isIntegerType(const Mstring
& in
) {
417 static const char* integer_types
[] = {
418 "integer", "positiveInteger", "nonPositiveInteger", "negativeInteger", "nonNegativeInteger", "long",
419 "unsignedLong", "int", "unsignedInt", "short", "unsignedShort", "byte", "unsignedByte", NULL
421 const Mstring
& name
= in
.getValueWithoutPrefix(':');
422 for (int i
= 0; integer_types
[i
]; ++i
) {
423 if (name
== integer_types
[i
]) {
430 bool isFloatType(const Mstring
& in
) {
431 static const char* float_types
[] = {
432 "decimal", "float", "double", NULL
434 const Mstring
& name
= in
.getValueWithoutPrefix(':');
435 for (int i
= 0; float_types
[i
]; ++i
) {
436 if (name
== float_types
[i
]) {
443 bool isTimeType(const Mstring
& in
) {
444 static const char* time_types
[] = {
445 "duration", "dateTime", "time", "date", "gYearMonth", "gYear", "gMonthDay", "gDay", "gMonth", NULL
447 const Mstring
& name
= in
.getValueWithoutPrefix(':');
448 for (int i
= 0; time_types
[i
]; ++i
) {
449 if (name
== time_types
[i
]) {
456 bool isSequenceType(const Mstring
& in
) {
457 static const char* sequence_types
[] = {
458 "NMTOKENS", "IDREFS", "ENTITIES", "QName", NULL
460 const Mstring
& name
= in
.getValueWithoutPrefix(':');
461 for (int i
= 0; sequence_types
[i
]; ++i
) {
462 if (name
== sequence_types
[i
]) {
469 bool isBooleanType(const Mstring
& in
) {
470 static const Mstring
booltype("boolean");
471 return booltype
== in
.getValueWithoutPrefix(':');
474 bool isQNameType(const Mstring
& in
) {
475 static const Mstring
qntype("QName");
476 return qntype
== in
.getValueWithoutPrefix(':');
479 bool isAnyType(const Mstring
& in
) {
480 static const char* any_types
[] = {
481 "anyType", "anySimpleType", NULL
483 const Mstring
& name
= in
.getValueWithoutPrefix(':');
484 for (int i
= 0; any_types
[i
]; ++i
) {
485 if (name
== any_types
[i
]) return true;
490 bool matchDates(const char * string
, const char * type
) {
491 const Mstring
day("(0[1-9]|[12][0-9]|3[01])");
492 const Mstring
month("(0[1-9]|1[0-2])");
493 const Mstring
year("([0-9][0-9][0-9][0-9])");
494 const Mstring
hour("([01][0-9]|2[0-3])");
495 const Mstring
minute("([0-5][0-9])");
496 const Mstring
second("([0-5][0-9])");
497 const Mstring
endofdayext("24:00:00(.0?)?");
498 const Mstring
yearext("((-)([1-9][0-9]*)?)?");
499 const Mstring
timezone("(Z|[+-]((0[0-9]|1[0-3]):[0-5][0-9]|14:00))?");
500 const Mstring
fraction("(.[0-9]+)?");
501 const Mstring
nums("[0-9]+");
502 const Mstring
durtime("(T[0-9]+"
503 "(H([0-9]+(M([0-9]+(S|.[0-9]+S))?|.[0-9]+S|S))?|"
504 "M([0-9]+(S|.[0-9]+S)|.[0-9]+M)?|S|.[0-9]+S))");
507 if (strcmp(type
, "gDay") == 0) {
508 pattern
= Mstring("(---)") + day
+ timezone
;
509 } else if (strcmp(type
, "gMonth") == 0) {
510 pattern
= Mstring("(--)") + month
+ timezone
;
511 } else if (strcmp(type
, "gYear") == 0) {
512 pattern
= yearext
+ year
+ timezone
;
513 } else if (strcmp(type
, "gYearMonth") == 0) {
514 pattern
= yearext
+ year
+ Mstring("(-)") + month
+ timezone
;
515 } else if (strcmp(type
, "gMonthDay") == 0) {
516 pattern
= Mstring("(--)") + month
+ Mstring("(-)") + day
+ timezone
;
517 } else if (strcmp(type
, "date") == 0) {
518 pattern
= yearext
+ year
+ Mstring("(-)") + month
+ Mstring("(-)") + day
+ timezone
;
519 } else if (strcmp(type
, "time") == 0) {
520 pattern
= Mstring("(") + hour
+ Mstring(":") + minute
+ Mstring(":") + second
+
521 fraction
+ Mstring("|") + endofdayext
+ Mstring(")") + timezone
;
522 } else if (strcmp(type
, "dateTime") == 0) {
523 pattern
= yearext
+ year
+ Mstring("(-)") + month
+ Mstring("(-)") + day
+
524 Mstring("T(") + hour
+ Mstring(":") + minute
+ Mstring(":") + second
+
525 fraction
+ Mstring("|") + endofdayext
+ Mstring(")") + timezone
;
526 } else if (strcmp(type
, "duration") == 0) {
527 pattern
= Mstring("(-)?P(") + nums
+ Mstring("(Y(") + nums
+ Mstring("(M(") +
528 nums
+ Mstring("D") + durtime
+ Mstring("?|") + durtime
+ Mstring("?|D") +
529 durtime
+ Mstring("?)|") + durtime
+ Mstring("?)|M") + nums
+ Mstring("D") +
530 durtime
+ Mstring("?|") + durtime
+ Mstring("?)|D") + durtime
+
531 Mstring("?)|") + durtime
+ Mstring(")");
536 pattern
= Mstring("^") + pattern
+ Mstring("$");
537 return matchRegexp(string
, pattern
.c_str());
540 bool matchRegexp(const char * string
, const char * pattern
) {
543 if (regcomp(&re
, pattern
, REG_EXTENDED
| REG_NOSUB
) != 0) {
544 return (false); /* report error */
546 status
= regexec(&re
, string
, (size_t) 0, NULL
, 0);
549 return (false); /* report error */
554 void printError(const Mstring
& filename
, int lineNumber
, const Mstring
& text
) {
564 void printError(const Mstring
& filename
, const Mstring
& typeName
, const Mstring
& text
) {
574 void printWarning(const Mstring
& filename
, int lineNumber
, const Mstring
& text
) {
575 if (w_flag_used
) return;
585 void printWarning(const Mstring
& filename
, const Mstring
& typeName
, const Mstring
& text
) {
586 if (w_flag_used
) return;
596 void indent(FILE* file
, const int x
) {
597 for (int l
= 0; l
< x
; ++l
) {
602 long double stringToLongDouble(const char *input
) {
603 long double result
= 0.0;
604 // `strtold()' is not available on older platforms.
605 sscanf(input
, "%Lf", &result
);
609 const Mstring
& getNameSpaceByPrefix(const RootType
* root
, const Mstring
& prefix
){
610 for(List
<NamespaceType
>::iterator mod
= root
->getModule()->getDeclaredNamespaces().begin(); mod
; mod
= mod
->Next
){
611 if(mod
->Data
.prefix
== prefix
){
612 return mod
->Data
.uri
;
618 const Mstring
& getPrefixByNameSpace(const RootType
* root
, const Mstring
& namespace_
){
619 for(List
<NamespaceType
>::iterator mod
= root
->getModule()->getDeclaredNamespaces().begin(); mod
; mod
= mod
->Next
){
620 if(mod
->Data
.uri
== namespace_
){
621 return mod
->Data
.prefix
;
627 const Mstring
findBuiltInType(const RootType
* ref
, Mstring type
){
628 RootType
* root
= TTCN3ModuleInventory::getInstance().lookup(ref
, type
, want_BOTH
);
629 if(root
!= NULL
&& isBuiltInType(root
->getType().originalValueWoPrefix
)){
630 return root
->getType().originalValueWoPrefix
;
631 }else if(root
!= NULL
){
632 return findBuiltInType(root
, root
->getType().originalValueWoPrefix
);
638 RootType
* lookup(const List
<TTCN3Module
*> mods
, const SimpleType
* reference
, wanted w
) {
639 const Mstring
& uri
= reference
->getReference().get_uri();
640 const Mstring
& name
= reference
->getReference().get_val();
642 return lookup(mods
, name
, uri
, reference
, w
);
645 RootType
* lookup(const List
<TTCN3Module
*> mods
,
646 const Mstring
& name
, const Mstring
& nsuri
, const RootType
*reference
, wanted w
) {
647 RootType
*ret
= NULL
;
648 for (List
<TTCN3Module
*>::iterator module
= mods
.begin(); module
; module
= module
->Next
) {
649 ret
= lookup1(module
->Data
, name
, nsuri
, reference
, w
);
650 if (ret
!= NULL
) break;
656 RootType
*lookup1(const TTCN3Module
*module
,
657 const Mstring
& name
, const Mstring
& nsuri
, const RootType
*reference
, wanted w
) {
658 if (nsuri
!= module
->getTargetNamespace()) return NULL
;
660 for (List
<RootType
*>::iterator type
= module
->getDefinedTypes().begin(); type
; type
= type
->Next
) {
661 switch (type
->Data
->getConstruct()) {
665 if (w
== want_ST
|| w
== want_BOTH
) {
666 if ((const RootType
*) reference
!= type
->Data
667 && name
== type
->Data
->getName().originalValueWoPrefix
) {
675 case c_attributeGroup
:
676 if (w
== want_CT
|| w
== want_BOTH
) {
677 if ((const RootType
*) reference
!= type
->Data
678 && name
== type
->Data
->getName().originalValueWoPrefix
) {
692 int multi(const TTCN3Module
*module
, ReferenceData
const& outside_reference
,
693 const RootType
*obj
) {
694 int multiplicity
= 0;
696 RootType
* st
= ::lookup1(module
, outside_reference
.get_val(), outside_reference
.get_uri(), obj
, want_ST
);
697 RootType
* ct
= ::lookup1(module
, outside_reference
.get_val(), outside_reference
.get_uri(), obj
, want_CT
);
699 multiplicity
= 1; // locally defined, no qualif needed
700 } else for (List
<const TTCN3Module
*>::iterator it
= module
->getImportedModules().begin(); it
; it
= it
->Next
) {
702 st
= ::lookup1(it
->Data
, outside_reference
.get_val(), it
->Data
->getTargetNamespace(), obj
, want_ST
);
703 ct
= ::lookup1(it
->Data
, outside_reference
.get_val(), it
->Data
->getTargetNamespace(), obj
, want_CT
);