diff --git a/.github/badges/tests.svg b/.github/badges/tests.svg
index 1a4e5085..4e32f161 100644
--- a/.github/badges/tests.svg
+++ b/.github/badges/tests.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/code/+openminds/@Collection/Collection.m b/code/+openminds/@Collection/Collection.m
index 4e9a2f25..e7d3616a 100644
--- a/code/+openminds/@Collection/Collection.m
+++ b/code/+openminds/@Collection/Collection.m
@@ -36,6 +36,7 @@
% Todo: Validation.
% - Linked subject states should have same subject
+% - Add method to update links for specified instances
% Need mechanism to check if embedded nodes are added to the collection
@@ -51,7 +52,7 @@
NumNodes
NumTypes
end
-
+
properties (SetAccess = protected)
% Nodes - Dictionary storing instances as values with identifiers
% as keys
@@ -66,11 +67,11 @@
properties (SetAccess = protected)
LinkResolver
-
+
% MetadataStore - Optional metadata store for saving/loading
MetadataStore openminds.interface.MetadataStore = openminds.internal.FileMetadataStore.empty
end
-
+
methods % Constructor
function obj = Collection(instance, options)
% Create an instance of an openMINDS collection
@@ -97,7 +98,7 @@
% creates a collection with the specified metadata store. If no
% instances are provided, the collection will automatically load
% instances from the store.
-
+
% collection = openminds.Collection(..., NameA, ValueA, ... )
% also specifies optional name value pairs when creating the
% collection.
@@ -106,7 +107,7 @@
% - Name : A name for the collection
% - Description : A description of the collection
% - MetadataStore : A metadata store for saving/loading instances
-
+
arguments (Repeating)
instance % openminds.abstract.Schema
end
@@ -126,13 +127,13 @@
obj.Nodes = containers.Map;
obj.TypeMap = containers.Map;
end
-
+
obj.initializeFromInstances(instance)
obj.Name = options.Name;
obj.Description = options.Description;
obj.MetadataStore = options.MetadataStore;
-
+
% Auto-load from MetadataStore if provided and no instances given
if isempty(instance) && ~isempty(obj.MetadataStore)
obj.load();
@@ -148,7 +149,7 @@
numNodes = length(obj.Nodes);
end
end
-
+
function numTypes = get.NumTypes(obj)
if isa(obj.TypeMap, 'dictionary')
numTypes = numEntries(obj.TypeMap);
@@ -190,16 +191,19 @@ function add(obj, instance, options)
end
arguments
options.AddSubNodesOnly = false;
+ options.AbortIfNodeExists = true;
end
for i = 1:numel(instance)
thisInstance = instance{i};
for j = 1:numel(thisInstance) % If thisInstance is an array
- obj.addNode(thisInstance(j), "AddSubNodesOnly", options.AddSubNodesOnly);
+ obj.addNode(thisInstance(j), ...
+ "AddSubNodesOnly", options.AddSubNodesOnly, ...
+ "AbortIfNodeExists", options.AbortIfNodeExists);
end
end
end
-
+
function tf = contains(obj, instance)
% Todo:work for arrays
tf = false;
@@ -210,10 +214,10 @@ function add(obj, instance, options)
end
end
end
-
+
function remove(obj, instance)
% remove - Remove metadata instance from the collection
-
+
if isstring(instance) || ischar(instance)
instanceId = instance;
elseif openminds.utility.isInstance(instance)
@@ -250,11 +254,11 @@ function remove(obj, instance)
instance = instance{1};
end
end
-
+
function instances = getAll(obj)
% getAll - Get all instances of collection
instances = obj.Nodes.values();
-
+
% For older MATLAB releases, the instances might be nested a
% cell array, need to unnest if that's the case:
if iscell(instances{1})
@@ -269,11 +273,11 @@ function remove(obj, instance)
end
tf = false;
-
+
if obj.NumNodes == 0
return
end
-
+
typeKeys = obj.TypeMap.keys;
tf = any( endsWith(typeKeys, "."+type) ); %i.e ".Person"
end
@@ -293,10 +297,10 @@ function remove(obj, instance)
if obj.NumNodes == 0
return
end
-
+
instanceKeys = obj.getInstanceKeysForType(type);
if isempty(instanceKeys); return; end
-
+
if isa(obj.Nodes, 'dictionary')
instances = obj.Nodes(instanceKeys);
else
@@ -327,6 +331,10 @@ function updateLinks(obj)
allInstances = [allInstances{:}];
end
+ if iscolumn(allInstances)
+ allInstances = reshape(allInstances, 1, []);
+ end
+
for instance = allInstances
obj.addNode(instance{1}, ...
'AddSubNodesOnly', true, ...
@@ -360,14 +368,14 @@ function updateLinks(obj)
% ------
%
% outputPaths (cell): A list of the file paths created.
-
+
arguments
obj openminds.Collection
savePath (1,1) string = ""
options.MetadataStore openminds.interface.MetadataStore = openminds.internal.FileMetadataStore.empty
% options.SaveFormat = "jsonld" Implement if more formats are supported
end
-
+
% Update links before saving
obj.updateLinks()
instances = obj.getAll();
@@ -375,7 +383,7 @@ function updateLinks(obj)
if savePath ~= ""
tempStore = openminds.internal.store.createTemporaryStore(savePath);
outputPaths = tempStore.save(instances);
-
+
elseif ~isempty(options.MetadataStore)
outputPaths = obj.MetadataStore.save(instances);
@@ -387,7 +395,7 @@ function updateLinks(obj)
error('openminds:Collection:NoSavePath', ...
'Either provide savePath or configure a MetadataStore');
end
-
+
if ~nargout
clear outputPaths
end
@@ -436,7 +444,7 @@ function load(obj, loadPath, options)
error('openminds:Collection:PathNotFound', 'Path not found: %s', loadPath);
end
end
-
+
for i = 1:numel(instances)
if openminds.utility.isInstance(instances{i})
obj.addNode(instances{i});
@@ -469,13 +477,13 @@ function load(obj, loadPath, options)
% --------
% collection : openminds.Collection
% A new collection loaded with instances from the store
-
+
arguments
metadataStore (1,1) openminds.interface.MetadataStore
options.Name (1,1) string = ""
options.Description (1,1) string = ""
end
-
+
% Create collection with the metadata store
collection = openminds.Collection('MetadataStore', metadataStore, ...
'Name', options.Name, 'Description', options.Description);
@@ -493,14 +501,18 @@ function load(obj, loadPath, options)
end
wasAdded = false;
-
+
if isempty(instance.id)
instance.id = obj.getBlankNodeIdentifier();
end
- % Do not add openminds controlled term instances
- if startsWith(instance.id, "https://openminds.ebrains.eu/instances/")
- return
+ % Do not add openminds controlled term instances if disabled in
+ % preferences
+ if startsWith(instance.id, "https://openminds.ebrains.eu/instances/") ...
+ || startsWith(instance.id, "https://openminds.om-i.org/instances/")
+ if ~openminds.getpref('AddControlledInstanceToCollection')
+ return
+ end
end
if obj.NumNodes > 0
@@ -511,7 +523,10 @@ function load(obj, loadPath, options)
end
end
end
-
+
+ % Add subnodes first
+ obj.addSubNodes(instance)
+
if ~options.AddSubNodesOnly
obj.Nodes(instance.id) = {instance};
wasAdded = true;
@@ -529,13 +544,12 @@ function load(obj, loadPath, options)
obj.TypeMap(instanceType) = {string(instance.id)};
end
end
-
- obj.addSubNodes(instance)
+
if ~nargout
clear wasAdded
end
end
-
+
% Add sub node instances (linked types) to the Node container.
function addSubNodes(obj, instance)
% Add links.
@@ -552,7 +566,7 @@ function addSubNodes(obj, instance)
obj.addNode(embeddedInstances{i}, 'AddSubNodesOnly', true);
end
end
-
+
function identifier = getBlankNodeIdentifier(obj)
fmt = '_:%06d';
identifier = length(obj) + 1;
@@ -567,19 +581,19 @@ function initializeFromInstances(obj, instance)
isFilePath = @(x) (ischar(x) || isstring(x)) && isfile(x);
isFolderPath = @(x) (ischar(x) || isstring(x)) && isfolder(x);
isMetadata = @(x) openminds.utility.isInstance(x);
-
+
% Initialize from file(s)
if all( cellfun(isFilePath, instance) )
obj.load(instance{:})
-
+
% Initialize from folder
elseif all( cellfun(isFolderPath, instance) )
obj.load(instance{:})
-
+
% Initialize from instance(s)
elseif all( cellfun(isMetadata, instance) )
obj.add(instance{:});
-
+
else
ME = MException(...
'OPENMINDS_MATLAB:Collection:InvalidInstanceSpecification', ...
@@ -597,7 +611,7 @@ function initializeFromInstances(obj, instance)
if obj.NumTypes > 0
typeKeys = obj.TypeMap.keys;
-
+
isMatch = strcmp(typeKeys, instanceType.ClassName);
if any(isMatch)
if isa(obj.TypeMap, 'dictionary')
@@ -615,9 +629,9 @@ function initializeFromInstances(obj, instance)
instanceKeys = {};
return
end
-
+
existingKeys = obj.Nodes.keys();
-
+
% Sanity check, make sure all keys exist in Nodes dictionary
assert( all( ismember( instanceKeys, existingKeys ) ), ...
'TypeMap has too many keys' )
diff --git a/code/+openminds/getpref.m b/code/+openminds/getpref.m
index adf216da..f4152a68 100644
--- a/code/+openminds/getpref.m
+++ b/code/+openminds/getpref.m
@@ -20,6 +20,7 @@
[ ...
"PropertyDisplayMode", ...
"DocLinkTarget", ...
+ "AddControlledInstanceToCollection", ...
"" ...
])...
} = ""
diff --git a/code/+openminds/instanceFromIRI.m b/code/+openminds/instanceFromIRI.m
index 83ef6d99..f2a82985 100644
--- a/code/+openminds/instanceFromIRI.m
+++ b/code/+openminds/instanceFromIRI.m
@@ -35,7 +35,7 @@
end
[typeEnum, instanceName] = openminds.utility.parseInstanceIRI(IRI);
-
+
if contains(typeEnum.ClassName, "controlledterms")
instance = feval(typeEnum.ClassName, IRI);
else
diff --git a/code/+openminds/setpref.m b/code/+openminds/setpref.m
index a046de66..da26292d 100644
--- a/code/+openminds/setpref.m
+++ b/code/+openminds/setpref.m
@@ -18,7 +18,7 @@
end
pref = openminds.utility.Preferences.getSingleton;
-
+
prefNames = fieldnames(prefValues);
for i = 1:numel(prefNames)
preferenceName = prefNames{i};
diff --git a/code/+openminds/startup.m b/code/+openminds/startup.m
index 53af3747..8dbc387e 100644
--- a/code/+openminds/startup.m
+++ b/code/+openminds/startup.m
@@ -3,7 +3,7 @@ function startup(version)
%
% This function ensures that only one version of openMINDS schema classes
% are on MATLAB's search path.
-
+
arguments
version (1,1) openminds.internal.utility.VersionNumber ...
{openminds.mustBeValidVersion(version)} = "latest"
@@ -14,7 +14,7 @@ function startup(version)
% NB: Assumes this function is located in code/+openminds:
codePath = fileparts( fileparts( mfilename('fullpath') ) );
addpath( fullfile(codePath, 'internal') )
-
+
% Run internal function that correctly configures the search path
openminds.selectOpenMindsVersion(version)
fprintf(['Added classes for version "%s" of the openMINDS metadata model ' ...
diff --git a/code/+openminds/toolboxversion.m b/code/+openminds/toolboxversion.m
index 2471e19f..17dc4647 100644
--- a/code/+openminds/toolboxversion.m
+++ b/code/+openminds/toolboxversion.m
@@ -5,7 +5,7 @@
contentsFile = fullfile(rootPath, 'Contents.m');
fileStr = fileread(contentsFile);
-
+
% First try to get a version with a sub-patch version number
matchedStr = regexp(fileStr, 'Version \d*\.\d*\.\d*.\d*(?= )', 'match');
diff --git a/code/internal/+openminds/+utility/Preferences.m b/code/internal/+openminds/+utility/Preferences.m
index 8b6ad1c0..968fa0b3 100644
--- a/code/internal/+openminds/+utility/Preferences.m
+++ b/code/internal/+openminds/+utility/Preferences.m
@@ -10,12 +10,16 @@
% - "help" : MATLAB docstrings
% - "online" : Online ReadTheDocs
% documentation
+% AddControlledInstanceToCollection (logical): Whether to add
+% openMINDS controlled instances to a
+% collection
properties (SetObservable)
PropertyDisplayMode (1,1) string ...
{mustBeMember(PropertyDisplayMode, ["all", "non-empty"])} = "all"
DocLinkTarget (1,1) string ...
{mustBeMember(DocLinkTarget, ["help", "online"])} = "online"
+ AddControlledInstanceToCollection (1,1) logical = true
end
properties (Constant, Access = private)