properties class

properties class declaration
1) properties class declaration
2) config header update
3) getJsonProperties function declaration
4) mergeWith function declaration
This commit is contained in:
timatifey
2022-11-16 09:34:52 +03:00
parent de80298852
commit 90f17ecd0c
5 changed files with 359 additions and 9 deletions
+1
View File
@@ -57,6 +57,7 @@ class Config:
self.header: Text = dictionary.get('header') or ''
self.errors_collectors: List[str] = dictionary.get('errorsCollectors') or []
self.kotlin_annotations: List[str] = dictionary.get('kotlinAnnotations') or []
self.top_level_annotations: List[str] = dictionary.get('topLevelAnnotations') or []
self.generate_equality: bool = dictionary.get('generateEquality') or False
def __init__(self, generator_path: Optional[str], config_path: str, schema_path: str, output_path: str):
@@ -0,0 +1,266 @@
from __future__ import annotations
from typing import List, cast
from ...schema.modeling.entities import (
Entity,
EntityEnumeration,
StringEnumeration,
Declarable,
Property,
PropertyType,
Int,
Bool,
BoolInt,
Double,
StaticString,
Object,
Array,
Url,
Color,
String,
Dictionary,
)
from ... import utils
from ...schema.modeling.text import Text
def update_base(obj: Declarable) -> Declarable:
if isinstance(obj, Entity):
obj.__class__ = DivanEntity
cast(DivanEntity, obj).update_base()
elif isinstance(obj, EntityEnumeration):
obj.__class__ = DivanEntityEnumeration
cast(DivanEntityEnumeration, obj).update_base()
elif isinstance(obj, StringEnumeration):
obj.__class__ = DivanStringEnumeration
return obj
def comment(*lines) -> Text:
comment_block = Text('/**')
for line in lines:
comment_block += f' * {line}'
comment_block += ' */'
return comment_block
def default_value_comment(value) -> Text:
return comment(f'Значение по умолчанию: {value}')
def update_property_type_base(property_type: PropertyType):
if isinstance(property_type, Array):
property_type.__class__ = DivanArray
property_type = cast(DivanArray, property_type)
property_type.update_base()
elif isinstance(property_type, Bool):
property_type.__class__ = DivanBool
elif isinstance(property_type, BoolInt):
property_type.__class__ = DivanBoolInt
elif isinstance(property_type, Color):
property_type.__class__ = DivanColor
elif isinstance(property_type, Dictionary):
property_type.__class__ = DivanDictionary
elif isinstance(property_type, Double):
property_type.__class__ = DivanDouble
elif isinstance(property_type, Int):
property_type.__class__ = DivanInt
elif isinstance(property_type, Object):
property_type.__class__ = DivanObject
property_type = cast(DivanObject, property_type)
property_type.update_base()
elif isinstance(property_type, StaticString):
property_type.__class__ = DivanStaticString
elif isinstance(property_type, String):
property_type.__class__ = DivanString
elif isinstance(property_type, Url):
property_type.__class__ = DivanUrl
else:
raise NotImplementedError
class DivanEntity(Entity):
def update_base(self):
for prop in self.properties:
prop.__class__ = DivanProperty
cast(DivanProperty, prop).update_base()
@property
def supertype_declaration(self) -> str:
supertypes = []
enumeration = self.enclosing_enumeration
if enumeration is not None:
supertypes.append(f'{utils.capitalize_snake_case(enumeration.name)}()')
if self.root_entity:
supertypes.append('Root')
additional_supertypes = self.protocol_plus_super_entities
if additional_supertypes is not None:
supertypes.append(additional_supertypes)
if not supertypes:
return ''
return f' : {", ".join(supertypes)}'
@property
def header_comment_block(self) -> Text:
required_prop_names = sorted([prop.name for prop in self.properties if not prop.optional], reverse=True)
factory_method_name = utils.snake_case(self.name) # TODO(use parent naming)
lines = [
'',
f'Можно создать при помощи метода [{factory_method_name}].'
'',
f'Обязательные поля: {", ".join(required_prop_names)}'
]
return comment(*lines)
@property
def serialization_declaration(self) -> Text:
result = Text()
static_type = self.static_type
if static_type is None:
result += '@JsonAnyGetter'
result += 'internal fun getJsonProperties(): Map<String, Any> = properties.mergeWith(emptyMap())'
else:
result += '@JsonAnyGetter'
result += 'internal fun getJsonProperties(): Map<String, Any> = properties.mergeWith('
result += f' mapOf("type" to "{static_type}")'
result += ')'
return result
@property
def properties_class_declaration(self) -> Text:
declaration = Text()
declaration += 'class Properties internal constructor('
for prop in cast(List[DivanProperty], self.instance_properties):
declaration += f'{prop.constructor_parameter_declaration.indented(indent_width=4)},'
declaration += ') {'
merge_with_declaration = Text('internal fun mergeWith(properties: Map<String, Any>): Map<String, Any> {')
merge_with_declaration += ' val result = mutableMapOf<String, Any>()'
merge_with_declaration += ' result.putAll(properties)'
for prop in cast(List[DivanProperty], self.instance_properties):
merge_with_declaration += f' result.tryPutProperty("{prop.name}", {prop.name_declaration})'
merge_with_declaration += ' return result'
merge_with_declaration += '}'
declaration += merge_with_declaration.indented(indent_width=4)
declaration += '}'
return declaration
class DivanEntityEnumeration(EntityEnumeration):
def update_base(self):
self._entities = list(map(lambda e: (e[0], update_base(e[1])), self._entities))
class DivanStringEnumeration(StringEnumeration):
pass
class DivanProperty(Property):
def update_base(self):
update_property_type_base(self.property_type)
@property
def constructor_parameter_declaration(self) -> Text:
prop_type = cast(DivanPropertyType, self.property_type)
type_declaration = f'Property<{cast(DivanPropertyType, prop_type.declaration(prefixed=False))}>'
default_value = self.default_value
declaration = Text()
if default_value is not None:
declaration += default_value_comment(default_value)
declaration += f'val {self.name_declaration}: {type_declaration}?'
return declaration
@property
def name_declaration(self) -> str:
name = utils.lower_camel_case(self.name)
if isinstance(self.property_type, StaticString):
name = utils.constant_upper_case(self.name)
return utils.fixing_first_digit(name)
class DivanPropertyType(PropertyType):
def declaration(self, prefixed: bool) -> str:
if isinstance(self, Int):
return 'Int'
elif isinstance(self, Double):
return 'Double'
elif isinstance(self, Bool):
return 'Boolean'
elif isinstance(self, BoolInt):
return 'BoolInt'
elif isinstance(self, (String, StaticString)):
return 'String'
elif isinstance(self, Color):
return 'Color'
elif isinstance(self, Url):
return 'URI'
elif isinstance(self, Dictionary):
return 'Map<String, Any>'
elif isinstance(self, Array):
element_type = cast(DivanPropertyType, self.property_type).declaration(prefixed)
return f'List<{element_type}>'
elif isinstance(self, Object):
if self.name.startswith('$predefined_'):
return self.name.replace('$predefined_', '')
obj_name = utils.capitalize_camel_case(self.name)
obj = self.object
if obj is not None:
if prefixed:
obj_name = obj.resolved_prefixed_declaration
else:
obj_name = utils.capitalize_camel_case(obj.resolved_name)
return obj_name
else:
raise NotImplementedError
class DivanArray(DivanPropertyType, Array):
def update_base(self):
if not isinstance(self.property_type, DivanPropertyType):
update_property_type_base(self.property_type)
@property
def property_type_dsl(self) -> DivanPropertyType:
return cast(DivanPropertyType, self.property_type)
class DivanBool(DivanPropertyType, Bool):
pass
class DivanBoolInt(DivanPropertyType, BoolInt):
pass
class DivanColor(DivanPropertyType, Color):
pass
class DivanDictionary(DivanPropertyType, Dictionary):
pass
class DivanDouble(DivanPropertyType, Double):
pass
class DivanInt(DivanPropertyType, Int):
pass
class DivanObject(DivanPropertyType, Object):
def update_base(self):
if isinstance(self.object, Entity):
self.object.__class__ = DivanEntity
cast(DivanEntity, self.object).update_base()
class DivanStaticString(DivanPropertyType, StaticString):
pass
class DivanString(DivanPropertyType, String):
pass
class DivanUrl(DivanPropertyType, Url):
pass
@@ -1,19 +1,94 @@
from typing import List
from .divan_entities import (
update_base,
DivanEntity,
DivanEntityEnumeration,
DivanStringEnumeration,
)
from ..base import Generator
from ...schema.modeling.entities import StringEnumeration, EntityEnumeration, Entity
from ...schema.modeling.text import Text
from ...schema.modeling.text import Text, EMPTY
from ...schema.modeling.entities import (
StringEnumeration,
EntityEnumeration,
Entity,
Declarable,
)
from ... import utils
class DivanGenerator(Generator):
def __init__(self, config):
super(DivanGenerator, self).__init__(config)
self.kotlin_annotations = config.generation.kotlin_annotations
self.top_level_annotations = config.generation.top_level_annotations
def generate(self, objects: List[Declarable]):
super(DivanGenerator, self).generate(list(map(lambda obj: update_base(obj), objects)))
def filename(self, name: str) -> str:
return f'{utils.snake_case(name).capitalize()}.kt'
return f'{utils.capitalize_snake_case(name)}.kt'
def _entity_declaration(self, entity: Entity) -> Text:
def _entity_declaration(self, entity: DivanEntity) -> Text:
return self.__entity_declaration_with(entity, with_factory_methods=True)
def __entity_declaration_with(self, entity: DivanEntity, with_factory_methods: bool) -> Text:
result_declaration = Text()
result_declaration += entity.header_comment_block
for annotation in self.top_level_annotations + self.kotlin_annotations:
result_declaration += annotation
result_declaration += f'class {utils.capitalize_snake_case(entity.name)} internal constructor('
result_declaration += ' @JsonIgnore'
result_declaration += ' val properties: Properties,'
result_declaration += f'){entity.supertype_declaration} {{'
serialization_declaration = entity.serialization_declaration.indented(indent_width=4)
if serialization_declaration.lines:
result_declaration += serialization_declaration
result_declaration += EMPTY
result_declaration += entity.properties_class_declaration.indented(indent_width=4)
nested_classes_declaration = self.__nested_classes_declaration(entity)
if nested_classes_declaration.lines:
result_declaration += EMPTY
result_declaration += nested_classes_declaration.indented(indent_width=4)
result_declaration += '}'
return result_declaration
def __nested_classes_declaration(self, entity: DivanEntity) -> Text:
def declaration_of(decl_type, decl_method) -> Text:
def sort_predicate(d: Declarable):
return d.name
inner_types_decl = Text()
inner_types = sorted(filter(lambda t: isinstance(t, decl_type), entity.inner_types), key=sort_predicate)
for ind, p in enumerate(inner_types):
if isinstance(p, decl_type):
inner_types_decl += decl_method(p)
if ind != len(inner_types) - 1:
inner_types_decl += EMPTY
return inner_types_decl
declarations = [declaration_of(StringEnumeration, self._string_enumeration_declaration),
declaration_of(EntityEnumeration, self._entity_enumeration_declaration),
declaration_of(Entity,
lambda ent: self.__entity_declaration_with(ent,
with_factory_methods=False)
)
]
result = Text()
declarations = list(filter(lambda d: d.lines, declarations))
for ind, decl in enumerate(declarations):
result += decl
if ind != len(declarations) - 1:
result += EMPTY
return result
def _entity_enumeration_declaration(self, entity_enumeration: DivanEntityEnumeration) -> Text:
return Text()
def _entity_enumeration_declaration(self, entity_enumeration: EntityEnumeration) -> Text:
return Text()
def _string_enumeration_declaration(self, string_enumeration: StringEnumeration) -> Text:
def _string_enumeration_declaration(self, string_enumeration: DivanStringEnumeration) -> Text:
return Text()
+4
View File
@@ -35,6 +35,10 @@ def snake_case(string: str) -> str:
return '_'.join(map(lambda component: component.lower(), filter(lambda s: s, name_components(string))))
def capitalize_snake_case(string: str) -> str:
return snake_case(string).capitalize()
def upper_snake_case(string: str) -> str:
return '_'.join(map(lambda component: component.upper(), filter(lambda s: s, name_components(string))))
@@ -1,3 +1,7 @@
{
"lang": "divan"
"lang": "divan",
"header": "@file:Suppress(\n \"unused\",\n \"UNUSED_PARAMETER\",\n)\n\npackage divan\n\nimport com.fasterxml.jackson.annotation.JsonAnyGetter\nimport com.fasterxml.jackson.annotation.JsonIgnore\nimport divan.annotation.Generated\nimport divan.core.Guard\nimport divan.core.Property\nimport divan.core.ReferenceProperty\nimport divan.core.tryPutProperty\nimport divan.core.valueOrNull\nimport divan.scope.DivScope\nimport divan.scope.TemplateScope\nimport kotlin.Any\nimport kotlin.String\nimport kotlin.Suppress\nimport kotlin.collections.List\nimport kotlin.collections.Map\n",
"topLevelAnnotations": [
"@Generated"
]
}