Skip to content

Commit 15b4762

Browse files
authored
Enhance node addition logic in Collection class (#79)
Added 'AbortIfNodeExists' option to node addition methods and improved handling of controlled term instances based on preferences.
1 parent 1987e0c commit 15b4762

File tree

8 files changed

+66
-47
lines changed

8 files changed

+66
-47
lines changed

.github/badges/tests.svg

Lines changed: 1 addition & 1 deletion
Loading

code/+openminds/@Collection/Collection.m

Lines changed: 55 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636

3737
% Todo: Validation.
3838
% - Linked subject states should have same subject
39+
% - Add method to update links for specified instances
3940

4041
% Need mechanism to check if embedded nodes are added to the collection
4142

@@ -51,7 +52,7 @@
5152
NumNodes
5253
NumTypes
5354
end
54-
55+
5556
properties (SetAccess = protected)
5657
% Nodes - Dictionary storing instances as values with identifiers
5758
% as keys
@@ -66,11 +67,11 @@
6667

6768
properties (SetAccess = protected)
6869
LinkResolver
69-
70+
7071
% MetadataStore - Optional metadata store for saving/loading
7172
MetadataStore openminds.interface.MetadataStore = openminds.internal.FileMetadataStore.empty
7273
end
73-
74+
7475
methods % Constructor
7576
function obj = Collection(instance, options)
7677
% Create an instance of an openMINDS collection
@@ -97,7 +98,7 @@
9798
% creates a collection with the specified metadata store. If no
9899
% instances are provided, the collection will automatically load
99100
% instances from the store.
100-
101+
101102
% collection = openminds.Collection(..., NameA, ValueA, ... )
102103
% also specifies optional name value pairs when creating the
103104
% collection.
@@ -106,7 +107,7 @@
106107
% - Name : A name for the collection
107108
% - Description : A description of the collection
108109
% - MetadataStore : A metadata store for saving/loading instances
109-
110+
110111
arguments (Repeating)
111112
instance % openminds.abstract.Schema
112113
end
@@ -126,13 +127,13 @@
126127
obj.Nodes = containers.Map;
127128
obj.TypeMap = containers.Map;
128129
end
129-
130+
130131
obj.initializeFromInstances(instance)
131132

132133
obj.Name = options.Name;
133134
obj.Description = options.Description;
134135
obj.MetadataStore = options.MetadataStore;
135-
136+
136137
% Auto-load from MetadataStore if provided and no instances given
137138
if isempty(instance) && ~isempty(obj.MetadataStore)
138139
obj.load();
@@ -148,7 +149,7 @@
148149
numNodes = length(obj.Nodes);
149150
end
150151
end
151-
152+
152153
function numTypes = get.NumTypes(obj)
153154
if isa(obj.TypeMap, 'dictionary')
154155
numTypes = numEntries(obj.TypeMap);
@@ -190,16 +191,19 @@ function add(obj, instance, options)
190191
end
191192
arguments
192193
options.AddSubNodesOnly = false;
194+
options.AbortIfNodeExists = true;
193195
end
194196

195197
for i = 1:numel(instance)
196198
thisInstance = instance{i};
197199
for j = 1:numel(thisInstance) % If thisInstance is an array
198-
obj.addNode(thisInstance(j), "AddSubNodesOnly", options.AddSubNodesOnly);
200+
obj.addNode(thisInstance(j), ...
201+
"AddSubNodesOnly", options.AddSubNodesOnly, ...
202+
"AbortIfNodeExists", options.AbortIfNodeExists);
199203
end
200204
end
201205
end
202-
206+
203207
function tf = contains(obj, instance)
204208
% Todo:work for arrays
205209
tf = false;
@@ -210,10 +214,10 @@ function add(obj, instance, options)
210214
end
211215
end
212216
end
213-
217+
214218
function remove(obj, instance)
215219
% remove - Remove metadata instance from the collection
216-
220+
217221
if isstring(instance) || ischar(instance)
218222
instanceId = instance;
219223
elseif openminds.utility.isInstance(instance)
@@ -250,11 +254,11 @@ function remove(obj, instance)
250254
instance = instance{1};
251255
end
252256
end
253-
257+
254258
function instances = getAll(obj)
255259
% getAll - Get all instances of collection
256260
instances = obj.Nodes.values();
257-
261+
258262
% For older MATLAB releases, the instances might be nested a
259263
% cell array, need to unnest if that's the case:
260264
if iscell(instances{1})
@@ -269,11 +273,11 @@ function remove(obj, instance)
269273
end
270274

271275
tf = false;
272-
276+
273277
if obj.NumNodes == 0
274278
return
275279
end
276-
280+
277281
typeKeys = obj.TypeMap.keys;
278282
tf = any( endsWith(typeKeys, "."+type) ); %i.e ".Person"
279283
end
@@ -293,10 +297,10 @@ function remove(obj, instance)
293297
if obj.NumNodes == 0
294298
return
295299
end
296-
300+
297301
instanceKeys = obj.getInstanceKeysForType(type);
298302
if isempty(instanceKeys); return; end
299-
303+
300304
if isa(obj.Nodes, 'dictionary')
301305
instances = obj.Nodes(instanceKeys);
302306
else
@@ -327,6 +331,10 @@ function updateLinks(obj)
327331
allInstances = [allInstances{:}];
328332
end
329333

334+
if iscolumn(allInstances)
335+
allInstances = reshape(allInstances, 1, []);
336+
end
337+
330338
for instance = allInstances
331339
obj.addNode(instance{1}, ...
332340
'AddSubNodesOnly', true, ...
@@ -360,22 +368,22 @@ function updateLinks(obj)
360368
% ------
361369
%
362370
% outputPaths (cell): A list of the file paths created.
363-
371+
364372
arguments
365373
obj openminds.Collection
366374
savePath (1,1) string = ""
367375
options.MetadataStore openminds.interface.MetadataStore = openminds.internal.FileMetadataStore.empty
368376
% options.SaveFormat = "jsonld" Implement if more formats are supported
369377
end
370-
378+
371379
% Update links before saving
372380
obj.updateLinks()
373381
instances = obj.getAll();
374382

375383
if savePath ~= ""
376384
tempStore = openminds.internal.store.createTemporaryStore(savePath);
377385
outputPaths = tempStore.save(instances);
378-
386+
379387
elseif ~isempty(options.MetadataStore)
380388
outputPaths = obj.MetadataStore.save(instances);
381389

@@ -387,7 +395,7 @@ function updateLinks(obj)
387395
error('openminds:Collection:NoSavePath', ...
388396
'Either provide savePath or configure a MetadataStore');
389397
end
390-
398+
391399
if ~nargout
392400
clear outputPaths
393401
end
@@ -436,7 +444,7 @@ function load(obj, loadPath, options)
436444
error('openminds:Collection:PathNotFound', 'Path not found: %s', loadPath);
437445
end
438446
end
439-
447+
440448
for i = 1:numel(instances)
441449
if openminds.utility.isInstance(instances{i})
442450
obj.addNode(instances{i});
@@ -469,13 +477,13 @@ function load(obj, loadPath, options)
469477
% --------
470478
% collection : openminds.Collection
471479
% A new collection loaded with instances from the store
472-
480+
473481
arguments
474482
metadataStore (1,1) openminds.interface.MetadataStore
475483
options.Name (1,1) string = ""
476484
options.Description (1,1) string = ""
477485
end
478-
486+
479487
% Create collection with the metadata store
480488
collection = openminds.Collection('MetadataStore', metadataStore, ...
481489
'Name', options.Name, 'Description', options.Description);
@@ -493,14 +501,18 @@ function load(obj, loadPath, options)
493501
end
494502

495503
wasAdded = false;
496-
504+
497505
if isempty(instance.id)
498506
instance.id = obj.getBlankNodeIdentifier();
499507
end
500508

501-
% Do not add openminds controlled term instances
502-
if startsWith(instance.id, "https://openminds.ebrains.eu/instances/")
503-
return
509+
% Do not add openminds controlled term instances if disabled in
510+
% preferences
511+
if startsWith(instance.id, "https://openminds.ebrains.eu/instances/") ...
512+
|| startsWith(instance.id, "https://openminds.om-i.org/instances/")
513+
if ~openminds.getpref('AddControlledInstanceToCollection')
514+
return
515+
end
504516
end
505517

506518
if obj.NumNodes > 0
@@ -511,7 +523,10 @@ function load(obj, loadPath, options)
511523
end
512524
end
513525
end
514-
526+
527+
% Add subnodes first
528+
obj.addSubNodes(instance)
529+
515530
if ~options.AddSubNodesOnly
516531
obj.Nodes(instance.id) = {instance};
517532
wasAdded = true;
@@ -529,13 +544,12 @@ function load(obj, loadPath, options)
529544
obj.TypeMap(instanceType) = {string(instance.id)};
530545
end
531546
end
532-
533-
obj.addSubNodes(instance)
547+
534548
if ~nargout
535549
clear wasAdded
536550
end
537551
end
538-
552+
539553
% Add sub node instances (linked types) to the Node container.
540554
function addSubNodes(obj, instance)
541555
% Add links.
@@ -552,7 +566,7 @@ function addSubNodes(obj, instance)
552566
obj.addNode(embeddedInstances{i}, 'AddSubNodesOnly', true);
553567
end
554568
end
555-
569+
556570
function identifier = getBlankNodeIdentifier(obj)
557571
fmt = '_:%06d';
558572
identifier = length(obj) + 1;
@@ -567,19 +581,19 @@ function initializeFromInstances(obj, instance)
567581
isFilePath = @(x) (ischar(x) || isstring(x)) && isfile(x);
568582
isFolderPath = @(x) (ischar(x) || isstring(x)) && isfolder(x);
569583
isMetadata = @(x) openminds.utility.isInstance(x);
570-
584+
571585
% Initialize from file(s)
572586
if all( cellfun(isFilePath, instance) )
573587
obj.load(instance{:})
574-
588+
575589
% Initialize from folder
576590
elseif all( cellfun(isFolderPath, instance) )
577591
obj.load(instance{:})
578-
592+
579593
% Initialize from instance(s)
580594
elseif all( cellfun(isMetadata, instance) )
581595
obj.add(instance{:});
582-
596+
583597
else
584598
ME = MException(...
585599
'OPENMINDS_MATLAB:Collection:InvalidInstanceSpecification', ...
@@ -597,7 +611,7 @@ function initializeFromInstances(obj, instance)
597611

598612
if obj.NumTypes > 0
599613
typeKeys = obj.TypeMap.keys;
600-
614+
601615
isMatch = strcmp(typeKeys, instanceType.ClassName);
602616
if any(isMatch)
603617
if isa(obj.TypeMap, 'dictionary')
@@ -615,9 +629,9 @@ function initializeFromInstances(obj, instance)
615629
instanceKeys = {};
616630
return
617631
end
618-
632+
619633
existingKeys = obj.Nodes.keys();
620-
634+
621635
% Sanity check, make sure all keys exist in Nodes dictionary
622636
assert( all( ismember( instanceKeys, existingKeys ) ), ...
623637
'TypeMap has too many keys' )

code/+openminds/getpref.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
[ ...
2121
"PropertyDisplayMode", ...
2222
"DocLinkTarget", ...
23+
"AddControlledInstanceToCollection", ...
2324
"" ...
2425
])...
2526
} = ""

code/+openminds/instanceFromIRI.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
end
3636

3737
[typeEnum, instanceName] = openminds.utility.parseInstanceIRI(IRI);
38-
38+
3939
if contains(typeEnum.ClassName, "controlledterms")
4040
instance = feval(typeEnum.ClassName, IRI);
4141
else

code/+openminds/setpref.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
end
1919

2020
pref = openminds.utility.Preferences.getSingleton;
21-
21+
2222
prefNames = fieldnames(prefValues);
2323
for i = 1:numel(prefNames)
2424
preferenceName = prefNames{i};

code/+openminds/startup.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ function startup(version)
33
%
44
% This function ensures that only one version of openMINDS schema classes
55
% are on MATLAB's search path.
6-
6+
77
arguments
88
version (1,1) openminds.internal.utility.VersionNumber ...
99
{openminds.mustBeValidVersion(version)} = "latest"
@@ -14,7 +14,7 @@ function startup(version)
1414
% NB: Assumes this function is located in code/+openminds:
1515
codePath = fileparts( fileparts( mfilename('fullpath') ) );
1616
addpath( fullfile(codePath, 'internal') )
17-
17+
1818
% Run internal function that correctly configures the search path
1919
openminds.selectOpenMindsVersion(version)
2020
fprintf(['Added classes for version "%s" of the openMINDS metadata model ' ...

code/+openminds/toolboxversion.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
contentsFile = fullfile(rootPath, 'Contents.m');
66

77
fileStr = fileread(contentsFile);
8-
8+
99
% First try to get a version with a sub-patch version number
1010
matchedStr = regexp(fileStr, 'Version \d*\.\d*\.\d*.\d*(?= )', 'match');
1111

0 commit comments

Comments
 (0)