Skip to content

Commit

Permalink
Merge branch 'master' into update-readme-2024
Browse files Browse the repository at this point in the history
ehennestad authored Dec 9, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
2 parents 149c566 + 42e9995 commit d7bfd71
Showing 173 changed files with 11,360 additions and 1,319 deletions.
2 changes: 1 addition & 1 deletion +file/Attribute.m
Original file line number Diff line number Diff line change
@@ -48,7 +48,7 @@
else
obj.value = [];
obj.readonly = false;
end
end

if isKey(source, 'dims')
obj.dimnames = source('dims');
41 changes: 32 additions & 9 deletions +file/Dataset.m
Original file line number Diff line number Diff line change
@@ -6,6 +6,8 @@
dtype;
isConstrainedSet;
required;
value;
readonly; %determines whether value can be changed or not
scalar;
shape;
dimnames;
@@ -22,12 +24,15 @@
obj.type = '';
obj.dtype = 'any';
obj.required = true;
obj.value = [];
obj.readonly = false;
obj.scalar = true;
obj.definesType = false;

obj.shape = {};
obj.dimnames = {};
obj.attributes = [];


if nargin < 1
return;
@@ -42,6 +47,20 @@
if isKey(source, nameKey)
obj.name = source(nameKey);
end

% Todo: same as for attribute, should consolidate
valueKey = 'value';
defaultKey = 'default_value';
if isKey(source, defaultKey)
obj.value = source(defaultKey);
obj.readonly = false;
elseif isKey(source, valueKey)
obj.value = source(valueKey);
obj.readonly = true;
else
obj.value = [];
obj.readonly = false;
end

typeKeys = {'neurodata_type_def', 'data_type_def'};
parentKeys = {'neurodata_type_inc', 'data_type_inc'};
@@ -121,15 +140,19 @@

%constrained
% error unless it defines the object.

if isempty(obj.type)
error('You shouldn''t be calling getProps on an untyped dataset');
end

if obj.isConstrainedSet && ~obj.definesType
error('You shouldn''t be calling getProps on a constrained dataset');
end


assert(...
~isempty(obj.type), ...
'NWB:Dataset:UnsupportedOperation', ...
'The method `getProps` should not be called on an untyped dataset.' ...
);

assert( ...
~obj.isConstrainedSet || obj.definesType, ...
'NWB:Dataset:UnsupportedOperation', ...
'The method `getProps` should not be called on constrained dataset.' ...
);

if ~isempty(obj.dtype)
props('data') = obj.dtype;
end
8 changes: 7 additions & 1 deletion +file/Group.m
Original file line number Diff line number Diff line change
@@ -146,8 +146,14 @@
%should never happen

if obj.isConstrainedSet && ~obj.definesType
error('getProps shouldn''t be called on a constrained set.');
error('NWB:Group:UnsupportedOperation', ...
'The method `getProps` should not be called on a constrained dataset.');
end
assert( ...
~obj.isConstrainedSet || obj.definesType, ...
'NWB:Group:UnsupportedOperation', ...
'The method `getProps` should not be called on a constrained group.' ...
);

%datasets
for i=1:length(obj.datasets)
19 changes: 13 additions & 6 deletions +file/cloneNwbFileClass.m
Original file line number Diff line number Diff line change
@@ -5,14 +5,21 @@ function cloneNwbFileClass(typeFileName, fullTypeName)

