@@ -58,39 +58,55 @@ def safename2(name: Dict[str, str]) -> str:
5858 return safename (name ["namespace" ]) + "::" + safename (name ["classname" ])
5959
6060
61- # Splits names like https://xyz.xyz/blub#cwl/class
62- # into its class path and non class path
6361def split_name (s : str ) -> Tuple [str , str ]:
62+ """Split url name into its components.
63+
64+ Splits names like https://xyz.xyz/blub#cwl/class
65+ into its class path and non class path
66+ """
6467 t = s .split ("#" )
6568 if len (t ) != 2 :
6669 raise ValueError ("Expected field to be formatted as 'https://xyz.xyz/blub#cwl/class'." )
6770 return (t [0 ], t [1 ])
6871
6972
70- # similar to split_name but for field names
7173def split_field (s : str ) -> Tuple [str , str , str ]:
74+ """Split field into its components.
75+
76+ similar to split_name but for field names
77+ """
7278 (namespace , field ) = split_name (s )
7379 t = field .split ("/" )
7480 if len (t ) != 2 :
7581 raise ValueError ("Expected field to be formatted as 'https://xyz.xyz/blub#cwl/class'." )
7682 return (namespace , t [0 ], t [1 ])
7783
7884
79- # Prototype of a class
8085class ClassDefinition :
86+ """Prototype of a class."""
87+
8188 def __init__ (self , name : str ):
89+ """Initialize the class definition with a name."""
8290 self .fullName = name
8391 self .extends : List [Dict [str , str ]] = []
92+
93+ # List of types from parent classes that have been specialized
94+ self .specializationTypes : List [str ] = []
95+
96+ # this includes fields that are also inheritant
97+ self .allfields : List [FieldDefinition ] = []
8498 self .fields : List [FieldDefinition ] = []
8599 self .abstract = False
86100 (self .namespace , self .classname ) = split_name (name )
87101 self .namespace = safename (self .namespace )
88102 self .classname = safename (self .classname )
89103
90104 def writeFwdDeclaration (self , target : IO [str ], fullInd : str , ind : str ) -> None :
105+ """Write forward declaration."""
91106 target .write (f"{ fullInd } namespace { self .namespace } {{ struct { self .classname } ; }}\n " )
92107
93108 def writeDefinition (self , target : IO [Any ], fullInd : str , ind : str ) -> None :
109+ """Write definition of the class."""
94110 target .write (f"{ fullInd } namespace { self .namespace } {{\n " )
95111 target .write (f"{ fullInd } struct { self .classname } " )
96112 extends = list (map (safename2 , self .extends ))
@@ -113,6 +129,7 @@ def writeDefinition(self, target: IO[Any], fullInd: str, ind: str) -> None:
113129 target .write (f"{ fullInd } }}\n \n " )
114130
115131 def writeImplDefinition (self , target : IO [str ], fullInd : str , ind : str ) -> None :
132+ """Write definition with implementation."""
116133 extends = list (map (safename2 , self .extends ))
117134
118135 if self .abstract :
@@ -131,20 +148,33 @@ def writeImplDefinition(self, target: IO[str], fullInd: str, ind: str) -> None:
131148
132149 for field in self .fields :
133150 fieldname = safename (field .name )
134- target .write (
135- f'{ fullInd } { ind } addYamlField(n, "{ field .name } ", toYaml(*{ fieldname } ));\n ' # noqa: B907
136- )
151+ if field .remap != "" :
152+ target .write (
153+ f"""{ fullInd } { ind } addYamlField(n, "{ field .name } ",
154+ convertListToMap(toYaml(*{ fieldname } ), "{ field .remap } "));\n """ # noqa: B907
155+ )
156+ else :
157+ target .write (
158+ f'{ fullInd } { ind } addYamlField(n, "{ field .name } ", toYaml(*{ fieldname } ));\n ' # noqa: B907
159+ )
137160 # target.write(f"{fullInd}{ind}addYamlIfNotEmpty(n, \"{field.name}\", toYaml(*{fieldname}));\n")
138161
139162 target .write (f"{ fullInd } { ind } return n;\n { fullInd } }}\n " )
140163
141164
142- # Prototype of a single field of a class
143165class FieldDefinition :
144- def __init__ (self , name : str , typeStr : str , optional : bool ):
166+ """Prototype of a single field from a class definition."""
167+
168+ def __init__ (self , name : str , typeStr : str , optional : bool , remap : str ):
169+ """Initialize field definition.
170+
171+ Creates a new field with name, its type, optional and which field to use to convert
172+ from list to map (or empty if it is not possible)
173+ """
145174 self .name = name
146175 self .typeStr = typeStr
147176 self .optional = optional
177+ self .remap = remap
148178
149179 def writeDefinition (self , target : IO [Any ], fullInd : str , ind : str , namespace : str ) -> None :
150180 """Write a C++ definition for the class field."""
@@ -153,13 +183,16 @@ def writeDefinition(self, target: IO[Any], fullInd: str, ind: str, namespace: st
153183 target .write (f"{ fullInd } heap_object<{ typeStr } > { name } ;\n " )
154184
155185
156- # Prototype of an enum definition
157186class EnumDefinition :
187+ """Prototype of a enum."""
188+
158189 def __init__ (self , name : str , values : List [str ]):
190+ """Initialize enum definition with a name and possible values."""
159191 self .name = name
160192 self .values = values
161193
162194 def writeDefinition (self , target : IO [str ], ind : str ) -> None :
195+ """Write enum definition to output."""
163196 namespace = ""
164197 if len (self .name .split ("#" )) == 2 :
165198 (namespace , classname ) = split_name (self .name )
@@ -201,12 +234,14 @@ def writeDefinition(self, target: IO[str], ind: str) -> None:
201234
202235# !TODO way tot many functions, most of these shouldn't exists
203236def isPrimitiveType (v : Any ) -> bool :
237+ """Check if v is a primitve type."""
204238 if not isinstance (v , str ):
205239 return False
206240 return v in ["null" , "boolean" , "int" , "long" , "float" , "double" , "string" ]
207241
208242
209243def hasFieldValue (e : Any , f : str , v : Any ) -> bool :
244+ """Check if e has a field f value."""
210245 if not isinstance (e , dict ):
211246 return False
212247 if f not in e :
@@ -215,10 +250,12 @@ def hasFieldValue(e: Any, f: str, v: Any) -> bool:
215250
216251
217252def isRecordSchema (v : Any ) -> bool :
253+ """Check if v is of type record schema."""
218254 return hasFieldValue (v , "type" , "record" )
219255
220256
221257def isEnumSchema (v : Any ) -> bool :
258+ """Check if v is of type enum schema."""
222259 if not hasFieldValue (v , "type" , "enum" ):
223260 return False
224261 if "symbols" not in v :
@@ -229,6 +266,7 @@ def isEnumSchema(v: Any) -> bool:
229266
230267
231268def isArray (v : Any ) -> bool :
269+ """Check if v is of type array."""
232270 if not isinstance (v , list ):
233271 return False
234272 for i in v :
@@ -238,6 +276,7 @@ def isArray(v: Any) -> bool:
238276
239277
240278def pred (i : Any ) -> bool :
279+ """Check if v is any of the simple types."""
241280 return (
242281 isPrimitiveType (i )
243282 or isRecordSchema (i )
@@ -248,6 +287,7 @@ def pred(i: Any) -> bool:
248287
249288
250289def isArraySchema (v : Any ) -> bool :
290+ """Check if v is of type array schema."""
251291 if not hasFieldValue (v , "type" , "array" ):
252292 return False
253293 if "items" not in v :
@@ -272,6 +312,7 @@ def __init__(
272312 package : str ,
273313 copyright : Optional [str ],
274314 ) -> None :
315+ """Initialize the C++ code generator."""
275316 super ().__init__ ()
276317 self .base_uri = base
277318 self .target = target
@@ -376,8 +417,8 @@ def convertTypeToCpp(self, type_declaration: Union[List[Any], Dict[str, Any], st
376417 type_declaration = ", " .join (type_declaration )
377418 return f"std::variant<{ type_declaration } >"
378419
379- # start of our generated file
380420 def epilogue (self , root_loader : Optional [TypeDef ]) -> None :
421+ """Generate final part of our cpp file."""
381422 self .target .write (
382423 """#pragma once
383424
@@ -428,12 +469,23 @@ def epilogue(self, root_loader: Optional[TypeDef]) -> None:
428469 return YAML::Node{v};
429470}
430471
431- inline void addYamlField(YAML::Node node, std::string const& key, YAML::Node value) {
472+ inline void addYamlField(YAML::Node& node, std::string const& key, YAML::Node value) {
432473 if (value.IsDefined()) {
433474 node[key] = value;
434475 }
435476}
436477
478+ inline auto convertListToMap(YAML::Node list, std::string const& key_name) {
479+ if (list.size() == 0) return list;
480+ auto map = YAML::Node{};
481+ for (YAML::Node n : list) {
482+ auto key = n[key_name].as<std::string>();
483+ n.remove(key_name);
484+ map[key] = n;
485+ }
486+ return map;
487+ }
488+
437489// fwd declaring toYaml
438490template <typename T>
439491auto toYaml(std::vector<T> const& v) -> YAML::Node;
@@ -505,6 +557,27 @@ class heap_object {
505557 for key in self .classDefinitions :
506558 self .classDefinitions [key ].writeFwdDeclaration (self .target , "" , " " )
507559
560+ # remove parent classes, that are specialized/templated versions
561+ for key in self .classDefinitions :
562+ if len (self .classDefinitions [key ].specializationTypes ) > 0 :
563+ self .classDefinitions [key ].extends = []
564+
565+ # remove fields that are available in a parent class
566+ for key in self .classDefinitions :
567+ for field in self .classDefinitions [key ].allfields :
568+ found = False
569+ for parent_key in self .classDefinitions [key ].extends :
570+ fullKey = parent_key ["namespace" ] + "#" + parent_key ["classname" ]
571+ for f in self .classDefinitions [fullKey ].allfields :
572+ if f .name == field .name :
573+ found = True
574+ break
575+ if found :
576+ break
577+
578+ if not found :
579+ self .classDefinitions [key ].fields .append (field )
580+
508581 for key in self .enumDefinitions :
509582 self .enumDefinitions [key ].writeDefinition (self .target , " " )
510583 for key in self .classDefinitions :
@@ -542,7 +615,13 @@ class heap_object {
542615 )
543616
544617 def parseRecordField (self , field : Dict [str , Any ]) -> FieldDefinition :
618+ """Parse a record field."""
545619 (namespace , classname , fieldname ) = split_field (field ["name" ])
620+ remap = ""
621+ if "jsonldPredicate" in field :
622+ if "mapSubject" in field ["jsonldPredicate" ]:
623+ remap = field ["jsonldPredicate" ]["mapSubject" ]
624+
546625 if isinstance (field ["type" ], dict ):
547626 if field ["type" ]["type" ] == "enum" :
548627 fieldtype = "Enum"
@@ -553,9 +632,10 @@ def parseRecordField(self, field: Dict[str, Any]) -> FieldDefinition:
553632 fieldtype = field ["type" ]
554633 fieldtype = self .convertTypeToCpp (fieldtype )
555634
556- return FieldDefinition (name = fieldname , typeStr = fieldtype , optional = False )
635+ return FieldDefinition (name = fieldname , typeStr = fieldtype , optional = False , remap = remap )
557636
558637 def parseRecordSchema (self , stype : Dict [str , Any ]) -> None :
638+ """Parse a record schema."""
559639 cd = ClassDefinition (name = stype ["name" ])
560640 cd .abstract = stype .get ("abstract" , False )
561641
@@ -565,13 +645,18 @@ def parseRecordSchema(self, stype: Dict[str, Any]) -> None:
565645 ext = {"namespace" : base_namespace , "classname" : base_classname }
566646 cd .extends .append (ext )
567647
648+ if "specialize" in stype :
649+ for e in aslist (stype ["specialize" ]):
650+ cd .specializationTypes .append (e ["specializeFrom" ])
651+
568652 if "fields" in stype :
569653 for field in stype ["fields" ]:
570- cd .fields .append (self .parseRecordField (field ))
654+ cd .allfields .append (self .parseRecordField (field ))
571655
572656 self .classDefinitions [stype ["name" ]] = cd
573657
574658 def parseEnum (self , stype : Dict [str , Any ]) -> str :
659+ """Parse a schema salad enum."""
575660 name = cast (str , stype ["name" ])
576661 if name not in self .enumDefinitions :
577662 self .enumDefinitions [name ] = EnumDefinition (
@@ -580,6 +665,11 @@ def parseEnum(self, stype: Dict[str, Any]) -> str:
580665 return name
581666
582667 def parse (self , items : List [Dict [str , Any ]]) -> None :
668+ """Parse sechema salad items.
669+
670+ This function is being called from the outside and drives
671+ the whole code generation.
672+ """
583673 for stype in items :
584674 if "type" in stype and stype ["type" ] == "documentation" :
585675 continue
0 commit comments