xmi 0.3.21 → 0.5.0
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 +4 -4
- data/.github/workflows/release.yml +13 -6
- data/.gitignore +2 -1
- data/.rubocop.yml +12 -13
- data/.rubocop_todo.yml +150 -13
- data/CHANGELOG.md +55 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +10 -0
- data/README.adoc +319 -6
- data/benchmark_parse.rb +60 -0
- data/docs/migration.md +141 -0
- data/docs/versioning.md +255 -0
- data/lib/xmi/add.rb +14 -38
- data/lib/xmi/{the_custom_profile.rb → custom_profile.rb} +25 -25
- data/lib/xmi/delete.rb +14 -38
- data/lib/xmi/difference.rb +14 -38
- data/lib/xmi/documentation.rb +16 -101
- data/lib/xmi/ea_root.rb +114 -33
- data/lib/xmi/extension.rb +6 -6
- data/lib/xmi/namespace/dynamic.rb +28 -0
- data/lib/xmi/namespace/omg.rb +81 -0
- data/lib/xmi/namespace/sparx.rb +39 -0
- data/lib/xmi/namespace.rb +9 -0
- data/lib/xmi/namespace_detector.rb +138 -0
- data/lib/xmi/namespace_registry.rb +119 -0
- data/lib/xmi/parsing.rb +113 -0
- data/lib/xmi/replace.rb +14 -38
- data/lib/xmi/root.rb +49 -213
- data/lib/xmi/sparx/connector.rb +241 -0
- data/lib/xmi/sparx/custom_profile.rb +19 -0
- data/lib/xmi/sparx/diagram.rb +97 -0
- data/lib/xmi/sparx/ea_stub.rb +20 -0
- data/lib/xmi/{extensions/eauml.rb → sparx/ea_uml.rb} +3 -2
- data/lib/xmi/sparx/element.rb +453 -0
- data/lib/xmi/sparx/extension.rb +43 -0
- data/lib/xmi/{extensions → sparx}/gml.rb +9 -3
- data/lib/xmi/sparx/mappings/base_mapping.rb +182 -0
- data/lib/xmi/sparx/mappings.rb +10 -0
- data/lib/xmi/sparx/primitive_type.rb +18 -0
- data/lib/xmi/sparx/root.rb +60 -0
- data/lib/xmi/sparx/sys_ph_s.rb +18 -0
- data/lib/xmi/sparx.rb +17 -1376
- data/lib/xmi/type.rb +37 -0
- data/lib/xmi/uml.rb +191 -469
- data/lib/xmi/v20110701.rb +81 -0
- data/lib/xmi/v20131001.rb +68 -0
- data/lib/xmi/v20161101.rb +61 -0
- data/lib/xmi/version.rb +1 -1
- data/lib/xmi/version_registry.rb +164 -0
- data/lib/xmi/versioned.rb +142 -0
- data/lib/xmi.rb +83 -11
- data/scripts-xmi-profile/profile_xmi_simple.rb +213 -0
- data/xmi.gemspec +3 -9
- metadata +38 -77
data/README.adoc
CHANGED
|
@@ -6,6 +6,13 @@ This Ruby object mapper is a module designed to convert XMI (XML Metadata Interc
|
|
|
6
6
|
|
|
7
7
|
== Installation
|
|
8
8
|
|
|
9
|
+
=== Requirements
|
|
10
|
+
|
|
11
|
+
* Ruby 3.0 or later
|
|
12
|
+
* lutaml-model 0.8.0 or later (for `Lutaml::Xml::Namespace` support)
|
|
13
|
+
|
|
14
|
+
=== Installing the gem
|
|
15
|
+
|
|
9
16
|
Add this line to your application's Gemfile:
|
|
10
17
|
|
|
11
18
|
[source,ruby]
|
|
@@ -37,7 +44,7 @@ To convert XMI file into Ruby objects, run:
|
|
|
37
44
|
----
|
|
38
45
|
xml = "path/to/your/file.xmi"
|
|
39
46
|
xml_content = File.read(xml)
|
|
40
|
-
xmi_root_model = Xmi::Sparx::
|
|
47
|
+
xmi_root_model = Xmi::Sparx::Root.parse_xml(xml_content)
|
|
41
48
|
----
|
|
42
49
|
|
|
43
50
|
This method takes the path to an XMI file and generate the Ruby objects.
|
|
@@ -52,12 +59,12 @@ Xmi::EaRoot.load_extension("path/to/your/extension.xml")
|
|
|
52
59
|
|
|
53
60
|
xml = "path/to/your/file.xmi"
|
|
54
61
|
xml_content = File.read(xml)
|
|
55
|
-
xmi_root_model = Xmi::Sparx::
|
|
62
|
+
xmi_root_model = Xmi::Sparx::Root.parse_xml(xml_content)
|
|
56
63
|
----
|
|
57
64
|
|
|
58
65
|
`Xmi::EaRoot.load_extension` takes the path to an XML file and generate the
|
|
59
66
|
Ruby classes and modules defined in XML file dynamically.
|
|
60
|
-
Then, you can generate Ruby objects by `Xmi::Sparx::
|
|
67
|
+
Then, you can generate Ruby objects by `Xmi::Sparx::Root.parse_xml`.
|
|
61
68
|
|
|
62
69
|
=== Output Classes and Modules Generated from Extension into Ruby Files
|
|
63
70
|
|
|
@@ -154,14 +161,320 @@ module Xmi
|
|
|
154
161
|
end
|
|
155
162
|
----
|
|
156
163
|
|
|
164
|
+
== Namespace Architecture
|
|
165
|
+
|
|
166
|
+
=== General
|
|
167
|
+
|
|
168
|
+
The XMI library normalizes all input namespace versions to a canonical version
|
|
169
|
+
(20131001) before parsing. This allows the library to handle XMI files with
|
|
170
|
+
different namespace versions (2011, 2013, 2016) using a single set of model
|
|
171
|
+
classes.
|
|
172
|
+
|
|
173
|
+
=== Namespace Normalization
|
|
174
|
+
|
|
175
|
+
The normalization is performed by [`SparxRoot.replace_xmlns`](lib/xmi/sparx.rb:1158) which rewrites
|
|
176
|
+
namespace URIs in the input XML:
|
|
177
|
+
|
|
178
|
+
.Example of namespace normalization
|
|
179
|
+
====
|
|
180
|
+
[source,xml]
|
|
181
|
+
----
|
|
182
|
+
<!-- Input XMI with various namespace versions -->
|
|
183
|
+
<xmi:XMI xmlns:xmi="http://www.omg.org/spec/XMI/20110701"
|
|
184
|
+
xmlns:uml="http://www.omg.org/spec/UML/20161101">
|
|
185
|
+
<!-- content -->
|
|
186
|
+
</xmi:XMI>
|
|
187
|
+
|
|
188
|
+
<!-- After normalization, becomes -->
|
|
189
|
+
<xmi:XMI xmlns:xmi="http://www.omg.org/spec/XMI/20131001"
|
|
190
|
+
xmlns:uml="http://www.omg.org/spec/UML/20131001">
|
|
191
|
+
<!-- content -->
|
|
192
|
+
</xmi:XMI>
|
|
193
|
+
----
|
|
194
|
+
====
|
|
195
|
+
|
|
196
|
+
=== Namespace Classes
|
|
197
|
+
|
|
198
|
+
Namespace classes are defined in [`lib/xmi/namespace/omg.rb`](lib/xmi/namespace/omg.rb:1):
|
|
199
|
+
|
|
200
|
+
* **Version-specific classes**: `Xmi20110701`, `Uml20131001`, etc.
|
|
201
|
+
* **Version-agnostic aliases**: `Xmi`, `Uml`, `UmlDi`, `UmlDc`
|
|
202
|
+
|
|
203
|
+
The aliases inherit from the 20131001 versions (the normalized version):
|
|
204
|
+
|
|
205
|
+
.Alias class definitions
|
|
206
|
+
====
|
|
207
|
+
[source,ruby]
|
|
208
|
+
----
|
|
209
|
+
class Xmi < Lutaml::Xml::Namespace
|
|
210
|
+
uri "http://www.omg.org/spec/XMI/20131001"
|
|
211
|
+
prefix_default "xmi"
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
class Uml < Lutaml::Xml::Namespace
|
|
215
|
+
uri "http://www.omg.org/spec/UML/20131001"
|
|
216
|
+
prefix_default "uml"
|
|
217
|
+
end
|
|
218
|
+
----
|
|
219
|
+
====
|
|
220
|
+
|
|
221
|
+
This allows models to use clean namespace references without worrying about
|
|
222
|
+
specific versions:
|
|
223
|
+
|
|
224
|
+
.Using namespace aliases in model definitions
|
|
225
|
+
====
|
|
226
|
+
[source,ruby]
|
|
227
|
+
----
|
|
228
|
+
class MyModel < Lutaml::Model::Serializable
|
|
229
|
+
xml do
|
|
230
|
+
root "Model"
|
|
231
|
+
namespace ::Xmi::Namespace::Omg::Uml
|
|
232
|
+
namespace_scope [
|
|
233
|
+
::Xmi::Namespace::Omg::Xmi,
|
|
234
|
+
::Xmi::Namespace::Omg::Uml,
|
|
235
|
+
::Xmi::Namespace::Omg::UmlDi,
|
|
236
|
+
]
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
----
|
|
240
|
+
====
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
=== Namespace-Qualified Mapping Declaration
|
|
244
|
+
|
|
245
|
+
All element and attribute mappings explicitly declare their namespace to ensure
|
|
246
|
+
proper XML parsing and serialization. This is required even when types have
|
|
247
|
+
`xml_namespace` declared.
|
|
248
|
+
|
|
249
|
+
.Element mapping with namespace
|
|
250
|
+
====
|
|
251
|
+
[source,ruby]
|
|
252
|
+
----
|
|
253
|
+
xml do
|
|
254
|
+
root "Model"
|
|
255
|
+
namespace ::Xmi::Namespace::Omg::Uml
|
|
256
|
+
|
|
257
|
+
# Element mapping with explicit namespace
|
|
258
|
+
map_element "packagedElement", to: :packaged_element,
|
|
259
|
+
namespace: "http://www.omg.org/spec/UML/20131001",
|
|
260
|
+
prefix: "uml"
|
|
261
|
+
end
|
|
262
|
+
----
|
|
263
|
+
====
|
|
264
|
+
|
|
265
|
+
=== Attribute Namespace Handling
|
|
266
|
+
|
|
267
|
+
For attributes with namespace prefixes in XML (e.g., `<uml:Model xmi:id="...">`),
|
|
268
|
+
both `namespace:` and `prefix:` parameters are required in `map_attribute`, even
|
|
269
|
+
when the attribute type has `xml_namespace` declared.
|
|
270
|
+
|
|
271
|
+
.Attribute mapping with namespace prefix
|
|
272
|
+
====
|
|
273
|
+
[source,ruby]
|
|
274
|
+
----
|
|
275
|
+
xml do
|
|
276
|
+
root "Model"
|
|
277
|
+
namespace ::Xmi::Namespace::Omg::Uml
|
|
278
|
+
|
|
279
|
+
# XMI-typed attribute with explicit namespace declaration
|
|
280
|
+
map_attribute "id", to: :id,
|
|
281
|
+
namespace: "http://www.omg.org/spec/XMI/20131001",
|
|
282
|
+
prefix: "xmi"
|
|
283
|
+
|
|
284
|
+
# Regular attribute without namespace prefix
|
|
285
|
+
map_attribute "name", to: :name
|
|
286
|
+
end
|
|
287
|
+
----
|
|
288
|
+
|
|
289
|
+
Without explicit namespace declarations in `map_attribute`, attributes with
|
|
290
|
+
namespace prefixes will parse as `nil`.
|
|
291
|
+
====
|
|
292
|
+
|
|
293
|
+
=== Sparx Systems Namespaces
|
|
294
|
+
|
|
295
|
+
Sparx-specific namespaces are defined in [`lib/xmi/namespace/sparx.rb`](lib/xmi/namespace/sparx.rb:1):
|
|
296
|
+
|
|
297
|
+
* **SysPhS** - System Physical Systems profile
|
|
298
|
+
* **GML** - Geography Markup Language profile
|
|
299
|
+
* **EaUml** - Enterprise Architect UML extensions
|
|
300
|
+
* **CustomProfile** - Custom profile support
|
|
301
|
+
* **CityGML** - City Geography Markup Language
|
|
302
|
+
|
|
303
|
+
.Using Sparx namespaces
|
|
304
|
+
====
|
|
305
|
+
[source,ruby]
|
|
306
|
+
----
|
|
307
|
+
xml do
|
|
308
|
+
root "ModelicaParameter"
|
|
309
|
+
namespace ::Xmi::Namespace::Sparx::SysPhS
|
|
310
|
+
|
|
311
|
+
map_attribute "base_Package", to: :base_package
|
|
312
|
+
map_attribute "name", to: :name
|
|
313
|
+
end
|
|
314
|
+
----
|
|
315
|
+
====
|
|
316
|
+
|
|
317
|
+
=== Extension Namespaces
|
|
318
|
+
|
|
319
|
+
Dynamically loaded extensions (via [`EaRoot.load_extension`](lib/xmi/ea_root.rb:54)) also use
|
|
320
|
+
namespace-qualified mappings:
|
|
321
|
+
|
|
322
|
+
.Extension with namespace mapping
|
|
323
|
+
====
|
|
324
|
+
[source,ruby]
|
|
325
|
+
----
|
|
326
|
+
map_element "ApplicationSchema", to: :gml_application_schema,
|
|
327
|
+
namespace: "http://www.sparxsystems.com/profiles/GML/1.0",
|
|
328
|
+
prefix: "GML"
|
|
329
|
+
----
|
|
330
|
+
====
|
|
331
|
+
|
|
157
332
|
=== Limitation
|
|
158
333
|
|
|
159
334
|
This module is designed to work with XMI files generated by Enterprise
|
|
160
335
|
Architect. It may not work with other XMI files.
|
|
161
336
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
337
|
+
== Version-Aware Parsing
|
|
338
|
+
|
|
339
|
+
The XMI gem supports parsing XMI files from different versions (2.1, 2.5.1, 2.5.2) without preprocessing normalization.
|
|
340
|
+
|
|
341
|
+
=== Automatic Version Detection
|
|
342
|
+
|
|
343
|
+
[source,ruby]
|
|
344
|
+
----
|
|
345
|
+
require 'xmi'
|
|
346
|
+
|
|
347
|
+
# Parse with automatic version detection
|
|
348
|
+
doc = Xmi.parse(xml_content)
|
|
349
|
+
|
|
350
|
+
# Get version information without parsing
|
|
351
|
+
info = Xmi::Parsing.detect_version(xml_content)
|
|
352
|
+
puts info[:xmi_version] # => "20131001"
|
|
353
|
+
puts info[:uml_version] # => "20131001"
|
|
354
|
+
----
|
|
355
|
+
|
|
356
|
+
=== Explicit Version Specification
|
|
357
|
+
|
|
358
|
+
[source,ruby]
|
|
359
|
+
----
|
|
360
|
+
# Parse with explicit version
|
|
361
|
+
doc = Xmi.parse_with_version(xml_content, "20131001")
|
|
362
|
+
|
|
363
|
+
# Check if version is supported
|
|
364
|
+
Xmi::Parsing.version_supported?("20131001") # => true
|
|
365
|
+
Xmi::Parsing.supported_versions # => ["20110701", "20131001", "20161101"]
|
|
366
|
+
----
|
|
367
|
+
|
|
368
|
+
=== Supported Versions
|
|
369
|
+
|
|
370
|
+
| Version | Date | XMI Namespace |
|
|
371
|
+
|---------|------|---------------|
|
|
372
|
+
| XMI 2.1 | 20110701 | `http://www.omg.org/spec/XMI/20110701` |
|
|
373
|
+
| XMI 2.5.1 | 20131001 | `http://www.omg.org/spec/XMI/20131001` |
|
|
374
|
+
| XMI 2.5.2 | 20161101 | `http://www.omg.org/spec/XMI/20161101` |
|
|
375
|
+
|
|
376
|
+
=== Fallback Chain
|
|
377
|
+
|
|
378
|
+
Version-specific registers form a fallback hierarchy:
|
|
379
|
+
|
|
380
|
+
....
|
|
381
|
+
xmi_20161101
|
|
382
|
+
↓ fallback
|
|
383
|
+
xmi_20131001
|
|
384
|
+
↓ fallback
|
|
385
|
+
xmi_20110701
|
|
386
|
+
↓ fallback
|
|
387
|
+
xmi_common
|
|
388
|
+
↓ fallback
|
|
389
|
+
default
|
|
390
|
+
....
|
|
391
|
+
|
|
392
|
+
Types not found in a version fall back to older versions. For example,
|
|
393
|
+
XMI 2.5.2 uses XMI 2.5.1's `Documentation` model (same structure).
|
|
394
|
+
|
|
395
|
+
=== Migration from Old API
|
|
396
|
+
|
|
397
|
+
The old API used `SparxRoot.parse_xml` with automatic namespace normalization:
|
|
398
|
+
|
|
399
|
+
[source,ruby]
|
|
400
|
+
----
|
|
401
|
+
# Old API (still works, but version-aware API is recommended)
|
|
402
|
+
doc = Xmi::Sparx::SparxRoot.parse_xml(xml_content)
|
|
403
|
+
----
|
|
404
|
+
|
|
405
|
+
The new version-aware API:
|
|
406
|
+
|
|
407
|
+
[source,ruby]
|
|
408
|
+
----
|
|
409
|
+
# New API - explicit about version handling
|
|
410
|
+
doc = Xmi::Sparx::SparxRoot.parse_xml_with_versioning(xml_content)
|
|
411
|
+
|
|
412
|
+
# Or use the module-level API
|
|
413
|
+
doc = Xmi.parse(xml_content)
|
|
414
|
+
----
|
|
415
|
+
|
|
416
|
+
=== Enterprise Architect Quirks
|
|
417
|
+
|
|
418
|
+
Enterprise Architect (EA) generates XMI files with several non-standard
|
|
419
|
+
behaviors that this library handles through preprocessing.
|
|
420
|
+
|
|
421
|
+
==== OMG Namespace Version Normalization
|
|
422
|
+
|
|
423
|
+
OMG publishes XMI and UML specifications with dated namespace URIs
|
|
424
|
+
(e.g., `http://www.omg.org/spec/XMI/20110701`, `20131001`, `20161101`).
|
|
425
|
+
While these represent different specification versions, the core XMI
|
|
426
|
+
structure is compatible across versions.
|
|
427
|
+
|
|
428
|
+
This library normalizes all OMG namespace versions to the canonical
|
|
429
|
+
`20131001` version during parsing, allowing a single set of model classes
|
|
430
|
+
to handle all versions.
|
|
431
|
+
|
|
432
|
+
[source,xml]
|
|
433
|
+
----
|
|
434
|
+
<!-- Input with mixed versions -->
|
|
435
|
+
<xmi:XMI xmlns:xmi="http://www.omg.org/spec/XMI/20110701"
|
|
436
|
+
xmlns:uml="http://www.omg.org/spec/UML/20161101">
|
|
437
|
+
|
|
438
|
+
<!-- After normalization -->
|
|
439
|
+
<xmi:XMI xmlns:xmi="http://www.omg.org/spec/XMI/20131001"
|
|
440
|
+
xmlns:uml="http://www.omg.org/spec/UML/20131001">
|
|
441
|
+
----
|
|
442
|
+
|
|
443
|
+
==== EA's Misuse of the `xmlns` Attribute
|
|
444
|
+
|
|
445
|
+
In standard XML, the `xmlns` attribute has special meaning—it declares the
|
|
446
|
+
default namespace for an element and its descendants. The XML specification
|
|
447
|
+
reserves this attribute name for namespace declarations.
|
|
448
|
+
|
|
449
|
+
However, Enterprise Architect incorrectly uses `xmlns` as a *regular data
|
|
450
|
+
attribute* on certain stereotype elements, storing arbitrary URI values that
|
|
451
|
+
have nothing to do with XML namespace declarations. This is a violation of
|
|
452
|
+
XML conventions.
|
|
453
|
+
|
|
454
|
+
This quirk has been observed on:
|
|
455
|
+
- `GML:ApplicationSchema` (Geography Markup Language profile)
|
|
456
|
+
- `CityGML:ApplicationSchema` (City Geography Markup Language profile)
|
|
457
|
+
|
|
458
|
+
[source,xml]
|
|
459
|
+
----
|
|
460
|
+
<!-- EA-generated XMI with xmlns as a data attribute -->
|
|
461
|
+
<GML:ApplicationSchema xmlns="http://some-uri-value"
|
|
462
|
+
targetNamespace="http://example.org/ns">
|
|
463
|
+
----
|
|
464
|
+
|
|
465
|
+
This creates parsing conflicts because XML libraries treat `xmlns` as a
|
|
466
|
+
reserved keyword. This library works around the issue by renaming the
|
|
467
|
+
`xmlns` attribute to `altered_xmlns` before parsing:
|
|
468
|
+
|
|
469
|
+
[source,xml]
|
|
470
|
+
----
|
|
471
|
+
<!-- After preprocessing -->
|
|
472
|
+
<GML:ApplicationSchema altered_xmlns="http://some-uri-value"
|
|
473
|
+
targetNamespace="http://example.org/ns">
|
|
474
|
+
----
|
|
475
|
+
|
|
476
|
+
The corresponding model classes (e.g., `Xmi::Sparx::Gml::ApplicationSchema`)
|
|
477
|
+
define an `altered_xmlns` attribute to receive this value.
|
|
165
478
|
|
|
166
479
|
== Development
|
|
167
480
|
|
data/benchmark_parse.rb
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Benchmark script for XMI parsing performance.
|
|
4
|
+
# Usage: bundle exec ruby benchmark_parse.rb
|
|
5
|
+
#
|
|
6
|
+
# Parses the full-242.xmi fixture (3.5 MB) multiple times and reports
|
|
7
|
+
# average, min, and max parse times.
|
|
8
|
+
|
|
9
|
+
require "bundler/setup"
|
|
10
|
+
require "xmi"
|
|
11
|
+
|
|
12
|
+
FIXTURE_PATH = File.join(__dir__, "spec", "fixtures", "full-242.xmi")
|
|
13
|
+
WARMUP_RUNS = 2
|
|
14
|
+
BENCH_RUNS = 5
|
|
15
|
+
|
|
16
|
+
abort "Fixture not found: #{FIXTURE_PATH}" unless File.exist?(FIXTURE_PATH)
|
|
17
|
+
|
|
18
|
+
xml_content = File.read(FIXTURE_PATH)
|
|
19
|
+
file_size_mb = File.size(FIXTURE_PATH).to_f / (1024 * 1024)
|
|
20
|
+
|
|
21
|
+
puts "XMI Parsing Benchmark"
|
|
22
|
+
puts "=" * 50
|
|
23
|
+
puts "File: #{FIXTURE_PATH}"
|
|
24
|
+
puts "Size: #{file_size_mb.round(2)} MB"
|
|
25
|
+
puts "Ruby: #{RUBY_VERSION} (#{RUBY_PLATFORM})"
|
|
26
|
+
puts "Warmup runs: #{WARMUP_RUNS}"
|
|
27
|
+
puts "Benchmark runs: #{BENCH_RUNS}"
|
|
28
|
+
puts
|
|
29
|
+
|
|
30
|
+
# Warmup
|
|
31
|
+
puts "Warming up..."
|
|
32
|
+
WARMUP_RUNS.times do |i|
|
|
33
|
+
GC.start
|
|
34
|
+
Xmi::Sparx::SparxRoot.parse_xml(xml_content)
|
|
35
|
+
puts " Warmup #{i + 1}/#{WARMUP_RUNS} complete"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Benchmark
|
|
39
|
+
puts "Benchmarking..."
|
|
40
|
+
times = BENCH_RUNS.times.map do |i|
|
|
41
|
+
GC.start
|
|
42
|
+
t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
43
|
+
Xmi::Sparx::SparxRoot.parse_xml(xml_content)
|
|
44
|
+
t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
45
|
+
elapsed = t1 - t0
|
|
46
|
+
puts " Run #{i + 1}/#{BENCH_RUNS}: #{elapsed.round(3)}s"
|
|
47
|
+
elapsed
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
avg = times.sum / times.size
|
|
51
|
+
min = times.min
|
|
52
|
+
max = times.max
|
|
53
|
+
|
|
54
|
+
puts
|
|
55
|
+
puts "Results"
|
|
56
|
+
puts "-" * 50
|
|
57
|
+
puts "Average: #{avg.round(3)} s"
|
|
58
|
+
puts "Min: #{min.round(3)} s"
|
|
59
|
+
puts "Max: #{max.round(3)} s"
|
|
60
|
+
puts "StdDev: #{Math.sqrt(times.sum { |t| (t - avg)**2 } / times.size).round(3)} s"
|
data/docs/migration.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Migration Guide: Version-Aware Parsing
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This guide helps you migrate from the old XMI parsing API to the new version-aware parsing system.
|
|
6
|
+
|
|
7
|
+
## Old API (Pre-Versioning)
|
|
8
|
+
|
|
9
|
+
The old API used `SparxRoot.parse_xml` with automatic namespace normalization:
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
require 'xmi'
|
|
13
|
+
|
|
14
|
+
# Old approach - normalizes all namespaces to 20131001
|
|
15
|
+
doc = Xmi::Sparx::SparxRoot.parse_xml(xml_content)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
This approach:
|
|
19
|
+
- Preprocesses XML to normalize namespace versions
|
|
20
|
+
- Uses a single set of model classes
|
|
21
|
+
- Works for all XMI versions
|
|
22
|
+
|
|
23
|
+
## New API (Version-Aware)
|
|
24
|
+
|
|
25
|
+
The new API detects and uses the correct version automatically:
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
require 'xmi'
|
|
29
|
+
|
|
30
|
+
# New approach - version-aware parsing
|
|
31
|
+
doc = Xmi.parse(xml_content)
|
|
32
|
+
|
|
33
|
+
# Or for Sparx EA files
|
|
34
|
+
doc = Xmi::Sparx::SparxRoot.parse_xml_with_versioning(xml_content)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
This approach:
|
|
38
|
+
- Detects version from XML namespace
|
|
39
|
+
- Uses version-specific model classes when needed
|
|
40
|
+
- Falls back gracefully for shared types
|
|
41
|
+
|
|
42
|
+
## Quick Migration
|
|
43
|
+
|
|
44
|
+
### For Basic Parsing
|
|
45
|
+
|
|
46
|
+
```ruby
|
|
47
|
+
# Before
|
|
48
|
+
doc = Xmi::Sparx::SparxRoot.parse_xml(xml_content)
|
|
49
|
+
|
|
50
|
+
# After
|
|
51
|
+
doc = Xmi::Sparx::SparxRoot.parse_xml_with_versioning(xml_content)
|
|
52
|
+
|
|
53
|
+
# Or
|
|
54
|
+
doc = Xmi.parse(xml_content)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### For Version Detection
|
|
58
|
+
|
|
59
|
+
```ruby
|
|
60
|
+
# Before
|
|
61
|
+
# No built-in version detection
|
|
62
|
+
|
|
63
|
+
# After
|
|
64
|
+
info = Xmi::Parsing.detect_version(xml_content)
|
|
65
|
+
puts info[:xmi_version] # => "20131001"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### For Explicit Version
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
# Before
|
|
72
|
+
# No explicit version support
|
|
73
|
+
|
|
74
|
+
# After
|
|
75
|
+
doc = Xmi.parse_with_version(xml_content, "20131001")
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Benefits of Version-Aware Parsing
|
|
79
|
+
|
|
80
|
+
1. **Correct Types**: Version-specific models are used when structures differ
|
|
81
|
+
2. **No Preprocessing**: XML is parsed directly without modification
|
|
82
|
+
3. **Graceful Fallback**: Shared types work across versions
|
|
83
|
+
4. **Future Extensibility**: Easy to add new version support
|
|
84
|
+
|
|
85
|
+
## When to Use Each API
|
|
86
|
+
|
|
87
|
+
| Use Case | Recommended API |
|
|
88
|
+
|----------|----------------|
|
|
89
|
+
| Sparx EA files | `Xmi::Sparx::SparxRoot.parse_xml_with_versioning` |
|
|
90
|
+
| General XMI files | `Xmi.parse` |
|
|
91
|
+
| Version detection | `Xmi::Parsing.detect_version` |
|
|
92
|
+
| Known version | `Xmi.parse_with_version(xml, version)` |
|
|
93
|
+
|
|
94
|
+
## Backward Compatibility
|
|
95
|
+
|
|
96
|
+
The old `parse_xml` method still works but normalizes namespaces:
|
|
97
|
+
|
|
98
|
+
```ruby
|
|
99
|
+
# Old API - still supported
|
|
100
|
+
doc = Xmi::Sparx::SparxRoot.parse_xml(xml_content)
|
|
101
|
+
|
|
102
|
+
# This internally normalizes to 20131001 namespace
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Troubleshooting
|
|
106
|
+
|
|
107
|
+
### Version Not Detected
|
|
108
|
+
|
|
109
|
+
If version detection fails:
|
|
110
|
+
|
|
111
|
+
```ruby
|
|
112
|
+
# Check what was detected
|
|
113
|
+
info = Xmi::Parsing.detect_version(xml_content)
|
|
114
|
+
|
|
115
|
+
# Use explicit version if needed
|
|
116
|
+
doc = Xmi.parse_with_version(xml_content, "20131001")
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Type Resolution Fails
|
|
120
|
+
|
|
121
|
+
If a type isn't resolved correctly:
|
|
122
|
+
|
|
123
|
+
```ruby
|
|
124
|
+
# Initialize versioning explicitly
|
|
125
|
+
Xmi.init_versioning!
|
|
126
|
+
|
|
127
|
+
# Check what version was detected
|
|
128
|
+
info = Xmi::Parsing.detect_version(xml_content)
|
|
129
|
+
|
|
130
|
+
# Use explicit version
|
|
131
|
+
doc = Xmi.parse_with_version(xml_content, info[:xmi_version])
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## API Comparison
|
|
135
|
+
|
|
136
|
+
| Old API | New API |
|
|
137
|
+
|---------|---------|
|
|
138
|
+
| `SparxRoot.parse_xml` | `SparxRoot.parse_xml_with_versioning` |
|
|
139
|
+
| (no detection) | `Xmi::Parsing.detect_version` |
|
|
140
|
+
| (no explicit) | `Xmi.parse_with_version` |
|
|
141
|
+
| `Xmi.parse` (not existed) | `Xmi.parse` (auto-detect) |
|