nwbFilePath = which('NwbFile');
installPath = fileparts(nwbFilePath);
fileId = fopen(nwbFilePath);
text = strrep(char(fread(fileId) .'),...
'NwbFile < types.core.NWBFile',...
nwbFileClassDef = fileread(nwbFilePath);

% Update superclass name
updatedNwbFileClassDef = strrep(nwbFileClassDef, ...
'NwbFile < types.core.NWBFile', ...
sprintf('NwbFile < %s', fullTypeName));
fclose(fileId);

% Update call to superclass constructor
updatedNwbFileClassDef = strrep(updatedNwbFileClassDef, ...
'obj = [email protected]', ...
sprintf('obj = obj@%s', fullTypeName));

fileId = fopen(fullfile(installPath, [typeFileName '.m']), 'W');
fwrite(fileId, text);
fwrite(fileId, updatedNwbFileClassDef);
fclose(fileId);
end

rehash();
end
12 changes: 9 additions & 3 deletions +file/fillClass.m
Original file line number Diff line number Diff line change
@@ -33,16 +33,22 @@
optional = [optional {propertyName}];
end

if isa(prop, 'file.Attribute')
if isa(prop, 'file.Attribute') || isa(prop, 'file.Dataset')
if prop.readonly
readonly = [readonly {propertyName}];
end

if ~isempty(prop.value)
defaults = [defaults {propertyName}];
if isa(prop, 'file.Attribute')
defaults = [defaults {propertyName}];
else % file.Dataset
if isRequired || all(isPropertyRequired)
defaults = [defaults {propertyName}];
end
end
end

if ~isempty(prop.dependent)
if isa(prop, 'file.Attribute') && ~isempty(prop.dependent)
%extract prefix
parentName = strrep(propertyName, ['_' prop.name], '');
parent = classprops(parentName);
5 changes: 3 additions & 2 deletions +file/fillConstructor.m
Original file line number Diff line number Diff line change
@@ -100,7 +100,8 @@
end
end

%warn for missing namespaces/property types
% warn for missing namespaces/property types
warningId = 'NWB:ClassGenerator:NamespaceOrTypeNotFound';
warnmsg = ['`' parentName '`''s constructor is unable to check for type `%1$s` ' ...
'because its namespace or type specifier could not be found. Try generating ' ...
'the namespace or class definition for type `%1$s` or fix its schema.'];
@@ -109,7 +110,7 @@
invalidWarn = invalid & (dynamicConstrained | isAnonymousType) & ~isAttribute;
invalidVars = varnames(invalidWarn);
for i=1:length(invalidVars)
warning(warnmsg, invalidVars{i});
warning(warningId, warnmsg, invalidVars{i});
end
varnames = lower(varnames);

2 changes: 2 additions & 0 deletions +file/fillCustomConstraint.m
Original file line number Diff line number Diff line change
@@ -12,9 +12,11 @@
customConstraintStr = sprintf( [...
'function checkCustomConstraint(obj)\n', ...
' assert(~isempty(obj.timestamps) || ~isempty(obj.starting_time), ...\n', ...
' ''NWB:TimeSeries:TimeNotSpecified'', ...\n ', ...
' "''timestamps'' or ''starting_time'' must be specified")\n', ...
' if ~isempty(obj.starting_time)\n', ...
' assert(~isempty(obj.starting_time_rate), ...\n', ...
' ''NWB:TimeSeries:RateMissing'', ...\n', ...
' "''starting_time_rate'' must be specified when ''starting_time'' is specified")\n', ...
' end\n', ...
'end'] );
10 changes: 3 additions & 7 deletions +file/fillExport.m
Original file line number Diff line number Diff line change
@@ -21,9 +21,6 @@
for i = 1:length(propertyNames)
propertyName = propertyNames{i};
pathProps = traverseRaw(propertyName, RawClass);
if isempty(pathProps)
keyboard;
end
prop = pathProps{end};
elideProps = pathProps(1:end-1);
elisions = cell(length(elideProps),1);
@@ -84,11 +81,10 @@
path = {};

if isa(RawClass, 'file.Dataset')
if isempty(RawClass.attributes)
return;
if ~isempty(RawClass.attributes)
matchesAttribute = strcmp({RawClass.attributes.name}, propertyName);
path = {RawClass.attributes(matchesAttribute)};
end
matchesAttribute = strcmp({RawClass.attributes.name}, propertyName);
path = {RawClass.attributes(matchesAttribute)};
return;
end

38 changes: 20 additions & 18 deletions +file/fillProps.m
Original file line number Diff line number Diff line change
@@ -52,28 +52,14 @@
typeStr = ['Table with columns: (', strjoin(columnDocStr, ', '), ')'];
elseif isa(prop, 'file.Attribute')
if isa(prop.dtype, 'containers.Map')
switch prop.dtype('reftype')
case 'region'
refTypeName = 'Region';
case 'object'
refTypeName = 'Object';
otherwise
error('Invalid reftype found whilst filling Constructor prop docs.');
end
typeStr = sprintf('%s Reference to %s', refTypeName, prop.dtype('target_type'));
assertValidRefType(prop.dtype('reftype'))
typeStr = sprintf('%s reference to %s', capitalize(prop.dtype('reftype')), prop.dtype('target_type'));
else
typeStr = prop.dtype;
end
elseif isa(prop, 'containers.Map')
switch prop('reftype')
case 'region'
refTypeName = 'region';
case 'object'
refTypeName = 'object';
otherwise
error('Invalid reftype found whilst filling Constructor prop docs.');
end
typeStr = sprintf('%s Reference to %s', refTypeName, prop('target_type'));
assertValidRefType(prop('reftype'))
typeStr = sprintf('%s reference to %s', capitalize(prop('reftype')), prop('target_type'));
elseif isa(prop, 'file.interface.HasProps')
typeStrCell = cell(size(prop));
for iProp = 1:length(typeStrCell)
@@ -106,4 +92,20 @@
if nargin >= 2
propStr = [propName ' = ' propStr];
end
end

function assertValidRefType(referenceType)
arguments
referenceType (1,1) string
end
assert( ismember(referenceType, ["region", "object"]), ...
'NWB:ClassGenerator:InvalidRefType', ...
'Invalid reftype found while filling description for class properties.')
end

function word = capitalize(word)
arguments
word (1,:) char
end
word(1) = upper(word(1));
end
56 changes: 28 additions & 28 deletions +file/fillValidators.m
Original file line number Diff line number Diff line change
@@ -4,8 +4,8 @@
nm = propnames{i};
prop = props(nm);


if isa(prop, 'file.Attribute') && prop.readonly && ~isempty(prop.value)
if (isa(prop, 'file.Attribute') || isa(prop, 'file.Dataset')) ...
&& prop.readonly && ~isempty(prop.value)
% Need to add a validator for inherited and readonly properties. In
% the superclass these properties might not be read only and due to
% inheritance its not possible to change property attributes
@@ -155,34 +155,19 @@
fillDimensionValidation(prop.dtype, prop.shape)...
}, newline);
elseif prop.isConstrainedSet
try
fullname = namespaceReg.getFullClassName(prop.type);
catch ME
if ~endsWith(ME.identifier, 'Namespace:NotFound')
rethrow(ME);
end

