Skip to content

Commit 8f1e806

Browse files
authored
Merge branch 'main' into 635-customizable-chunking
2 parents dcf6ede + 3d252da commit 8f1e806

6 files changed

+76
-7
lines changed

+io/+spec/validateEmbeddedSpecifications.m

+7-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,13 @@ function checkMissingNamespaces(expectedNamespaceNames, embeddedNamespaceNames)
3030
missingNamespaces = setdiff(expectedNamespaceNames, embeddedNamespaceNames);
3131
if ~isempty(missingNamespaces)
3232
missingNamespacesStr = strjoin(" " + string(missingNamespaces), newline);
33-
warning('NWB:validators:MissingEmbeddedNamespace', 'Namespace is missing:\n%s', missingNamespacesStr)
33+
warning('NWB:validators:MissingEmbeddedNamespace', ...
34+
['The following namespace specifications are not embedded in ' ...
35+
'the file:\n%s\n' ...
36+
'To facilitate reading and validating the file across systems, it is ' ...
37+
'recommended to embed the specifications for these namespaces. ' ...
38+
'Please generate the missing extensions (using generateCore or ' ...
39+
'generateExtension) and then re-export the file.'], missingNamespacesStr)
3440
end
3541
end
3642

+io/mapData2H5.m

+7
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,14 @@
1313
|| all(cellfun('isclass', data, 'string')) ...
1414
, 'NWB:MapData:NonCellStr', ['Cell arrays must be cell arrays of character vectors. ' ...
1515
'Cell arrays of other types are not supported.']);
16+
elseif isstring(data)
17+
if isscalar(data)
18+
data = char(data);
19+
else
20+
data = cellstr(data);
21+
end
1622
end
23+
1724
tid = io.getBaseType(class(data));
1825

1926
% max size is always unlimited

+tests/+unit/dynamicTableTest.m

+1-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ function testToTableForTableImportedFromFile(testCase)
103103

104104
nwbExport(nwb, fileName)
105105

106-
nwbIn = nwbRead(fileName);
106+
nwbIn = nwbRead(fileName, "ignorecache");
107107

108108
T = nwbIn.acquisition.get('DynamicTable').toTable();
109109
testCase.verifyClass(T, 'table')

+tests/+unit/nwbExportTest.m

+39-2
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,9 @@ function testEmbeddedSpecs(testCase)
169169
nwbFilePath = testCase.getRandomFilename();
170170
nwbExport(nwb, nwbFilePath);
171171

172-
% Verify that no namespaces were embedded in file
172+
% Verify that core and hdmf-common were embedded in "empty" file
173173
embeddedNamespaces = io.spec.listEmbeddedSpecNamespaces(nwbFilePath);
174-
testCase.verifyEmpty(embeddedNamespaces)
174+
testCase.verifyEqual(sort(embeddedNamespaces), {'core', 'hdmf-common'})
175175

176176
ts = tests.factory.TimeSeriesWithTimestamps();
177177
nwb.acquisition.set('test', ts);
@@ -249,5 +249,42 @@ function testWarnIfMissingNamespaceSpecification(testCase)
249249
@() nwbExport(nwb, nwbFilePath), ...
250250
'NWB:validators:MissingEmbeddedNamespace')
251251
end
252+
253+
function testExportFileWithStringDataType(testCase)
254+
nwb = tests.factory.NWBFile();
255+
256+
generalExperimenter = ["John Doe", "Jane Doe"];
257+
generalExperimentDescription = "Test with string data types";
258+
nwb.general_experimenter = generalExperimenter;
259+
nwb.general_experiment_description = generalExperimentDescription;
260+
261+
ts = tests.factory.TimeSeriesWithTimestamps();
262+
ts.comments = "String comment";
263+
ts.data_unit = "test";
264+
265+
nwb.acquisition.set("TimeSeries", ts);
266+
nwbFilename = testCase.getRandomFilename();
267+
nwbExport(nwb, nwbFilename);
268+
269+
nwbIn = nwbRead(nwbFilename, 'ignorecache');
270+
271+
testCase.assertEqual( ...
272+
string( nwbIn.general_experimenter.load())', ...
273+
generalExperimenter)
274+
275+
testCase.assertEqual( ...
276+
string(nwbIn.general_experiment_description)', ...
277+
generalExperimentDescription)
278+
279+
tsIn = nwbIn.acquisition.get("TimeSeries");
280+
281+
testCase.assertEqual( ...
282+
string(tsIn.comments), ...
283+
ts.comments)
284+
285+
testCase.assertEqual( ...
286+
string(tsIn.data_unit), ...
287+
ts.data_unit)
288+
end
252289
end
253290
end

NwbFile.m

+19-2
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ function export(obj, filename, mode)
123123
arguments
124124
obj (1,1) NwbFile
125125
options.IncludeParentTypes (1,1) logical = false
126+
options.IncludeNwbFile (1,1) logical = false
126127
end
127128

128129
objectMap = searchProperties(containers.Map, obj, '', '');
@@ -136,6 +137,14 @@ function export(obj, filename, mode)
136137

137138
nwbTypeNames = objectClassNames(keep & ~ignore);
138139

140+
if options.IncludeNwbFile
141+
% Include class name for NWBFile superclass
142+
allSuperclasses = string(superclasses(obj));
143+
nwbTypeNames = [...
144+
allSuperclasses(endsWith(allSuperclasses, 'NWBFile')), ...
145+
nwbTypeNames];
146+
end
147+
139148
if options.IncludeParentTypes
140149
includedNwbTypesWithParents = string.empty;
141150
for i = 1:numel(nwbTypeNames)
@@ -152,12 +161,20 @@ function export(obj, filename, mode)
152161
function embedSpecifications(obj, output_file_id)
153162
jsonSpecs = schemes.exportJson();
154163

164+
if isempty(jsonSpecs)
165+
% Call generateCore to create cached namespaces
166+
generateCore(obj.nwb_version)
167+
jsonSpecs = schemes.exportJson();
168+
end
169+
155170
% Resolve the name of all types and parent types that are
156171
% included in this file. This will be used to filter the specs
157172
% to embed, so that only specs with used neurodata types are
158173
% embedded.
159-
includedNeurodataTypes = obj.listNwbTypes("IncludeParentTypes", true);
160-
174+
includedNeurodataTypes = obj.listNwbTypes(...
175+
"IncludeParentTypes", true, ...
176+
"IncludeNwbFile", true);
177+
161178
% Get the namespace names
162179
namespaceNames = getNamespacesForDataTypes(includedNeurodataTypes);
163180

nwbClearGenerated.m

+3-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@
3535
moduleNames = setdiff({listing.name}, {'+untyped', '+util', '.', '..'});
3636
generatedPaths = fullfile(typesPath, moduleNames);
3737
for i=1:length(generatedPaths)
38-
rmdir(generatedPaths{i}, 's');
38+
if isfolder(generatedPaths{i})
39+
rmdir(generatedPaths{i}, 's');
40+
end
3941
end
4042

4143
if options.ClearCache

0 commit comments

Comments
 (0)