From 0b30a2794c37b405ac9b601be123fa7bf418f863 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Mon, 31 Aug 2026 11:40:20 +0200 Subject: [PATCH] refactor: promote the extension interfaces out of the internal namespace External libraries integrate a database or format by subclassing the serialization and resolution bases, but those classes lived in internal namespaces, so building on openMINDS_MATLAB meant depending on its internals. The extension surface is now public and grouped by kind: openminds.interface.LinkResolver (was openminds.internal.resolver .AbstractLinkResolver; contract only, sits beside MetadataStore, and the rename drops the class attribute from the name) openminds.abstract.BaseSerializer (was openminds.internal.serializer .BaseSerializer; partial implementation meant for subclassing, beside BaseVisitor and BaseTransformer) openminds.abstract.BaseDeserializer (same move) An external backend now implements openminds.interface.MetadataStore and openminds.interface.LinkResolver, and subclasses the abstract serializer pair only when it needs its own format. The JSON-LD implementations stay internal. struct2jsonld is deleted. Nothing has called it since deserialization moved onto the deserializer. Breaking change for openminds-kg-sync: KGResolver's superclass becomes openminds.interface.LinkResolver, on top of the resolveNode rename it already needs from the traversal rework. Co-Authored-By: Claude Opus 5 --- .../BaseDeserializer.m | 4 +-- .../BaseSerializer.m | 2 +- code/internal/+openminds/+abstract/Schema.m | 2 +- .../LinkResolver.m} | 4 +-- .../+internal/+resolver/InstanceResolver.m | 2 +- .../+resolver/LinkResolverRegistry.m | 2 +- .../+internal/+resolver/ResolvingVisitor.m | 2 +- .../+serializer/JsonLdDeserializer.m | 2 +- .../+internal/+serializer/JsonLdSerializer.m | 4 +-- .../+internal/+serializer/LinkWiringVisitor.m | 2 +- .../+internal/+serializer/struct2jsonld.m | 31 ------------------- .../+openminds/registerLinkResolver.m | 4 +-- .../+helper/+mock/CyclicMockLinkResolver.m | 2 +- .../+ommtest/+helper/+mock/MockLinkResolver.m | 4 +-- .../+helper/+mock/ReplacingMockLinkResolver.m | 2 +- 15 files changed, 19 insertions(+), 50 deletions(-) rename code/internal/+openminds/{+internal/+serializer => +abstract}/BaseDeserializer.m (98%) rename code/internal/+openminds/{+internal/+serializer => +abstract}/BaseSerializer.m (99%) rename code/internal/+openminds/{+internal/+resolver/AbstractLinkResolver.m => +interface/LinkResolver.m} (94%) delete mode 100644 code/internal/+openminds/+internal/+serializer/struct2jsonld.m diff --git a/code/internal/+openminds/+internal/+serializer/BaseDeserializer.m b/code/internal/+openminds/+abstract/BaseDeserializer.m similarity index 98% rename from code/internal/+openminds/+internal/+serializer/BaseDeserializer.m rename to code/internal/+openminds/+abstract/BaseDeserializer.m index 763a12752..7e9f8cf0e 100644 --- a/code/internal/+openminds/+internal/+serializer/BaseDeserializer.m +++ b/code/internal/+openminds/+abstract/BaseDeserializer.m @@ -10,7 +10,7 @@ % ------ % Subclasses implement one method: % -% classdef MyDeserializer < openminds.internal.serializer.BaseDeserializer +% classdef MyDeserializer < openminds.abstract.BaseDeserializer % methods (Access = protected) % function rawStructs = parseToStructs(obj, data) % ... @@ -18,7 +18,7 @@ % end % end % -% See also openminds.internal.serializer.BaseSerializer, +% See also openminds.abstract.BaseSerializer, % openminds.internal.serializer.LinkWiringVisitor properties (Access = protected) diff --git a/code/internal/+openminds/+internal/+serializer/BaseSerializer.m b/code/internal/+openminds/+abstract/BaseSerializer.m similarity index 99% rename from code/internal/+openminds/+internal/+serializer/BaseSerializer.m rename to code/internal/+openminds/+abstract/BaseSerializer.m index d5b119829..211b43b4a 100644 --- a/code/internal/+openminds/+internal/+serializer/BaseSerializer.m +++ b/code/internal/+openminds/+abstract/BaseSerializer.m @@ -99,7 +99,7 @@ % Serialized output in the format specified by the subclass arguments - obj (1,1) openminds.internal.serializer.BaseSerializer + obj (1,1) openminds.abstract.BaseSerializer instances % openminds.abstract.Schema or cell array end diff --git a/code/internal/+openminds/+abstract/Schema.m b/code/internal/+openminds/+abstract/Schema.m index 5061855db..273becda8 100644 --- a/code/internal/+openminds/+abstract/Schema.m +++ b/code/internal/+openminds/+abstract/Schema.m @@ -120,7 +120,7 @@ arguments obj (1,:) openminds.abstract.Schema options.NumLinksToResolve = 0 - options.LinkResolver openminds.internal.resolver.AbstractLinkResolver + options.LinkResolver openminds.interface.LinkResolver % options.IsEmbedded = false - Todo? end diff --git a/code/internal/+openminds/+internal/+resolver/AbstractLinkResolver.m b/code/internal/+openminds/+interface/LinkResolver.m similarity index 94% rename from code/internal/+openminds/+internal/+resolver/AbstractLinkResolver.m rename to code/internal/+openminds/+interface/LinkResolver.m index 9cea820c5..adaad0f34 100644 --- a/code/internal/+openminds/+internal/+resolver/AbstractLinkResolver.m +++ b/code/internal/+openminds/+interface/LinkResolver.m @@ -1,5 +1,5 @@ -classdef (Abstract) AbstractLinkResolver < handle -% AbstractLinkResolver - Turns a reference node into a populated instance +classdef (Abstract) LinkResolver < handle +% LinkResolver - Turns a reference node into a populated instance % % A resolver knows how to fetch the data behind one kind of identifier. % It does not walk the graph: traversal, link depth and cycle detection diff --git a/code/internal/+openminds/+internal/+resolver/InstanceResolver.m b/code/internal/+openminds/+internal/+resolver/InstanceResolver.m index 3c2463523..765bc8dd4 100644 --- a/code/internal/+openminds/+internal/+resolver/InstanceResolver.m +++ b/code/internal/+openminds/+internal/+resolver/InstanceResolver.m @@ -1,4 +1,4 @@ -classdef InstanceResolver < openminds.internal.resolver.AbstractLinkResolver +classdef InstanceResolver < openminds.interface.LinkResolver % InstanceResolver - Resolves openMINDS controlled instances from the local library properties (Constant) diff --git a/code/internal/+openminds/+internal/+resolver/LinkResolverRegistry.m b/code/internal/+openminds/+internal/+resolver/LinkResolverRegistry.m index aff6b00a6..1234145bb 100644 --- a/code/internal/+openminds/+internal/+resolver/LinkResolverRegistry.m +++ b/code/internal/+openminds/+internal/+resolver/LinkResolverRegistry.m @@ -19,7 +19,7 @@ function addLinkResolver(obj, resolver) % Add a resolver instance to the registry (no duplicates by handle). arguments obj (1,1) openminds.internal.resolver.LinkResolverRegistry - resolver (1,1) {mustBeA(resolver, "openminds.internal.resolver.AbstractLinkResolver")} + resolver (1,1) {mustBeA(resolver, "openminds.interface.LinkResolver")} end if obj.hasResolverForPrefix(resolver.IRIPrefix) diff --git a/code/internal/+openminds/+internal/+resolver/ResolvingVisitor.m b/code/internal/+openminds/+internal/+resolver/ResolvingVisitor.m index 616849102..00833245a 100644 --- a/code/internal/+openminds/+internal/+resolver/ResolvingVisitor.m +++ b/code/internal/+openminds/+internal/+resolver/ResolvingVisitor.m @@ -10,7 +10,7 @@ % embedded instance does not, because an embedded instance is part of % its parent rather than a separate node. % -% See also openminds.internal.resolver.AbstractLinkResolver +% See also openminds.interface.LinkResolver properties % Number of further links to follow. diff --git a/code/internal/+openminds/+internal/+serializer/JsonLdDeserializer.m b/code/internal/+openminds/+internal/+serializer/JsonLdDeserializer.m index 62101d131..7e1e6fcf9 100644 --- a/code/internal/+openminds/+internal/+serializer/JsonLdDeserializer.m +++ b/code/internal/+openminds/+internal/+serializer/JsonLdDeserializer.m @@ -1,4 +1,4 @@ -classdef JsonLdDeserializer < openminds.internal.serializer.BaseDeserializer +classdef JsonLdDeserializer < openminds.abstract.BaseDeserializer % JsonLdDeserializer - Reads openMINDS instances from JSON-LD % % The counterpart to JsonLdSerializer. Accepts either a single document diff --git a/code/internal/+openminds/+internal/+serializer/JsonLdSerializer.m b/code/internal/+openminds/+internal/+serializer/JsonLdSerializer.m index c366daa14..3f8a8e7dc 100644 --- a/code/internal/+openminds/+internal/+serializer/JsonLdSerializer.m +++ b/code/internal/+openminds/+internal/+serializer/JsonLdSerializer.m @@ -1,4 +1,4 @@ -classdef JsonLdSerializer < openminds.internal.serializer.BaseSerializer +classdef JsonLdSerializer < openminds.abstract.BaseSerializer %JsonLdSerializer Serializer for JSON-LD format % % This class extends BaseSerializer to provide JSON-LD specific @@ -26,7 +26,7 @@ config.?openminds.internal.serializer.SerializationConfig end nvPairs = namedargs2cell(config); - obj = obj@openminds.internal.serializer.BaseSerializer(nvPairs{:}); + obj = obj@openminds.abstract.BaseSerializer(nvPairs{:}); end end diff --git a/code/internal/+openminds/+internal/+serializer/LinkWiringVisitor.m b/code/internal/+openminds/+internal/+serializer/LinkWiringVisitor.m index 33839687c..50b0f23c5 100644 --- a/code/internal/+openminds/+internal/+serializer/LinkWiringVisitor.m +++ b/code/internal/+openminds/+internal/+serializer/LinkWiringVisitor.m @@ -10,7 +10,7 @@ % A stub naming a controlled instance that is not part of the document % is resolved from the local instance library instead. % -% See also openminds.internal.serializer.BaseDeserializer +% See also openminds.abstract.BaseDeserializer properties (Access = private) % Identifier to instance, for everything in the document diff --git a/code/internal/+openminds/+internal/+serializer/struct2jsonld.m b/code/internal/+openminds/+internal/+serializer/struct2jsonld.m deleted file mode 100644 index 011e9375e..000000000 --- a/code/internal/+openminds/+internal/+serializer/struct2jsonld.m +++ /dev/null @@ -1,31 +0,0 @@ -function jsonInstance = struct2jsonld(structInstance) -%Convert a metadata instance from a struct to a JSON-LD text string. -% -% jsonInstance = struct2jsonld(structInstance) converts a MATLAB struct -% instance into a JSON-LD (JavaScript Object Notation for Linked Data) -% text string. JSON-LD is a lightweight data interchange format that -% is easy for humans to read and write and easy for machines to parse -% and generate. -% -% Parameters: -% - structInstance: A MATLAB struct array containing metadata -% instances to be converted to JSON-LD. -% -% Returns: -% - jsonInstance: A JSON-LD formatted text string representing -% the provided metadata instances. If structInstance -% is an array, jsonInstance is a cell array with the -% same size. - - vocabBaseUri = "https://openminds.ebrains.eu/vocab/"; - - if numel(structInstance) > 1 - structInstance = struct( ... - 'at_context', {struct('at_vocab', vocabBaseUri)}, ... - 'at_graph', {structInstance} ... - ); - end - - jsonStr = openminds.internal.utility.json.encode(structInstance); - jsonInstance = strrep(jsonStr, 'VOCAB_URI_', vocabBaseUri); -end diff --git a/code/internal/+openminds/registerLinkResolver.m b/code/internal/+openminds/registerLinkResolver.m index 2d028f0e7..98c90fea7 100644 --- a/code/internal/+openminds/registerLinkResolver.m +++ b/code/internal/+openminds/registerLinkResolver.m @@ -2,11 +2,11 @@ function registerLinkResolver(linkResolver) % registerLinkResolver - Register a link resolver in the link resolver registry % % See also: -% openminds.internal.resolver.AbstractLinkResolver +% openminds.interface.LinkResolver % openminds.internal.resolver.InstanceResolver arguments - linkResolver (1,1) {mustBeA(linkResolver, "openminds.internal.resolver.AbstractLinkResolver")} + linkResolver (1,1) {mustBeA(linkResolver, "openminds.interface.LinkResolver")} end resolverRegistry = openminds.internal.resolver.LinkResolverRegistry.instance(); diff --git a/tools/tests/+ommtest/+helper/+mock/CyclicMockLinkResolver.m b/tools/tests/+ommtest/+helper/+mock/CyclicMockLinkResolver.m index 58ac878f5..8f74ddc04 100644 --- a/tools/tests/+ommtest/+helper/+mock/CyclicMockLinkResolver.m +++ b/tools/tests/+ommtest/+helper/+mock/CyclicMockLinkResolver.m @@ -1,4 +1,4 @@ -classdef CyclicMockLinkResolver < openminds.internal.resolver.AbstractLinkResolver +classdef CyclicMockLinkResolver < openminds.interface.LinkResolver %CyclicMockLinkResolver Resolves two ContentType references that link to each other % % Resolving the node "a" links it to a reference "b", and resolving "b" diff --git a/tools/tests/+ommtest/+helper/+mock/MockLinkResolver.m b/tools/tests/+ommtest/+helper/+mock/MockLinkResolver.m index 1c77469b6..de50da989 100644 --- a/tools/tests/+ommtest/+helper/+mock/MockLinkResolver.m +++ b/tools/tests/+ommtest/+helper/+mock/MockLinkResolver.m @@ -1,5 +1,5 @@ -classdef MockLinkResolver < openminds.internal.resolver.AbstractLinkResolver -%MockLinkResolver Mock implementation of AbstractLinkResolver for testing +classdef MockLinkResolver < openminds.interface.LinkResolver +%MockLinkResolver Mock implementation of openminds.interface.LinkResolver for testing % % This class provides a mock implementation for testing resolver % functionality with fake data. It can resolve instances with IRIs diff --git a/tools/tests/+ommtest/+helper/+mock/ReplacingMockLinkResolver.m b/tools/tests/+ommtest/+helper/+mock/ReplacingMockLinkResolver.m index f977323b7..5dc6865de 100644 --- a/tools/tests/+ommtest/+helper/+mock/ReplacingMockLinkResolver.m +++ b/tools/tests/+ommtest/+helper/+mock/ReplacingMockLinkResolver.m @@ -1,4 +1,4 @@ -classdef ReplacingMockLinkResolver < openminds.internal.resolver.AbstractLinkResolver +classdef ReplacingMockLinkResolver < openminds.interface.LinkResolver %ReplacingMockLinkResolver Resolver that replaces rather than populates % % Mirrors the case where the type behind an identifier is not known