warning('NWB:Fill:Validators:NamespaceNotFound',...
['Namespace could not be found for type `%s`.' ...
' Skipping Validation for property `%s`.'], prop.type, name);
return;
fullname = getFullClassName(namespaceReg, prop.type, name);
if isempty(fullname)
return
end

unitValidationStr = strjoin({unitValidationStr...
['constrained = { ''' fullname ''' };']...
['types.util.checkSet(''' name ''', struct(), constrained, val);']...
}, newline);
else
try
fullname = namespaceReg.getFullClassName(prop.type);
catch ME
if ~endsWith(ME.identifier, 'Namespace:NotFound')
rethrow(ME);
end

warning('NWB:Fill:Validators:NamespaceNotFound',...
['Namespace could not be found for type `%s`.' ...
' Skipping Validation for property `%s`.'], prop.type, name);
return;
fullname = getFullClassName(namespaceReg, prop.type, name);
if isempty(fullname)
return
end
unitValidationStr = [unitValidationStr newline fillDtypeValidation(name, fullname)];
end
@@ -240,7 +225,7 @@
' return;'...
'end'...
'if ~istable(val) && ~isstruct(val) && ~isa(val, ''containers.Map'')'...
[' error(''Property `' name '` must be a table,struct, or containers.Map.'');']...
[' error(''NWB:Type:InvalidPropertyType'', ''Property `' name '` must be a table, struct, or containers.Map.'');']...
'end'...
'vprops = struct();'...
}, newline);
@@ -294,8 +279,7 @@

classNameSplit = strsplit(className, '.');
shortName = classNameSplit{end};

errorStr = sprintf( 'error(''Unable to set the ''''%s'''' property of class ''''<a href="matlab:doc %s">%s</a>'''' because it is read-only.'')', name, className, shortName);
errorStr = sprintf( 'error(''NWB:Type:ReadOnlyProperty'', ''Unable to set the ''''%s'''' property of class ''''<a href="matlab:doc %s">%s</a>'''' because it is read-only.'')', name, className, shortName);

if ischar(value)
condition = strjoin({ ...
@@ -311,11 +295,27 @@
% Note: According to the documentation for Attribute specification keys
% (https://schema-language.readthedocs.io/en/latest/description.html#sec-attributes-spec),
% the above cases should be sufficient.
error('Unhandled case')
error('NWB:ClassGenerator:ReadOnlyValidatorNotImplemented', ...
'Read-only validator is not implemented for values of type "%s"', class(value))
end

fdvstr = strjoin({...
condition, ...
sprintf(' %s', errorStr), ...
'end' }, newline );
end

function fullname = getFullClassName(namespaceReg, propType, name)
fullname = '';
try
fullname = namespaceReg.getFullClassName(propType);
catch ME
if ~endsWith(ME.identifier, 'Namespace:NotFound')
rethrow(ME);
end

warning('NWB:Fill:Validators:NamespaceNotFound',...
['Namespace could not be found for type `%s`.' ...
' Skipping Validation for property `%s`.'], propType, name);
end
end
Loading

0 comments on commit d7bfd71

Please sign in to comment.