opencdd 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CLAUDE.md +486 -0
- data/README.md +304 -0
- data/bin/lint-no-raw-mdc +41 -0
- data/lib/cdd.rb +11 -0
- data/lib/opencdd/alias_table.rb +52 -0
- data/lib/opencdd/cddal/ast.rb +151 -0
- data/lib/opencdd/cddal/builder.rb +374 -0
- data/lib/opencdd/cddal/fetcher/in_memory.rb +32 -0
- data/lib/opencdd/cddal/fetcher/net_http.rb +84 -0
- data/lib/opencdd/cddal/fetcher.rb +18 -0
- data/lib/opencdd/cddal/generated_parser.rb +805 -0
- data/lib/opencdd/cddal/lexer.rb +193 -0
- data/lib/opencdd/cddal/parser.rb +19 -0
- data/lib/opencdd/cddal/resolver.rb +100 -0
- data/lib/opencdd/cddal/serializer.rb +210 -0
- data/lib/opencdd/cddal/value_serializer.rb +60 -0
- data/lib/opencdd/cddal.rb +63 -0
- data/lib/opencdd/class_tree.rb +80 -0
- data/lib/opencdd/class_type.rb +33 -0
- data/lib/opencdd/codegen/ts.rb +185 -0
- data/lib/opencdd/codegen.rb +7 -0
- data/lib/opencdd/composition_tree.rb +119 -0
- data/lib/opencdd/condition.rb +120 -0
- data/lib/opencdd/data_type.rb +143 -0
- data/lib/opencdd/database.rb +719 -0
- data/lib/opencdd/effective_properties.rb +119 -0
- data/lib/opencdd/entity/field_reader.rb +141 -0
- data/lib/opencdd/entity/field_registry.rb +99 -0
- data/lib/opencdd/entity/version_history.rb +62 -0
- data/lib/opencdd/entity.rb +255 -0
- data/lib/opencdd/exporters/json.rb +235 -0
- data/lib/opencdd/exporters/mermaid.rb +62 -0
- data/lib/opencdd/exporters/yaml.rb +16 -0
- data/lib/opencdd/exporters.rb +9 -0
- data/lib/opencdd/guid.rb +27 -0
- data/lib/opencdd/instance_rule.rb +70 -0
- data/lib/opencdd/irdi.rb +128 -0
- data/lib/opencdd/klass.rb +230 -0
- data/lib/opencdd/languages.rb +70 -0
- data/lib/opencdd/meta_class.rb +274 -0
- data/lib/opencdd/model/entity_store.rb +85 -0
- data/lib/opencdd/model/yaml_database.rb +49 -0
- data/lib/opencdd/model/yaml_entity.rb +196 -0
- data/lib/opencdd/model.rb +13 -0
- data/lib/opencdd/parcel/csv_reader.rb +35 -0
- data/lib/opencdd/parcel/csv_writer.rb +114 -0
- data/lib/opencdd/parcel/entity_manifest.rb +119 -0
- data/lib/opencdd/parcel/flat_dir_reader.rb +134 -0
- data/lib/opencdd/parcel/layout_detector.rb +115 -0
- data/lib/opencdd/parcel/metadata.rb +112 -0
- data/lib/opencdd/parcel/referenced_irdis.rb +91 -0
- data/lib/opencdd/parcel/scrape_verifier.rb +122 -0
- data/lib/opencdd/parcel/selector.rb +115 -0
- data/lib/opencdd/parcel/sharded_dir_reader.rb +151 -0
- data/lib/opencdd/parcel/sheet.rb +287 -0
- data/lib/opencdd/parcel/sheet_emitter.rb +171 -0
- data/lib/opencdd/parcel/sheet_schema.rb +253 -0
- data/lib/opencdd/parcel/workbook.rb +172 -0
- data/lib/opencdd/parcel/workbook_reader.rb +200 -0
- data/lib/opencdd/parcel/writer.rb +185 -0
- data/lib/opencdd/parcel.rb +175 -0
- data/lib/opencdd/parse_helpers.rb +73 -0
- data/lib/opencdd/property.rb +120 -0
- data/lib/opencdd/property_data_element_type.rb +44 -0
- data/lib/opencdd/property_ids.rb +202 -0
- data/lib/opencdd/reader.rb +103 -0
- data/lib/opencdd/relation.rb +88 -0
- data/lib/opencdd/relation_tree.rb +74 -0
- data/lib/opencdd/relation_type.rb +58 -0
- data/lib/opencdd/structured_values.rb +183 -0
- data/lib/opencdd/unit.rb +16 -0
- data/lib/opencdd/validator/class_reference_rule.rb +77 -0
- data/lib/opencdd/validator/condition_rule.rb +27 -0
- data/lib/opencdd/validator/data_type_rule.rb +26 -0
- data/lib/opencdd/validator/enum_rule.rb +27 -0
- data/lib/opencdd/validator/format_rule.rb +54 -0
- data/lib/opencdd/validator/hierarchy_rule.rb +65 -0
- data/lib/opencdd/validator/irdi_rule.rb +57 -0
- data/lib/opencdd/validator/mandatory_rule.rb +25 -0
- data/lib/opencdd/validator/pattern_rule.rb +26 -0
- data/lib/opencdd/validator/reference_rule.rb +36 -0
- data/lib/opencdd/validator/rule.rb +65 -0
- data/lib/opencdd/validator/runner.rb +101 -0
- data/lib/opencdd/validator/set_rule.rb +41 -0
- data/lib/opencdd/validator/synonym_rule.rb +30 -0
- data/lib/opencdd/validator/type_rule.rb +102 -0
- data/lib/opencdd/validator/uniqueness_rule.rb +32 -0
- data/lib/opencdd/validator.rb +68 -0
- data/lib/opencdd/value_format.rb +71 -0
- data/lib/opencdd/value_list.rb +42 -0
- data/lib/opencdd/value_term.rb +24 -0
- data/lib/opencdd/version.rb +5 -0
- data/lib/opencdd/view_control.rb +10 -0
- data/lib/opencdd/visitor.rb +93 -0
- data/lib/opencdd.rb +57 -0
- metadata +325 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Opencdd
|
|
4
|
+
module Parcel
|
|
5
|
+
META_CLASS_TYPES = Opencdd::MetaClasses::TYPE_BY_META_CLASS
|
|
6
|
+
TYPE_TO_META_CLASS = META_CLASS_TYPES.invert.freeze
|
|
7
|
+
|
|
8
|
+
autoload :Metadata, "opencdd/parcel/metadata"
|
|
9
|
+
autoload :SheetSchema, "opencdd/parcel/sheet_schema"
|
|
10
|
+
autoload :Sheet, "opencdd/parcel/sheet"
|
|
11
|
+
autoload :Workbook, "opencdd/parcel/workbook"
|
|
12
|
+
autoload :WorkbookReader, "opencdd/parcel/workbook_reader"
|
|
13
|
+
autoload :FlatDirReader, "opencdd/parcel/flat_dir_reader"
|
|
14
|
+
autoload :ShardedDirReader, "opencdd/parcel/sharded_dir_reader"
|
|
15
|
+
autoload :LayoutDetector, "opencdd/parcel/layout_detector"
|
|
16
|
+
autoload :EntityManifest, "opencdd/parcel/entity_manifest"
|
|
17
|
+
autoload :ReferencedIrdis, "opencdd/parcel/referenced_irdis"
|
|
18
|
+
autoload :Selector, "opencdd/parcel/selector"
|
|
19
|
+
autoload :SheetEmitter, "opencdd/parcel/sheet_emitter"
|
|
20
|
+
autoload :Writer, "opencdd/parcel/writer"
|
|
21
|
+
autoload :CsvWriter, "opencdd/parcel/csv_writer"
|
|
22
|
+
autoload :CsvReader, "opencdd/parcel/csv_reader"
|
|
23
|
+
autoload :ScrapeVerifier, "opencdd/parcel/scrape_verifier"
|
|
24
|
+
|
|
25
|
+
module_function
|
|
26
|
+
|
|
27
|
+
# Aggregate multiple Parcel-shaped paths into a single Database.
|
|
28
|
+
# The +paths+ array may contain .xlsx files, .xls files, flat .xls
|
|
29
|
+
# directories, or sharded per-class subdirs. Each is read
|
|
30
|
+
# polymorphically via +Opencdd::Reader.detect+ and merged into a
|
|
31
|
+
# single target database.
|
|
32
|
+
#
|
|
33
|
+
# Returns the populated Opencdd::Database.
|
|
34
|
+
def aggregate(*paths)
|
|
35
|
+
paths.flatten
|
|
36
|
+
.map { |p| Opencdd::Database.load(p) }
|
|
37
|
+
.reduce(Opencdd::Database.new) { |acc, db| acc.merge(db) }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Split +database+ into multiple sub-databases partitioned along
|
|
41
|
+
# +by+:
|
|
42
|
+
#
|
|
43
|
+
# :entity_type — one partition per entity type
|
|
44
|
+
# (the legacy .xls layout, one file per type).
|
|
45
|
+
# :root_class — one partition per root class
|
|
46
|
+
# (full tree per parcel, lifted cross-type deps).
|
|
47
|
+
# :each_class — one partition per class
|
|
48
|
+
# (the class + its declared properties / value_lists /
|
|
49
|
+
# relations / units).
|
|
50
|
+
# callable — partition key is +callable.call(entity)+;
|
|
51
|
+
# the result is a +Hash{ key => Database }+.
|
|
52
|
+
#
|
|
53
|
+
# Returns +Hash{ key => Opencdd::Database }+.
|
|
54
|
+
def split(database, by:, lift_dependencies: true)
|
|
55
|
+
partitions = case by
|
|
56
|
+
when :entity_type then split_by_type(database)
|
|
57
|
+
when :root_class then split_by_root_class(database, lift_dependencies: lift_dependencies)
|
|
58
|
+
when :each_class then split_by_each_class(database, lift_dependencies: lift_dependencies)
|
|
59
|
+
when Proc then split_by_callable(database, by)
|
|
60
|
+
else
|
|
61
|
+
raise ArgumentError, "split `by:` must be :entity_type, :root_class, :each_class, or a Proc"
|
|
62
|
+
end
|
|
63
|
+
partitions
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def split_by_type(database)
|
|
67
|
+
database.entities.group_by(&:type).each_with_object({}) do |(type, entities), h|
|
|
68
|
+
h[type] = build_partition(database, entities)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def split_by_root_class(database, lift_dependencies: true)
|
|
73
|
+
roots = database.root_classes
|
|
74
|
+
roots.each_with_object({}) do |root, h|
|
|
75
|
+
tree = collect_root_subtree(database, root)
|
|
76
|
+
entities = lift_dependencies ? lift_for(database, tree) : tree
|
|
77
|
+
h[root.code || root.irdi.to_s] = build_partition(database, entities)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def split_by_each_class(database, lift_dependencies: true)
|
|
82
|
+
database.classes.each_with_object({}) do |klass, h|
|
|
83
|
+
entities = [klass]
|
|
84
|
+
if lift_dependencies
|
|
85
|
+
klass_props = database.properties_of(klass)
|
|
86
|
+
entities.concat(klass_props)
|
|
87
|
+
entities.concat(database.relations_for(domain: klass.irdi))
|
|
88
|
+
klass_props.each do |prop|
|
|
89
|
+
next unless prop.is_a?(Opencdd::Property)
|
|
90
|
+
if prop.unit_irdi
|
|
91
|
+
unit = database.find(prop.unit_irdi)
|
|
92
|
+
entities << unit if unit.is_a?(Opencdd::Unit)
|
|
93
|
+
end
|
|
94
|
+
vl = database.value_list_of(prop)
|
|
95
|
+
entities << vl if vl
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
h[klass.code || klass.irdi.to_s] = build_partition(database, entities)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def split_by_callable(database, callable)
|
|
103
|
+
database.entities.group_by { |e| callable.call(e) }.each_with_object({}) do |(key, entities), h|
|
|
104
|
+
h[key] = build_partition(database, entities)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def collect_root_subtree(database, root)
|
|
109
|
+
collected = [root]
|
|
110
|
+
queue = [root]
|
|
111
|
+
seen = { root.irdi => true }
|
|
112
|
+
until queue.empty?
|
|
113
|
+
current = queue.shift
|
|
114
|
+
children = current.children
|
|
115
|
+
children.each do |c|
|
|
116
|
+
next if seen[c.irdi]
|
|
117
|
+
seen[c.irdi] = true
|
|
118
|
+
collected << c
|
|
119
|
+
queue << c
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
collected
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Lifts cross-type dependencies for the given set of classes:
|
|
126
|
+
# declared properties, value_lists those properties reference,
|
|
127
|
+
# units those properties use, and relations where the class is
|
|
128
|
+
# in domain or codomain. This makes a per-tree partition
|
|
129
|
+
# self-sufficient when written out.
|
|
130
|
+
def lift_for(database, classes)
|
|
131
|
+
entities = classes.dup
|
|
132
|
+
classes.each do |klass|
|
|
133
|
+
database.properties_of(klass).each { |p| entities << p unless entities.include?(p) }
|
|
134
|
+
database.relations_for(domain: klass.irdi).each { |r| entities << r unless entities.include?(r) }
|
|
135
|
+
database.relations_for(codomain: klass.irdi).each { |r| entities << r unless entities.include?(r) }
|
|
136
|
+
database.properties_of(klass).each do |prop|
|
|
137
|
+
next unless prop.is_a?(Opencdd::Property)
|
|
138
|
+
if prop.unit_irdi
|
|
139
|
+
unit = database.find(prop.unit_irdi)
|
|
140
|
+
entities << unit if unit.is_a?(Opencdd::Unit) && !entities.include?(unit)
|
|
141
|
+
end
|
|
142
|
+
vl = database.value_list_of(prop)
|
|
143
|
+
entities << vl if vl && !entities.include?(vl)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
entities.uniq
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def build_partition(_database, entities)
|
|
150
|
+
db = Opencdd::Database.new
|
|
151
|
+
entities.each { |e| db.add_entity(deep_copy_entity(e)) }
|
|
152
|
+
db.finalize!
|
|
153
|
+
db
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Returns a detached copy of +entity+ — same +irdi+, +properties+,
|
|
157
|
+
# +schema+, +meta_class_irdi+, but no +@database+ back-reference and
|
|
158
|
+
# empty Klass-specific state (+@children+, +@declared_property_irdis+).
|
|
159
|
+
#
|
|
160
|
+
# The destination partition Database reattaches the entity and
|
|
161
|
+
# rebuilds class-tree state via +add_entity+ and +finalize!+.
|
|
162
|
+
#
|
|
163
|
+
# We can't use Marshal here because Entity indirectly references its
|
|
164
|
+
# source Database, which uses Hash.new { ... } (a default proc) for
|
|
165
|
+
# its indexes — Marshal refuses to dump hashes with default procs.
|
|
166
|
+
def deep_copy_entity(entity)
|
|
167
|
+
entity.class.new(
|
|
168
|
+
irdi: entity.irdi,
|
|
169
|
+
properties: entity.properties.dup,
|
|
170
|
+
schema: entity.schema,
|
|
171
|
+
meta_class_irdi: entity.meta_class_irdi,
|
|
172
|
+
)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "strscan"
|
|
4
|
+
|
|
5
|
+
# Legacy module. The canonical home for collection parsing is now
|
|
6
|
+
# +Opencdd::StructuredValues+ — see +unwrap_and_split+, +rejoin+,
|
|
7
|
+
# +parse_ref_set+, +parse_synonyms+. ParseHelpers remains as a
|
|
8
|
+
# thin back-compat layer for code that hasn't migrated yet (notably
|
|
9
|
+
# the +Entity+ mixin). Do not add new callers; prefer StructuredValues.
|
|
10
|
+
module Opencdd
|
|
11
|
+
module ParseHelpers
|
|
12
|
+
module_function
|
|
13
|
+
|
|
14
|
+
# Delegates to StructuredValues — single source of truth for
|
|
15
|
+
# brace/paren/comma handling. Whitespace-separated forms are a
|
|
16
|
+
# Parcel-specific quirk preserved here for back-compat.
|
|
17
|
+
def parse_irdi_list(raw)
|
|
18
|
+
return [] if raw.nil? || raw.to_s.strip.empty?
|
|
19
|
+
tokens = Opencdd::StructuredValues.unwrap_and_split(raw)
|
|
20
|
+
# Some Parcel sources use whitespace as the separator inside
|
|
21
|
+
# a single "set" cell. Honour both shapes.
|
|
22
|
+
tokens.flat_map { |t| t.split(/\s+/) }.reject(&:empty?).filter_map do |t|
|
|
23
|
+
Opencdd::IRDI.parse(t)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def parse_string_list(raw)
|
|
28
|
+
Opencdd::StructuredValues.unwrap_and_split(raw)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def parse_synonym_tuples(raw)
|
|
32
|
+
return [] if raw.nil? || raw.to_s.strip.empty?
|
|
33
|
+
Opencdd::StructuredValues.parse_synonyms(raw)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Parses both the {(name,lang),...} structured form and the
|
|
37
|
+
# legacy flat (lang, name, lang, name, ...) form. Kept on
|
|
38
|
+
# ParseHelpers because the flat form isn't a synonym-specific
|
|
39
|
+
# concept — it's an ad-hoc legacy wire shape.
|
|
40
|
+
def parse_pair_list(raw)
|
|
41
|
+
return [] if raw.nil? || raw.to_s.strip.empty?
|
|
42
|
+
s = raw.to_s.strip
|
|
43
|
+
return parse_synonym_tuples(s) if brace_wrapped?(s)
|
|
44
|
+
return [[nil, s]] unless paren_wrapped?(s)
|
|
45
|
+
unwrap_parens(s)
|
|
46
|
+
.split(",")
|
|
47
|
+
.each_slice(2)
|
|
48
|
+
.map { |lang, name| [lang&.strip, name&.strip] }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# ── Thin legacy predicates (kept for back-compat) ──────────
|
|
52
|
+
def unwrap_parens(s)
|
|
53
|
+
s = s.to_s
|
|
54
|
+
paren_wrapped?(s) ? s[1..-2] : s
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def unwrap_delimiters(s)
|
|
58
|
+
s = s.to_s
|
|
59
|
+
return s[1..-2] if paren_wrapped?(s) || brace_wrapped?(s)
|
|
60
|
+
s
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def paren_wrapped?(s)
|
|
64
|
+
s = s.to_s
|
|
65
|
+
s.start_with?("(") && s.end_with?(")")
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def brace_wrapped?(s)
|
|
69
|
+
s = s.to_s
|
|
70
|
+
s.start_with?("{") && s.end_with?("}")
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Opencdd
|
|
4
|
+
class Property < Opencdd::Entity
|
|
5
|
+
# Wire-format aliases for the data_type string. Used by #data_type
|
|
6
|
+
# to canonicalize the raw value before exposing it.
|
|
7
|
+
DATA_TYPE_ALIASES = {
|
|
8
|
+
"STRING_TYPE" => :string,
|
|
9
|
+
"TRANSLATABLE_STRING_TYPE" => :translatable_string,
|
|
10
|
+
"REAL_MEASURE_TYPE" => :real_measure,
|
|
11
|
+
"INTEGER_MEASURE_TYPE" => :integer_measure,
|
|
12
|
+
"INT_MEASURE_TYPE" => :integer_measure,
|
|
13
|
+
"REAL_TYPE" => :real,
|
|
14
|
+
"INTEGER_TYPE" => :integer,
|
|
15
|
+
"INT_TYPE" => :integer,
|
|
16
|
+
"BOOLEAN_TYPE" => :boolean,
|
|
17
|
+
"DATE_TYPE" => :date,
|
|
18
|
+
"DATETIME_TYPE" => :date_time,
|
|
19
|
+
"DATE_TIME_TYPE" => :date_time,
|
|
20
|
+
"TIME_TYPE" => :time,
|
|
21
|
+
"IRDI_TYPE" => :irdi,
|
|
22
|
+
"ICID_STRING" => :irdi,
|
|
23
|
+
"ICID_STRING_TYPE" => :irdi,
|
|
24
|
+
"URL_TYPE" => :url,
|
|
25
|
+
"MIME_TYPE" => :mime,
|
|
26
|
+
"FILE_TYPE" => :file,
|
|
27
|
+
"COMPLEX_TYPE" => :complex,
|
|
28
|
+
}.freeze
|
|
29
|
+
|
|
30
|
+
# ── Pure field reads (value_kind + multilingual resolved from
|
|
31
|
+
# PropertyIds::REGISTRY). Explicit :string override where the
|
|
32
|
+
# original code returned the raw value (REGISTRY kind would
|
|
33
|
+
# otherwise coerce to IRDI etc.). ──────────────────────────
|
|
34
|
+
field :symbol_in_text, "MDC_P025_1", :string
|
|
35
|
+
field :value_format, "MDC_P024", :string
|
|
36
|
+
field :constraint, "MDC_P068", :string
|
|
37
|
+
field :type_classification, "MDC_P033", :string
|
|
38
|
+
field :source_document, "MDC_P006_1", :string
|
|
39
|
+
field :coded_name, "MDC_P018", :string
|
|
40
|
+
field :definition_class_irdi, "MDC_P021"
|
|
41
|
+
field :class_value_assignment, "MDC_P017"
|
|
42
|
+
field :unit_irdi, "MDC_P041"
|
|
43
|
+
field :alternative_unit_irdis, "MDC_P042"
|
|
44
|
+
|
|
45
|
+
# ── Computed fields with block-form readers (synthetic: true).
|
|
46
|
+
# Blocks are evaluated via instance_exec on the entity, so
|
|
47
|
+
# they have access to private helpers without send dispatch.
|
|
48
|
+
# `data_type` is NOT a DSL field — the model's #data_type method
|
|
49
|
+
# returns a symbol (:real, :string, etc.) for callers. The wire-
|
|
50
|
+
# format string ("REAL_TYPE") comes from parsed_data_type.to_s
|
|
51
|
+
# and is emitted explicitly by Exporters::Json#property_node.
|
|
52
|
+
PARSED_DATA_TYPE_BLOCK = lambda {
|
|
53
|
+
Opencdd::DataType.parse_or_string(properties[Opencdd::PropertyIds::MDC_P022])
|
|
54
|
+
}
|
|
55
|
+
DATA_ELEMENT_TYPE_BLOCK = lambda {
|
|
56
|
+
@property_data_element_type ||=
|
|
57
|
+
Opencdd::PropertyDataTypeElement.parse(properties[Opencdd::PropertyIds::MDC_P020])
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
field :parsed_data_type, synthetic: true, &PARSED_DATA_TYPE_BLOCK
|
|
61
|
+
field :data_element_type, synthetic: true, as: "data_element_type", &DATA_ELEMENT_TYPE_BLOCK
|
|
62
|
+
field :property_data_element_type,
|
|
63
|
+
synthetic: true, &DATA_ELEMENT_TYPE_BLOCK
|
|
64
|
+
field :parsed_value_format, synthetic: true do
|
|
65
|
+
Opencdd::ValueFormat.parse(properties[Opencdd::PropertyIds::MDC_P024])
|
|
66
|
+
end
|
|
67
|
+
field :condition_raw, "MDC_P028", synthetic: true do
|
|
68
|
+
properties[Opencdd::PropertyIds::MDC_P028]
|
|
69
|
+
end
|
|
70
|
+
field :condition, synthetic: true do
|
|
71
|
+
@condition ||= Opencdd::Condition.parse(properties[Opencdd::PropertyIds::MDC_P028])
|
|
72
|
+
rescue ArgumentError
|
|
73
|
+
# IEC CDD occasionally stores a bare class-reference set in the
|
|
74
|
+
# condition column (e.g. "{0112/2///62683#ACE132}") instead of
|
|
75
|
+
# a `left OP right` boolean expression. Condition's grammar
|
|
76
|
+
# rejects that shape; treat it as nil rather than crashing the
|
|
77
|
+
# import. Tracked in TODO.full-cdd/17-condition-grammar-fix.md.
|
|
78
|
+
@condition = nil
|
|
79
|
+
end
|
|
80
|
+
field :formula, synthetic: true do
|
|
81
|
+
properties[Opencdd::PropertyIds::MDC_P027_1] ||
|
|
82
|
+
properties[Opencdd::PropertyIds::MDC_P027_2]
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Model-side data_type — returns the canonical symbol form.
|
|
86
|
+
def data_type
|
|
87
|
+
raw = properties[Opencdd::PropertyIds::MDC_P022]
|
|
88
|
+
DATA_TYPE_ALIASES[raw] || raw
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# ── Predicates and methods with arguments stay as regular
|
|
92
|
+
# methods. The DSL is for fields, not behavior. ───────────
|
|
93
|
+
|
|
94
|
+
def class_reference?
|
|
95
|
+
parsed_data_type.is_a?(Opencdd::DataType::ClassReference)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def enum?
|
|
99
|
+
parsed_data_type.is_a?(Opencdd::DataType::EnumStringType) ||
|
|
100
|
+
parsed_data_type.is_a?(Opencdd::DataType::EnumReferenceType)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def conditional?
|
|
104
|
+
det = property_data_element_type
|
|
105
|
+
det && (det.condition? || det.dependent?)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def active_for?(bindings)
|
|
109
|
+
return true unless conditional?
|
|
110
|
+
cond = condition
|
|
111
|
+
cond ? cond.satisfied_by?(bindings) : true
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def attaches_to(*_args)
|
|
115
|
+
raise NotImplementedError,
|
|
116
|
+
"Property#attaches_to is provided by Opencdd::Database; use db.properties_of(klass)"
|
|
117
|
+
end
|
|
118
|
+
alias_method :applies_to, :attaches_to
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Opencdd
|
|
4
|
+
class PropertyDataTypeElement
|
|
5
|
+
VALUES = %w[NON_DEPENDENT_P_DET CONDITION_DET DEPENDENT_P_DET].freeze
|
|
6
|
+
|
|
7
|
+
attr_reader :value
|
|
8
|
+
|
|
9
|
+
def initialize(value)
|
|
10
|
+
v = value.to_s.strip.upcase
|
|
11
|
+
raise ArgumentError, "unknown property data element type: #{value.inspect}" unless VALUES.include?(v)
|
|
12
|
+
@value = v
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def non_dependent? = @value == "NON_DEPENDENT_P_DET"
|
|
16
|
+
def condition? = @value == "CONDITION_DET"
|
|
17
|
+
def dependent? = @value == "DEPENDENT_P_DET"
|
|
18
|
+
|
|
19
|
+
def conditional?
|
|
20
|
+
condition? || dependent?
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def to_s = @value
|
|
24
|
+
def to_sym = @value.downcase.to_sym
|
|
25
|
+
|
|
26
|
+
def ==(other)
|
|
27
|
+
other.is_a?(Opencdd::PropertyDataTypeElement) && @value == other.value
|
|
28
|
+
end
|
|
29
|
+
alias_method :eql?, :==
|
|
30
|
+
|
|
31
|
+
def hash
|
|
32
|
+
@value.hash
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
alias_method :inspect, :to_s
|
|
36
|
+
|
|
37
|
+
def self.parse(raw)
|
|
38
|
+
return nil if raw.nil? || raw.to_s.strip.empty?
|
|
39
|
+
s = raw.to_s.strip.upcase
|
|
40
|
+
return nil unless VALUES.include?(s)
|
|
41
|
+
new(s)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Opencdd
|
|
4
|
+
module PropertyIds
|
|
5
|
+
Entry = Struct.new(:id, :aliases, :applies_to, :multilingual, :value_kind, keyword_init: true)
|
|
6
|
+
|
|
7
|
+
REGISTRY = {
|
|
8
|
+
# ── Identity & version (applies_to: :all) ─────────────────────────
|
|
9
|
+
"MDC_P001" => Entry.new(id: "MDC_P001", aliases: %w[code_generic], applies_to: :all, multilingual: false, value_kind: :string),
|
|
10
|
+
"MDC_P001_1" => Entry.new(id: "MDC_P001_1", aliases: %w[code_dictionary], applies_to: :all, multilingual: false, value_kind: :string),
|
|
11
|
+
"MDC_P001_2" => Entry.new(id: "MDC_P001_2", aliases: %w[code_supplier], applies_to: :all, multilingual: false, value_kind: :string),
|
|
12
|
+
"MDC_P001_5" => Entry.new(id: "MDC_P001_5", aliases: %w[code], applies_to: :all, multilingual: false, value_kind: :string),
|
|
13
|
+
"MDC_P001_6" => Entry.new(id: "MDC_P001_6", aliases: %w[code_property], applies_to: :all, multilingual: false, value_kind: :string),
|
|
14
|
+
"MDC_P001_7" => Entry.new(id: "MDC_P001_7", aliases: %w[code_datatype], applies_to: :all, multilingual: false, value_kind: :string),
|
|
15
|
+
"MDC_P001_8" => Entry.new(id: "MDC_P001_8", aliases: %w[code_document], applies_to: :all, multilingual: false, value_kind: :string),
|
|
16
|
+
"MDC_P001_10" => Entry.new(id: "MDC_P001_10", aliases: %w[code_unit], applies_to: :all, multilingual: false, value_kind: :string),
|
|
17
|
+
"MDC_P001_11" => Entry.new(id: "MDC_P001_11", aliases: %w[code_value_term], applies_to: :all, multilingual: false, value_kind: :string),
|
|
18
|
+
"MDC_P001_12" => Entry.new(id: "MDC_P001_12", aliases: %w[code_value_list], applies_to: :all, multilingual: false, value_kind: :string),
|
|
19
|
+
"MDC_P001_13" => Entry.new(id: "MDC_P001_13", aliases: %w[code_relation], applies_to: :all, multilingual: false, value_kind: :string),
|
|
20
|
+
"EXT_P001" => Entry.new(id: "EXT_P001", aliases: %w[code_view_control], applies_to: :all, multilingual: false, value_kind: :string),
|
|
21
|
+
"MDC_P002_1" => Entry.new(id: "MDC_P002_1", aliases: %w[version], applies_to: :all, multilingual: false, value_kind: :string),
|
|
22
|
+
"MDC_P002_2" => Entry.new(id: "MDC_P002_2", aliases: %w[revision], applies_to: :all, multilingual: false, value_kind: :string),
|
|
23
|
+
"MDC_P003_1" => Entry.new(id: "MDC_P003_1", aliases: %w[original_definition_date], applies_to: :all, multilingual: false, value_kind: :date),
|
|
24
|
+
"MDC_P003_2" => Entry.new(id: "MDC_P003_2", aliases: %w[current_version_date], applies_to: :all, multilingual: false, value_kind: :date),
|
|
25
|
+
"MDC_P003_3" => Entry.new(id: "MDC_P003_3", aliases: %w[current_revision_date], applies_to: :all, multilingual: false, value_kind: :date),
|
|
26
|
+
|
|
27
|
+
# ── Names & definitions (applies_to: :all, mostly multilingual) ──
|
|
28
|
+
"MDC_P004" => Entry.new(id: "MDC_P004", aliases: %w[preferred_name], applies_to: :all, multilingual: true, value_kind: :string),
|
|
29
|
+
"MDC_P004_1" => Entry.new(id: "MDC_P004_1", aliases: %w[preferred_name_localized], applies_to: :all, multilingual: true, value_kind: :string),
|
|
30
|
+
"MDC_P004_2" => Entry.new(id: "MDC_P004_2", aliases: %w[synonymous_names], applies_to: :all, multilingual: true, value_kind: :string),
|
|
31
|
+
"MDC_P004_3" => Entry.new(id: "MDC_P004_3", aliases: %w[short_name_localized], applies_to: :all, multilingual: true, value_kind: :string),
|
|
32
|
+
"MDC_P004_4" => Entry.new(id: "MDC_P004_4", aliases: %w[name_icon], applies_to: :all, multilingual: false, value_kind: :identifier_ref),
|
|
33
|
+
"MDC_P005" => Entry.new(id: "MDC_P005", aliases: %w[short_name], applies_to: :all, multilingual: true, value_kind: :string),
|
|
34
|
+
"MDC_P005_1" => Entry.new(id: "MDC_P005_1", aliases: %w[short_name_variant], applies_to: :all, multilingual: true, value_kind: :string),
|
|
35
|
+
"MDC_P006" => Entry.new(id: "MDC_P006", aliases: %w[definition], applies_to: :all, multilingual: true, value_kind: :string),
|
|
36
|
+
"MDC_P006_1" => Entry.new(id: "MDC_P006_1", aliases: %w[source_document_of_definition], applies_to: :all, multilingual: false, value_kind: :identifier_ref),
|
|
37
|
+
"MDC_P007" => Entry.new(id: "MDC_P007", aliases: %w[synonym], applies_to: :all, multilingual: true, value_kind: :string),
|
|
38
|
+
"MDC_P007_1" => Entry.new(id: "MDC_P007_1", aliases: %w[note_localized], applies_to: :all, multilingual: true, value_kind: :string),
|
|
39
|
+
"MDC_P007_2" => Entry.new(id: "MDC_P007_2", aliases: %w[remark_localized], applies_to: :all, multilingual: true, value_kind: :string),
|
|
40
|
+
"MDC_P008" => Entry.new(id: "MDC_P008", aliases: %w[note], applies_to: :all, multilingual: true, value_kind: :string),
|
|
41
|
+
"MDC_P008_1" => Entry.new(id: "MDC_P008_1", aliases: %w[simplified_drawing], applies_to: :all, multilingual: false, value_kind: :identifier_ref),
|
|
42
|
+
"MDC_P008_2" => Entry.new(id: "MDC_P008_2", aliases: %w[graphics], applies_to: :all, multilingual: false, value_kind: :identifier_ref),
|
|
43
|
+
"MDC_P009" => Entry.new(id: "MDC_P009", aliases: %w[remark], applies_to: :all, multilingual: true, value_kind: :string),
|
|
44
|
+
|
|
45
|
+
# ── Class-level (applies_to: :class) ──────────────────────────────
|
|
46
|
+
"MDC_P010" => Entry.new(id: "MDC_P010", aliases: %w[superclass], applies_to: :class, multilingual: false, value_kind: :identifier_ref),
|
|
47
|
+
"MDC_P010_1" => Entry.new(id: "MDC_P010_1", aliases: %w[superclass_localized known_superclasses], applies_to: :class, multilingual: false, value_kind: :identifier_ref),
|
|
48
|
+
"MDC_P011" => Entry.new(id: "MDC_P011", aliases: %w[class_type], applies_to: :class, multilingual: false, value_kind: :string),
|
|
49
|
+
"MDC_P012" => Entry.new(id: "MDC_P012", aliases: %w[supplier_class_ref], applies_to: :class, multilingual: false, value_kind: :identifier_ref),
|
|
50
|
+
"MDC_P013" => Entry.new(id: "MDC_P013", aliases: %w[is_case_of], applies_to: :class, multilingual: false, value_kind: :set_of_refs),
|
|
51
|
+
"MDC_P014" => Entry.new(id: "MDC_P014", aliases: %w[applicable_properties], applies_to: :class, multilingual: false, value_kind: :set_of_refs),
|
|
52
|
+
"MDC_P014_1" => Entry.new(id: "MDC_P014_1", aliases: %w[known_applicable_properties], applies_to: :class, multilingual: false, value_kind: :set_of_refs),
|
|
53
|
+
"MDC_P014_2" => Entry.new(id: "MDC_P014_2", aliases: %w[visible_properties], applies_to: :class, multilingual: false, value_kind: :set_of_refs),
|
|
54
|
+
"MDC_P015" => Entry.new(id: "MDC_P015", aliases: %w[applicable_types], applies_to: :class, multilingual: false, value_kind: :set_of_refs),
|
|
55
|
+
"MDC_P015_1" => Entry.new(id: "MDC_P015_1", aliases: %w[known_applicable_types], applies_to: :class, multilingual: false, value_kind: :set_of_refs),
|
|
56
|
+
"MDC_P015_2" => Entry.new(id: "MDC_P015_2", aliases: %w[visible_types], applies_to: :class, multilingual: false, value_kind: :set_of_refs),
|
|
57
|
+
"MDC_P016" => Entry.new(id: "MDC_P016", aliases: %w[sub_class_selection], applies_to: :class, multilingual: false, value_kind: :set_of_refs),
|
|
58
|
+
"MDC_P033" => Entry.new(id: "MDC_P033", aliases: %w[type_classification], applies_to: :class, multilingual: false, value_kind: :set_of_refs),
|
|
59
|
+
"MDC_P040" => Entry.new(id: "MDC_P040", aliases: %w[det_classification], applies_to: :property, multilingual: false, value_kind: :string),
|
|
60
|
+
"MDC_P090" => Entry.new(id: "MDC_P090", aliases: %w[imported_properties], applies_to: :class, multilingual: false, value_kind: :set_of_refs),
|
|
61
|
+
"MDC_P091" => Entry.new(id: "MDC_P091", aliases: %w[imported_types], applies_to: :class, multilingual: false, value_kind: :set_of_refs),
|
|
62
|
+
"MDC_P093" => Entry.new(id: "MDC_P093", aliases: %w[imported_documents], applies_to: :class, multilingual: false, value_kind: :set_of_refs),
|
|
63
|
+
"MDC_P094" => Entry.new(id: "MDC_P094", aliases: %w[applicable_documents], applies_to: :class, multilingual: false, value_kind: :set_of_refs),
|
|
64
|
+
"MDC_P094_1" => Entry.new(id: "MDC_P094_1", aliases: %w[known_applicable_documents], applies_to: :class, multilingual: false, value_kind: :set_of_refs),
|
|
65
|
+
"MDC_P094_2" => Entry.new(id: "MDC_P094_2", aliases: %w[visible_documents], applies_to: :class, multilingual: false, value_kind: :set_of_refs),
|
|
66
|
+
|
|
67
|
+
# ── Property-level (applies_to: :property unless noted) ──────────
|
|
68
|
+
"MDC_P017" => Entry.new(id: "MDC_P017", aliases: %w[class_value_assignment], applies_to: :property, multilingual: false, value_kind: :identifier_ref),
|
|
69
|
+
"MDC_P018" => Entry.new(id: "MDC_P018", aliases: %w[coded_name], applies_to: :property, multilingual: false, value_kind: :string),
|
|
70
|
+
"MDC_P018_1" => Entry.new(id: "MDC_P018_1", aliases: %w[value_term_data_type], applies_to: :all, multilingual: false, value_kind: :class_ref),
|
|
71
|
+
"MDC_P020" => Entry.new(id: "MDC_P020", aliases: %w[property_data_element_type property_det], applies_to: :property, multilingual: false, value_kind: :string),
|
|
72
|
+
"MDC_P021" => Entry.new(id: "MDC_P021", aliases: %w[definition_class], applies_to: :property, multilingual: false, value_kind: :identifier_ref),
|
|
73
|
+
"MDC_P022" => Entry.new(id: "MDC_P022", aliases: %w[data_type], applies_to: :property, multilingual: false, value_kind: :class_ref),
|
|
74
|
+
"MDC_P023" => Entry.new(id: "MDC_P023", aliases: %w[unit_structure unit_symbol], applies_to: :property, multilingual: false, value_kind: :identifier_ref),
|
|
75
|
+
"MDC_P023_1" => Entry.new(id: "MDC_P023_1", aliases: %w[unit_in_text], applies_to: :property, multilingual: false, value_kind: :identifier_ref),
|
|
76
|
+
"MDC_P023_2" => Entry.new(id: "MDC_P023_2", aliases: %w[unit_in_sgml], applies_to: :property, multilingual: false, value_kind: :identifier_ref),
|
|
77
|
+
"MDC_P024" => Entry.new(id: "MDC_P024", aliases: %w[value_format], applies_to: :property, multilingual: false, value_kind: :string),
|
|
78
|
+
"MDC_P025_1" => Entry.new(id: "MDC_P025_1", aliases: %w[symbol preferred_symbol_text symbol_in_text], applies_to: :property, multilingual: false, value_kind: :string),
|
|
79
|
+
"MDC_P025_2" => Entry.new(id: "MDC_P025_2", aliases: %w[preferred_symbol_sgml], applies_to: :property, multilingual: false, value_kind: :string),
|
|
80
|
+
"MDC_P025_3" => Entry.new(id: "MDC_P025_3", aliases: %w[synonymous_symbol], applies_to: :property, multilingual: false, value_kind: :string),
|
|
81
|
+
"MDC_P026" => Entry.new(id: "MDC_P026", aliases: [], applies_to: :property, multilingual: false, value_kind: :string),
|
|
82
|
+
"MDC_P026_1" => Entry.new(id: "MDC_P026_1", aliases: [], applies_to: :property, multilingual: false, value_kind: :string),
|
|
83
|
+
"MDC_P027_1" => Entry.new(id: "MDC_P027_1", aliases: %w[formula_text property_formula], applies_to: :property, multilingual: false, value_kind: :string),
|
|
84
|
+
"MDC_P027_2" => Entry.new(id: "MDC_P027_2", aliases: %w[formula_sgml property_formula_localized], applies_to: :property, multilingual: false, value_kind: :string),
|
|
85
|
+
"MDC_P028" => Entry.new(id: "MDC_P028", aliases: %w[condition], applies_to: :property, multilingual: false, value_kind: :condition),
|
|
86
|
+
"MDC_P030" => Entry.new(id: "MDC_P030", aliases: [], applies_to: :property, multilingual: false, value_kind: :identifier_ref),
|
|
87
|
+
"MDC_P031" => Entry.new(id: "MDC_P031", aliases: [], applies_to: :property, multilingual: false, value_kind: :set_of_refs),
|
|
88
|
+
"MDC_P032" => Entry.new(id: "MDC_P032", aliases: [], applies_to: :property, multilingual: false, value_kind: :string),
|
|
89
|
+
"MDC_P041" => Entry.new(id: "MDC_P041", aliases: %w[unit unit_irdi codefor_unit], applies_to: :property, multilingual: false, value_kind: :identifier_ref),
|
|
90
|
+
"MDC_P042" => Entry.new(id: "MDC_P042", aliases: %w[alternative_units codefor_alt_unit], applies_to: :property, multilingual: false, value_kind: :set_of_refs),
|
|
91
|
+
"MDC_P043" => Entry.new(id: "MDC_P043", aliases: %w[enumerated_terms term_irdis], applies_to: :value_list, multilingual: false, value_kind: :set_of_refs),
|
|
92
|
+
"MDC_P044" => Entry.new(id: "MDC_P044", aliases: %w[enumerated_values code_list], applies_to: :value_list, multilingual: false, value_kind: :set_of_refs),
|
|
93
|
+
"MDC_P045" => Entry.new(id: "MDC_P045", aliases: %w[selection_count], applies_to: :value_list, multilingual: false, value_kind: :string),
|
|
94
|
+
"MDC_P046" => Entry.new(id: "MDC_P046", aliases: %w[list_type], applies_to: :value_list, multilingual: false, value_kind: :string),
|
|
95
|
+
"MDC_P068" => Entry.new(id: "MDC_P068", aliases: %w[constraint property_constraint], applies_to: :property, multilingual: false, value_kind: :string),
|
|
96
|
+
"MDC_P096" => Entry.new(id: "MDC_P096", aliases: %w[property_classification], applies_to: :property, multilingual: false, value_kind: :string),
|
|
97
|
+
"MDC_P097" => Entry.new(id: "MDC_P097", aliases: %w[requirement], applies_to: :property, multilingual: false, value_kind: :string),
|
|
98
|
+
"MDC_P101" => Entry.new(id: "MDC_P101", aliases: %w[alternate_id], applies_to: :all, multilingual: false, value_kind: :string),
|
|
99
|
+
"MDC_P102" => Entry.new(id: "MDC_P102", aliases: %w[alternate_class_id], applies_to: :all, multilingual: false, value_kind: :string),
|
|
100
|
+
"MDC_P110" => Entry.new(id: "MDC_P110", aliases: %w[super_property], applies_to: :property, multilingual: false, value_kind: :identifier_ref),
|
|
101
|
+
"MDC_P111" => Entry.new(id: "MDC_P111", aliases: %w[alternative_units_list], applies_to: :property, multilingual: false, value_kind: :set_of_refs),
|
|
102
|
+
"MDC_P112" => Entry.new(id: "MDC_P112", aliases: %w[description], applies_to: :all, multilingual: true, value_kind: :string),
|
|
103
|
+
"MDC_P113" => Entry.new(id: "MDC_P113", aliases: %w[example], applies_to: :all, multilingual: false, value_kind: :string),
|
|
104
|
+
"MDC_P114" => Entry.new(id: "MDC_P114", aliases: %w[quantity], applies_to: :property, multilingual: false, value_kind: :identifier_ref),
|
|
105
|
+
|
|
106
|
+
# ── Relation-level (applies_to: :relation) ────────────────────────
|
|
107
|
+
"MDC_P200" => Entry.new(id: "MDC_P200", aliases: %w[relation_type], applies_to: :relation, multilingual: false, value_kind: :string),
|
|
108
|
+
"MDC_P201" => Entry.new(id: "MDC_P201", aliases: %w[domain domain_of_relation], applies_to: :relation, multilingual: false, value_kind: :set_of_refs),
|
|
109
|
+
"MDC_P202" => Entry.new(id: "MDC_P202", aliases: %w[domain_of_function], applies_to: :relation, multilingual: false, value_kind: :set_of_refs),
|
|
110
|
+
"MDC_P203" => Entry.new(id: "MDC_P203", aliases: %w[codomain codomain_of_function], applies_to: :relation, multilingual: false, value_kind: :identifier_ref),
|
|
111
|
+
"MDC_P204" => Entry.new(id: "MDC_P204", aliases: %w[formula relation_formula], applies_to: :relation, multilingual: false, value_kind: :string),
|
|
112
|
+
"MDC_P205" => Entry.new(id: "MDC_P205", aliases: %w[formula_language], applies_to: :relation, multilingual: false, value_kind: :string),
|
|
113
|
+
"MDC_P206" => Entry.new(id: "MDC_P206", aliases: %w[external_solver], applies_to: :relation, multilingual: false, value_kind: :string),
|
|
114
|
+
"MDC_P207" => Entry.new(id: "MDC_P207", aliases: %w[trigger_event], applies_to: :relation, multilingual: false, value_kind: :string),
|
|
115
|
+
"MDC_P208" => Entry.new(id: "MDC_P208", aliases: %w[domain_element_type], applies_to: :relation, multilingual: false, value_kind: :string),
|
|
116
|
+
"MDC_P209" => Entry.new(id: "MDC_P209", aliases: %w[codomain_element_type], applies_to: :relation, multilingual: false, value_kind: :string),
|
|
117
|
+
"MDC_P210" => Entry.new(id: "MDC_P210", aliases: %w[role], applies_to: :relation, multilingual: false, value_kind: :string),
|
|
118
|
+
"MDC_P211" => Entry.new(id: "MDC_P211", aliases: %w[segment], applies_to: :relation, multilingual: false, value_kind: :string),
|
|
119
|
+
"MDC_P212" => Entry.new(id: "MDC_P212", aliases: %w[super_relation_irdi super_relation], applies_to: :relation, multilingual: false, value_kind: :identifier_ref),
|
|
120
|
+
"MDC_P230" => Entry.new(id: "MDC_P230", aliases: %w[applicable_relations], applies_to: :relation, multilingual: false, value_kind: :set_of_refs),
|
|
121
|
+
"MDC_P231" => Entry.new(id: "MDC_P231", aliases: %w[applicable_terms], applies_to: :relation, multilingual: false, value_kind: :set_of_refs),
|
|
122
|
+
|
|
123
|
+
# ── CIM extension ─────────────────────────────────────────────────
|
|
124
|
+
"CIM_P001" => Entry.new(id: "CIM_P001", aliases: %w[cim_uml_id], applies_to: :all, multilingual: false, value_kind: :string),
|
|
125
|
+
"CIM_P002" => Entry.new(id: "CIM_P002", aliases: %w[cim_package], applies_to: :all, multilingual: false, value_kind: :string),
|
|
126
|
+
"CIM_P003" => Entry.new(id: "CIM_P003", aliases: %w[cim_property_type], applies_to: :all, multilingual: false, value_kind: :string),
|
|
127
|
+
"CIM_P004" => Entry.new(id: "CIM_P004", aliases: %w[cim_basic_datatype], applies_to: :all, multilingual: false, value_kind: :string),
|
|
128
|
+
"CIM_P005" => Entry.new(id: "CIM_P005", aliases: %w[cim_multiplicity], applies_to: :all, multilingual: false, value_kind: :string),
|
|
129
|
+
|
|
130
|
+
# ── Global (applies_to: :all) ─────────────────────────────────────
|
|
131
|
+
"MDC_P066" => Entry.new(id: "MDC_P066", aliases: %w[data_object_identifier], applies_to: :all, multilingual: false, value_kind: :string),
|
|
132
|
+
"MDC_P067" => Entry.new(id: "MDC_P067", aliases: %w[time_stamp], applies_to: :all, multilingual: false, value_kind: :date_time),
|
|
133
|
+
|
|
134
|
+
# ── View-control extension ────────────────────────────────────────
|
|
135
|
+
"EXT_P002" => Entry.new(id: "EXT_P002", aliases: %w[controlled_classes], applies_to: :view_control, multilingual: false, value_kind: :set_of_refs),
|
|
136
|
+
"EXT_P003" => Entry.new(id: "EXT_P003", aliases: %w[shown_properties], applies_to: :view_control, multilingual: false, value_kind: :set_of_refs),
|
|
137
|
+
}.freeze
|
|
138
|
+
|
|
139
|
+
# PARCEL_VARIANT_TO_CANONICAL moved to
|
|
140
|
+
# Opencdd::Parcel::SheetSchema::VARIANT_TO_CANONICAL (format-
|
|
141
|
+
# specific mapping belongs in the format module, not the
|
|
142
|
+
# ontology registry). Kept here as an alias for back-compat.
|
|
143
|
+
PARCEL_VARIANT_TO_CANONICAL = Opencdd::Parcel::SheetSchema::VARIANT_TO_CANONICAL
|
|
144
|
+
|
|
145
|
+
REGISTRY.each_key do |id|
|
|
146
|
+
const_set(id, id.freeze) unless const_defined?(id, false)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def self.entry(id)
|
|
150
|
+
REGISTRY[id.to_s]
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def self.canonical_id(name)
|
|
154
|
+
key = name.to_s
|
|
155
|
+
return key if REGISTRY.key?(key)
|
|
156
|
+
alias_map[key]
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def self.multilingual?(id)
|
|
160
|
+
e = entry(id)
|
|
161
|
+
e ? e.multilingual : false
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def self.applies_to(id)
|
|
165
|
+
e = entry(id)
|
|
166
|
+
e ? e.applies_to : nil
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def self.value_kind(id)
|
|
170
|
+
e = entry(id)
|
|
171
|
+
e ? e.value_kind : nil
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def self.all_ids
|
|
175
|
+
@all_ids ||= REGISTRY.keys.freeze
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def self.alias_map
|
|
179
|
+
@alias_map ||= REGISTRY.each_with_object({}) do |(_id, e), h|
|
|
180
|
+
e.aliases.each { |a| h[a] = e.id }
|
|
181
|
+
end.freeze
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def self.normalize(raw_id)
|
|
185
|
+
return nil if raw_id.nil?
|
|
186
|
+
s = raw_id.to_s.strip
|
|
187
|
+
return nil if s.empty?
|
|
188
|
+
match = s.match(/\.(?<lang>[A-Za-z0-9-]+)\z/)
|
|
189
|
+
base = match ? match.pre_match : s
|
|
190
|
+
lang = match && match[:lang]
|
|
191
|
+
canonical = canonical_id(base) || base
|
|
192
|
+
lang ? "#{canonical}.#{lang}" : canonical
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Back-compat delegate. The canonical implementation lives at
|
|
196
|
+
# Opencdd::Parcel::SheetSchema.canonical_id (format-specific
|
|
197
|
+
# concern, not ontology).
|
|
198
|
+
def self.canonical_parcel_id(raw_id)
|
|
199
|
+
Opencdd::Parcel::SheetSchema.canonical_id(raw_id)
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
end
|