File indexing completed on 2024-06-23 05:16:56
0001 # SPDX-FileCopyrightText: 2021 Daniel Vrátil <dvratil@kde.org> 0002 # 0003 # SPDX-License-Identifier: LGPL-2.1-only 0004 # SPDX-License-Identifier: LGPL-3.0-only 0005 # SPDX-License-Identifier: LicenseRef-KDE-Accepted-LGPL 0006 0007 from typing import Dict, List, Optional, cast 0008 0009 class Type: 0010 """ 0011 Base class for all representation of schema types. 0012 """ 0013 0014 _is_array = False 0015 _is_ref = False 0016 _is_object = False 0017 _is_enum = False 0018 _is_builtin = False 0019 0020 def __init__(self, name): 0021 self._name = name 0022 0023 def __lt__(self, other: "Type") -> bool: 0024 # pylint: disable=no-member 0025 if (isinstance(self, BuiltinType) and cast(BuiltinType, self).is_qt_type) and ( 0026 not isinstance(other, BuiltinType) 0027 or not cast(BuiltinType, other).is_qt_type 0028 ): 0029 return True 0030 if isinstance(self, BuiltinType) and not isinstance(other, BuiltinType): 0031 return True 0032 return self._name < other._name 0033 0034 def rename_to(self, new_name: str) -> None: 0035 self._name = new_name 0036 0037 @classmethod 0038 @property 0039 def is_array(cls) -> bool: 0040 return cls._is_array 0041 0042 @classmethod 0043 @property 0044 def is_ref(cls) -> bool: 0045 return cls._is_ref 0046 0047 @classmethod 0048 @property 0049 def is_object(cls) -> bool: 0050 return cls._is_object 0051 0052 @classmethod 0053 @property 0054 def is_enum(cls) -> bool: 0055 return cls._is_enum 0056 0057 @classmethod 0058 @property 0059 def is_builtin(cls) -> bool: 0060 return cls._is_builtin 0061 0062 @staticmethod 0063 def _map_to_builtin(name: str, orig_ref: "Ref") -> "Optional[Type]": 0064 if name == "Date": 0065 return QDate(orig_ref=orig_ref) 0066 return None 0067 0068 @staticmethod 0069 def parse( 0070 type_schema: Dict, name: str, schema_name: str, ref_visitor: "RefVisitor" 0071 ): 0072 # pylint: disable=too-many-return-statements 0073 if "enum" in type_schema: 0074 return Enum(name, schema_name, type_schema) 0075 if "type" in type_schema: 0076 prop_type = type_schema["type"] 0077 if prop_type == "array": 0078 return QList(type_schema["items"], name, schema_name, ref_visitor) 0079 if prop_type == "boolean": 0080 return Boolean() 0081 if prop_type == "integer": 0082 return Integer() 0083 if prop_type == "object": 0084 if "additionalProperties" in type_schema: 0085 return QVariantMap() 0086 return Object(type_schema, ref_visitor) 0087 if prop_type == "string": 0088 return QString() 0089 raise RuntimeError(f'Unknown type "{prop_type}"') 0090 if "$ref" in type_schema: 0091 builtin = Type._map_to_builtin( 0092 type_schema["$ref"], Ref(type_schema["$ref"], ref_visitor) 0093 ) 0094 return builtin if builtin else Ref(type_schema["$ref"], ref_visitor) 0095 0096 raise RuntimeError(f"Missing type information in schema {type_schema}.") 0097 0098 @property 0099 def name(self) -> str: 0100 return self._name 0101 0102 @property 0103 def full_name(self) -> str: 0104 return self.name 0105 0106 0107 class Property: 0108 """ 0109 Represents a single property of an Object type. 0110 0111 A property has a name, and a type, described by instance of a Type subclass. 0112 """ 0113 0114 def __init__( 0115 self, name: str, prop: Dict, schema_name: str, ref_visitor: "RefVisitor" 0116 ): 0117 self._prop = prop 0118 self._name = name 0119 self._orig_name = name 0120 self._description = prop["description"] 0121 self._read_only = prop.get("readOnly", False) 0122 self._type = Type.parse(self._prop, self._name, schema_name, ref_visitor) 0123 # Avoid some C++ reserved words 0124 if self._name == "default": 0125 self._name = "isDefault" 0126 0127 def __repr__(self) -> str: 0128 return f"{self._name}({self._type.name})" 0129 0130 @property 0131 def name(self) -> str: 0132 return self._name 0133 0134 @property 0135 def capitalized_name(self) -> str: 0136 return self._name[0].upper() + self._name[1:] 0137 0138 @property 0139 def description(self) -> str: 0140 return self._description 0141 0142 @property 0143 def type(self): 0144 return self._type 0145 0146 @property 0147 def read_only(self) -> bool: 0148 return self._read_only 0149 0150 @property 0151 def orig_name(self) -> str: 0152 return self._orig_name 0153 0154 0155 class Object(Type): 0156 """ 0157 Maps to the "object" type as it appears in the schema documents. 0158 An Object type is composed of multiple properties. 0159 """ 0160 0161 _is_object = True 0162 0163 def __init__(self, schema: dict, ref_visitor: "RefVisitor"): 0164 super().__init__(schema["id"]) 0165 assert schema["type"] == "object" 0166 self._description: str = schema["description"] 0167 self._properties = list( 0168 map( 0169 lambda tpl: Property( 0170 tpl[0], tpl[1], schema_name=self._name, ref_visitor=ref_visitor 0171 ), 0172 schema["properties"].items(), 0173 ) 0174 ) 0175 0176 def __repr__(self) -> str: 0177 return f"<{self._name} {{ {','.join(map(str, self._properties))} }}>" 0178 0179 @property 0180 def description(self) -> str: 0181 return self._description 0182 0183 @property 0184 def properties(self) -> List[Property]: 0185 return self._properties 0186 0187 @property 0188 def nested_enums(self) -> "List[Enum]": 0189 enums = [] 0190 for prop in self._properties: 0191 if isinstance(prop.type, Enum): 0192 enums.append(prop.type) 0193 elif prop.type.is_array and isinstance(prop.type.element_type, Enum): 0194 enums.append(prop.type.element_type) 0195 return enums 0196 0197 @property 0198 def dependencies(self) -> List[Type]: 0199 deps = [] 0200 known = set() 0201 0202 def add_dep(dep_type) -> None: 0203 if dep_type.name not in known: 0204 known.add(dep_type.name) 0205 deps.append(dep_type) 0206 0207 for prop in self._properties: 0208 if prop.type.is_array: 0209 add_dep(prop.type) 0210 add_dep(prop.type.element_type) 0211 elif prop.type.is_ref or (prop.type.is_builtin and prop.type.is_qt_type): 0212 add_dep(prop.type) 0213 return sorted(deps) 0214 0215 @property 0216 def hhhhas_primary(self) -> bool: 0217 for prop in self._properties: 0218 if prop.name == "primary": 0219 return True 0220 return False 0221 0222 0223 class EnumValue: 0224 """ 0225 A single value of an enumerator. 0226 """ 0227 0228 def __init__(self, value, name, description): 0229 self._value = value 0230 self._name = name 0231 self._description = description 0232 0233 @property 0234 def value(self) -> str: 0235 return self._value 0236 0237 @property 0238 def name(self) -> str: 0239 return self._name 0240 0241 @property 0242 def description(self) -> str: 0243 return self._description 0244 0245 0246 class Enum(Type): 0247 """ 0248 Represents an enum in a property. 0249 """ 0250 0251 is_enum = True 0252 0253 def __init__(self, name: str, schema_name: str, schema: Dict): 0254 super().__init__(name[0].upper() + name[1:]) 0255 self._schema_name = schema_name 0256 self._values = [] 0257 for i, value_name in enumerate(schema["enum"]): 0258 self._values.append(EnumValue(i, value_name, schema["enumDescriptions"][i])) 0259 0260 @property 0261 def values(self) -> List[EnumValue]: 0262 return self._values 0263 0264 @property 0265 def full_name(self) -> str: 0266 return f"{self._schema_name}::{self._name}" 0267 0268 0269 0270 class Ref(Type): 0271 """ 0272 Ref is a reference to a type (usually an Object) that is defined elsewhere 0273 in the schema. 0274 0275 The referenced type can be accessed through ref_type property. 0276 """ 0277 0278 _is_ref = True 0279 0280 def __init__(self, name: str, ref_visitor: "RefVisitor"): 0281 super().__init__(name) 0282 self._ref_type = None 0283 ref_visitor.add_ref(self) 0284 0285 @property 0286 def ref_type(self) -> Optional[Type]: 0287 return self._ref_type 0288 0289 @property 0290 def has_primary(self) -> bool: 0291 return self._ref_type is not None and self._ref_type.has_primary 0292 0293 0294 class BuiltinType(Type): 0295 """ 0296 Base class for builtin types 0297 0298 Builtin types are types that are built in the C++ language (int, bool) 0299 and, for the purpose of the schema_generator, also any known Qt classes. 0300 """ 0301 0302 _is_builtin: bool = True 0303 _include_name: str 0304 _is_qt_type: bool = False 0305 0306 def __init__(self, name, orig_ref: Ref = None): 0307 super().__init__(name) 0308 self._orig_ref = orig_ref 0309 0310 @classmethod 0311 @property 0312 def include_name(cls) -> str: 0313 return cls._include_name 0314 0315 @classmethod 0316 @property 0317 def is_qt_type(cls) -> bool: 0318 return cls._is_qt_type 0319 0320 @property 0321 def has_primary(self) -> bool: 0322 return False 0323 0324 @property 0325 def orig_ref(self): 0326 return self._orig_ref 0327 0328 0329 class Boolean(BuiltinType): 0330 """Represents a C++ bool type.""" 0331 0332 def __init__(self): 0333 super().__init__("bool") 0334 0335 0336 class Integer(BuiltinType): 0337 """Represents a C++ int type.""" 0338 0339 def __init__(self): 0340 super().__init__("int") 0341 0342 0343 class QList(BuiltinType): 0344 """ 0345 Represents a QList class. 0346 0347 The element_type property holds the type of the elements in the array. 0348 """ 0349 0350 _include_name = "QList" 0351 _is_qt_type = True 0352 _is_array = True 0353 0354 def __init__( 0355 self, 0356 element_type: Dict, 0357 name_hint: str, 0358 schema_name: str, 0359 ref_visitor: "RefVisitor", 0360 ): 0361 super().__init__("QList") 0362 self._element_type = Type.parse( 0363 element_type, name_hint, schema_name, ref_visitor 0364 ) 0365 0366 @property 0367 def element_type(self) -> Type: 0368 return self._element_type 0369 0370 @property 0371 def full_name(self) -> str: 0372 return f"QList<{self._element_type.full_name}>" 0373 0374 0375 class QVariantMap(BuiltinType): 0376 """ 0377 Represents a QVariantMap class. 0378 """ 0379 0380 _include_name = "QVariantMap" 0381 _is_qt_type = True 0382 0383 def __init__(self): 0384 super().__init__("QVariantMap") 0385 0386 0387 class QDate(BuiltinType): 0388 """ 0389 Represents a QDate class. 0390 """ 0391 0392 _include_name = "QDate" 0393 _is_qt_type = True 0394 0395 def __init__(self, orig_ref: Ref = None): 0396 super().__init__("QDate", orig_ref) 0397 0398 0399 class QString(BuiltinType): 0400 """ 0401 Represents a QString class. 0402 0403 This maps a plain string (in the schema) to QString. 0404 """ 0405 0406 _include_name = "QString" 0407 _is_qt_type = True 0408 0409 def __init__(self, orig_ref: Ref = None): 0410 super().__init__("QString", orig_ref)