|
4 | 4 |
|
5 | 5 | import logging |
6 | 6 | from datetime import date |
7 | | -from typing import Any, Dict, List, Optional, Set, Union, cast |
| 7 | +from typing import Any, Dict, List, Optional, Set, Type, Union, cast |
8 | 8 |
|
9 | 9 | from curies import Converter |
10 | 10 | from linkml_runtime.linkml_model.meta import SlotDefinition |
@@ -284,6 +284,50 @@ def to_rdf(self, value: Any) -> Node: |
284 | 284 | return Literal(value) |
285 | 285 |
|
286 | 286 |
|
| 287 | +class ValueConverterFactory(object): |
| 288 | + """Helper object to create value converters.""" |
| 289 | + |
| 290 | + constructors: Dict[str, Type[ValueConverter]] |
| 291 | + |
| 292 | + def __init__(self) -> None: |
| 293 | + """Create a new instance.""" |
| 294 | + self.constructors = { |
| 295 | + "date": DateValueConverter, |
| 296 | + "double": DoubleValueConverter, |
| 297 | + "string": StringValueConverter, |
| 298 | + "ncname": StringValueConverter, |
| 299 | + "EntityReference": EntityReferenceValueConverter, |
| 300 | + "uriorcurie": EntityReferenceValueConverter, |
| 301 | + "NonRelativeURI": NonRelativeURIValueConverter, |
| 302 | + } |
| 303 | + |
| 304 | + def create( |
| 305 | + self, range_name: str, schema: SchemaView, curie_converter: Converter |
| 306 | + ) -> ValueConverter: |
| 307 | + """Create a new value converter. |
| 308 | +
|
| 309 | + :param range_name: The range for which a value converter is |
| 310 | + wanted. |
| 311 | + :param schema: The SSSOM LinkML schema. |
| 312 | + :param curie_converter: The CURIE converter to use, for the |
| 313 | + converters that need one. |
| 314 | +
|
| 315 | + :returns: A suitable value converter for the range. |
| 316 | + """ |
| 317 | + ctor = self.constructors.get(range_name) |
| 318 | + if ctor is not None: |
| 319 | + if ctor == EntityReferenceValueConverter: |
| 320 | + return EntityReferenceValueConverter(curie_converter) |
| 321 | + else: |
| 322 | + return ctor() |
| 323 | + elif range_name.endswith("_enum"): |
| 324 | + return EnumValueConverter(schema, range_name) |
| 325 | + else: |
| 326 | + # This should only happen if a brand new type of slot has |
| 327 | + # been introduced in the SSSOM schema |
| 328 | + raise NotImplementedError(f"Range {range_name} is not supported") |
| 329 | + |
| 330 | + |
287 | 331 | class ObjectConverter(object): |
288 | 332 | """Base class for conversion of SSSOM objects to and from RDF. |
289 | 333 |
|
@@ -331,31 +375,20 @@ def __init__(self, class_name: str, curie_converter: Converter): |
331 | 375 | self.schema = SSSOMSchemaView() |
332 | 376 | self.curie_converter = curie_converter |
333 | 377 |
|
334 | | - str_value_converter = StringValueConverter() |
335 | | - er_value_converter = EntityReferenceValueConverter(curie_converter) |
336 | | - self.value_converters = { |
337 | | - "string": str_value_converter, |
338 | | - "ncname": str_value_converter, |
339 | | - "EntityReference": er_value_converter, |
340 | | - "uriorcurie": er_value_converter, |
341 | | - "NonRelativeURI": NonRelativeURIValueConverter(), |
342 | | - "date": DateValueConverter(), |
343 | | - "double": DoubleValueConverter(), |
344 | | - "entity_type_enum": EnumValueConverter(self.schema.view, "entity_type_enum"), |
345 | | - "sssom_version_enum": EnumValueConverter(self.schema.view, "sssom_version_enum"), |
346 | | - "mapping_cardinality_enum": EnumValueConverter( |
347 | | - self.schema.view, "mapping_cardinality_enum" |
348 | | - ), |
349 | | - "predicate_modifier_enum": EnumValueConverter( |
350 | | - self.schema.view, "predicate_modifier_enum" |
351 | | - ), |
352 | | - } |
353 | | - |
354 | 378 | self.slots_by_name = {} |
355 | 379 | self.slots_by_uri = {} |
| 380 | + ranges: List[SlotDefinition] = [] |
356 | 381 | for slot in self.schema.view.class_induced_slots(class_name): |
357 | 382 | self.slots_by_name[slot.name] = slot |
358 | 383 | self.slots_by_uri[self._get_slot_uri(slot)] = slot |
| 384 | + ranges.append(slot.range) |
| 385 | + |
| 386 | + self.value_converters = {} |
| 387 | + factory = ValueConverterFactory() |
| 388 | + for rng in set(ranges): |
| 389 | + if self.schema.view.get_class(rng) is not None: |
| 390 | + continue |
| 391 | + self.value_converters[rng] = factory.create(rng, self.schema.view, curie_converter) |
359 | 392 |
|
360 | 393 | self.name = self._fix_class_name(class_name) |
361 | 394 | object_class = self.schema.view.get_class(class_name) |
@@ -583,8 +616,8 @@ def _get_value_converter(self, slot: SlotDefinition) -> ValueConverter: |
583 | 616 | """ |
584 | 617 | converter = self.value_converters.get(slot.range) |
585 | 618 | if converter is None: |
586 | | - # This should only happen if a brand new type of slot has |
587 | | - # been introduced in the SSSOM schema |
| 619 | + # This should have been caught already at init time by the |
| 620 | + # ValueConverterFactory |
588 | 621 | raise NotImplementedError(f"Unsupported range {slot.range} for {slot.name}") |
589 | 622 | return converter |
590 | 623 |
|
|
0 commit comments