lib/lutaml/xmi/parsers/xml.rb in lutaml-0.9.9 vs lib/lutaml/xmi/parsers/xml.rb in lutaml-0.9.10
- old
+ new
@@ -14,27 +14,71 @@
"0" => "C",
"1" => "M",
}.freeze
attr_reader :xmi_cache, :xmi_root_model
- # @param xml [String] path to xml
- # @param options [Hash] options for parsing
- # @return [Lutaml::Uml::Document]
- def self.parse(xml, _options = {})
- xml_content = File.read(xml)
- xmi_model = Xmi::Sparx::SparxRoot.parse_xml(xml_content)
- new.parse(xmi_model)
+ class << self
+ # @param xml [String] path to xml
+ # @param options [Hash] options for parsing
+ # @return [Lutaml::Uml::Document]
+ def parse(xml, _options = {})
+ xmi_model = get_xmi_model(xml)
+ new.parse(xmi_model)
+ end
+
+ # @param xml [String] path to xml
+ # @return [Hash]
+ def serialize_xmi(xml)
+ xmi_model = get_xmi_model(xml)
+ new.serialize_xmi(xmi_model)
+ end
+
+ # @param xml [String] path to xml
+ # @param name [String]
+ # @return [Hash]
+ def serialize_generalization_by_name(xml, name)
+ xmi_model = get_xmi_model(xml)
+ new.serialize_generalization_by_name(xmi_model, name)
+ end
+
+ private
+
+ # @param xml [String]
+ # @return [Shale::Mapper]
+ def get_xmi_model(xml)
+ Xmi::Sparx::SparxRoot.parse_xml(File.read(xml))
+ end
end
# @param xmi_model [Shale::Mapper]
# @return [Lutaml::Uml::Document]
def parse(xmi_model)
@xmi_cache = {}
@xmi_root_model = xmi_model
- ::Lutaml::Uml::Document.new(serialize_to_hash(xmi_model))
+ serialized_hash = serialize_xmi(xmi_model)
+
+ ::Lutaml::Uml::Document.new(serialized_hash)
end
+ # @param xmi_model [Shale::Mapper]
+ # return [Hash]
+ def serialize_xmi(xmi_model)
+ @xmi_cache = {}
+ @xmi_root_model = xmi_model
+ serialize_to_hash(xmi_model)
+ end
+
+ # @param xmi_model [Shale::Mapper]
+ # @param name [String]
+ # @return [Hash]
+ def serialize_generalization_by_name(xmi_model, name)
+ @xmi_cache = {}
+ @xmi_root_model = xmi_model
+ klass = find_klass_packaged_element_by_name(name)
+ serialize_generalization(klass)
+ end
+
private
# @param xmi_model [Shale::Mapper]
# @return [Hash]
# @note xpath: //uml:Model[@xmi:type="uml:Model"]
@@ -103,10 +147,142 @@
stereotype: doc_node_attribute_value(klass.id, "stereotype"),
}
end
end
+ # @param klass [Shale::Mapper]
+ # # @return [Hash]
+ def serialize_generalization(klass)
+ general_hash, next_general_node_id = get_top_level_general_hash(klass)
+ return general_hash unless next_general_node_id
+
+ general_hash[:general] = serialize_generalization_attributes(
+ next_general_node_id,
+ )
+
+ general_hash
+ end
+
+ # @param klass [Shale::Mapper]
+ # @return [Array<Hash>]
+ def get_top_level_general_hash(klass) # rubocop:disable Metrics/AbcSize
+ general_hash, next_general_node_id = get_general_hash(klass.id)
+ general_hash[:name] = klass.name
+ general_hash[:type] = klass.type
+ general_hash[:definition] = lookup_general_documentation(klass.id)
+ general_hash[:stereotype] = doc_node_attribute_value(
+ klass.id, "stereotype"
+ )
+
+ # update_inherited_attributes(general_hash)
+ # update_gen_attributes(general_hash)
+
+ [general_hash, next_general_node_id]
+ end
+
+ def lookup_general_documentation(klass_id)
+ # lookup_attribute_documentation(klass_id) ||
+ # lookup_element_prop_documentation(klass_id)
+
+ lookup_element_prop_documentation(klass_id)
+ end
+
+ def update_gen_attributes(general_hash)
+ general_hash[:gen_attributes] = serialize_gen_attributes
+ end
+
+ def update_inherited_attributes(general_hash)
+ general_hash[:gml_attributes] = serialize_gml_attributes
+ general_hash[:core_attributes] = serialize_core_attributes
+ end
+
+ # @param xmi_id [String]
+ # @param model [Shale::Mapper]
+ # @return [Array<Hash>]
+ # @note get generalization node and its owned attributes
+ def serialize_generalization_attributes(general_id)
+ general_hash, next_general_node_id = get_general_hash(general_id)
+
+ if next_general_node_id
+ general_hash[:general] = serialize_generalization_attributes(
+ next_general_node_id,
+ )
+ end
+
+ general_hash
+ end
+
+ # @param xmi_id [String]
+ # @return [Shale::Mapper]
+ def get_general_node(xmi_id)
+ find_packaged_element_by_id(xmi_id)
+ end
+
+ # @param general_node [Shale::Mapper]
+ # # @return [Hash]
+ def get_general_attributes(general_node)
+ attrs = serialize_class_attributes(general_node, with_assoc: true)
+ attrs.sort_by { |i| i[:name] }
+ end
+
+ # @param general_node [Shale::Mapper]
+ # @return [String]
+ def get_next_general_node_id(general_node)
+ general_node.generalization.first&.general
+ end
+
+ # @param general_id [String]
+ # @return [Array<Hash>]
+ def get_general_hash(general_id)
+ general_node = get_general_node(general_id)
+ general_node_attrs = get_general_attributes(general_node)
+ general_upper_klass = find_upper_level_packaged_element(general_id)
+ next_general_node_id = get_next_general_node_id(general_node)
+
+ [
+ {
+ general_id: general_id,
+ general_name: general_node.name,
+ general_attributes: general_node_attrs,
+ general_upper_klass: general_upper_klass,
+ general: {},
+ },
+ next_general_node_id,
+ ]
+ end
+
+ # @param id [String]
+ # @return [Shale::Mapper]
+ def find_packaged_element_by_id(id)
+ all_packaged_elements.find { |e| e.id == id }
+ end
+
+ # @param id [String]
+ # @return [Shale::Mapper]
+ def find_upper_level_packaged_element(klass_id)
+ upper_klass = all_packaged_elements.find do |e|
+ e.packaged_element.find { |pe| pe.id == klass_id }
+ end
+ upper_klass&.name
+ end
+
+ # @param name [String]
+ # @return [Shale::Mapper]
+ def find_klass_packaged_element_by_name(name)
+ all_packaged_elements.find do |e|
+ e.type?("uml:Class") && e.name == name
+ end
+ end
+
+ # @param name [String]
+ # @return [Shale::Mapper]
+ def find_packaged_element_by_name(name)
+ all_packaged_elements.find do |e|
+ e.name == name
+ end
+ end
+
# @param package [Shale::Mapper]
# @return [Array<Hash>]
# @note xpath ./packagedElement[@xmi:type="uml:Enumeration"]
def serialize_model_enums(package)
package.packaged_element.select { |e| e.type?("uml:Enumeration") }
@@ -224,13 +400,13 @@
# @param link_id [String]
# @return [Shale::Mapper]
# @note xpath %(//connector[@xmi:idref="#{link_id}"])
def fetch_connector(link_id)
- @xmi_root_model.extension.connectors.connector.select do |con|
+ @xmi_root_model.extension.connectors.connector.find do |con|
con.idref == link_id
- end.first
+ end
end
# @param link_id [String]
# @param node_name [String] source or target
# @return [String]
@@ -426,13 +602,13 @@
# %(//ownedAttribute[@association]/type[@xmi:idref="#{xmi_id}"])
def fetch_owned_attribute_node(xmi_id)
all_elements = all_packaged_elements
owned_attributes = all_elements.map(&:owned_attribute).flatten
- oa = owned_attributes.select do |a|
+ oa = owned_attributes.find do |a|
!!a.association && a.uml_type && a.uml_type.idref == xmi_id
- end.first
+ end
if oa
cardinality = cardinality_min_max_value(
oa.lower_value&.value, oa.upper_value&.value
)
@@ -444,41 +620,108 @@
# @param klass_id [String]
# @return [Shale::Mapper]
# @note xpath %(//element[@xmi:idref="#{klass['xmi:id']}"])
def fetch_element(klass_id)
- @xmi_root_model.extension.elements.element.select do |e|
+ @xmi_root_model.extension.elements.element.find do |e|
e.idref == klass_id
- end.first
+ end
end
# @param klass [Shale::Mapper]
+ # @param with_assoc [Boolean]
# @return [Array<Hash>]
# @note xpath .//ownedAttribute[@xmi:type="uml:Property"]
- def serialize_class_attributes(klass)
+ def serialize_class_attributes(klass, with_assoc: false)
klass.owned_attribute.select { |attr| attr.type?("uml:Property") }
.map do |oa|
- uml_type = oa.uml_type
- uml_type_idref = uml_type.idref if uml_type
+ if with_assoc || oa.association.nil?
+ attrs = build_class_attributes(oa)
- if oa.association.nil?
- {
- id: oa.id,
- name: oa.name,
- type: lookup_entity_name(uml_type_idref) || uml_type_idref,
- xmi_id: uml_type_idref,
- is_derived: oa.is_derived,
- cardinality: cardinality_min_max_value(
- oa.lower_value&.value,
- oa.upper_value&.value,
- ),
- definition: lookup_attribute_documentation(oa.id),
- }
+ if with_assoc && oa.association
+ attrs[:association] = oa.association
+ attrs[:definition] = loopup_assoc_def(oa.association)
+ attrs[:type_ns] = get_ns_by_type(attrs[:type])
+ end
+
+ attrs
end
end.compact
end
+ def loopup_assoc_def(association)
+ connector = fetch_connector(association)
+ connector&.documentation&.value
+ end
+
+ # @return [Array<Hash>]
+ def serialize_gml_attributes
+ element = find_packaged_element_by_name("_Feature")
+ attrs = serialize_class_attributes(element, with_assoc: true)
+ attrs.each { |attr| attr[:upper_klass] = "gml" }
+ end
+
+ # @return [Array<Hash>]
+ def serialize_core_attributes
+ element = find_packaged_element_by_name("_CityObject")
+ attrs = serialize_class_attributes(element, with_assoc: false)
+ attrs.each { |attr| attr[:upper_klass] = "core" }
+ end
+
+ # @return [Array<Hash>]
+ def select_gen_attributes
+ element = find_packaged_element_by_name("gen")
+ gen_attr_element = find_packaged_element_by_name("_genericAttribute")
+
+ element.packaged_element.select do |e|
+ e.type?("uml:Class") &&
+ e.generalization&.first&.general == gen_attr_element.id
+ end
+ end
+
+ # @return [Array<Hash>]
+ def serialize_gen_attributes
+ klasses = select_gen_attributes
+
+ klasses.map do |klass|
+ attr = serialize_class_attributes(klass, with_assoc: false)
+ attr.first[:name] = klass.name
+ attr.first[:type] = "gen:#{klass.name}"
+ attr.first[:upper_klass] = "gen"
+ attr
+ end.flatten!
+ end
+
+ # @param type [String]
+ # @return [String]
+ def get_ns_by_type(type)
+ return unless type
+
+ p = find_klass_packaged_element_by_name(type)
+ find_upper_level_packaged_element(p.id)
+ end
+
+ # @param klass_id [String]
+ # @return [Array<Hash>]
+ def build_class_attributes(owned_attr) # rubocop:disable Metrics/MethodLength
+ uml_type = owned_attr.uml_type
+ uml_type_idref = uml_type.idref if uml_type
+
+ {
+ id: owned_attr.id,
+ name: owned_attr.name,
+ type: lookup_entity_name(uml_type_idref) || uml_type_idref,
+ xmi_id: uml_type_idref,
+ is_derived: owned_attr.is_derived,
+ cardinality: cardinality_min_max_value(
+ owned_attr.lower_value&.value,
+ owned_attr.upper_value&.value,
+ ),
+ definition: lookup_attribute_documentation(owned_attr.id),
+ }
+ end
+
# @param min [String]
# @param max [String]
# @return [Hash]
def cardinality_min_max_value(min, max)
{
@@ -529,9 +772,21 @@
attribute_node = fetch_attribute_node(xmi_id)
return unless attribute_node&.documentation
attribute_node&.documentation&.value
+ end
+
+ # @param xmi_id [String]
+ # @return [String]
+ def lookup_element_prop_documentation(xmi_id)
+ element_node = @xmi_root_model.extension.elements.element.find do |e|
+ e.idref == xmi_id
+ end
+
+ return unless element_node&.properties
+
+ element_node&.properties&.documentation
end
# @param xmi_id [String]
# @return [String]
def lookup_entity_name(xmi_id)