|
4 | 4 | nm = propnames{i}; |
5 | 5 | prop = props(nm); |
6 | 6 |
|
7 | | - if (isa(prop, 'file.Attribute') || isa(prop, 'file.Dataset')) ... |
8 | | - && prop.readonly && ~isempty(prop.value) |
| 7 | + if file.internal.isPropertyReadonly(prop) && ~isempty(prop.value) |
9 | 8 | % Need to add a validator for inherited and readonly properties. In |
10 | 9 | % the superclass these properties might not be read-only and due to |
11 | 10 | % inheritance rules in MATLAB it is not possible to change property |
|
16 | 15 | else |
17 | 16 | continue |
18 | 17 | end |
19 | | - elseif isa(prop, 'file.Link') && ~prop.isConstrainedSet |
20 | | - validationBody = fillLinkValidation(nm, prop, namespaceReg); |
21 | | - else |
22 | | - if startsWith(class(prop), 'file.') |
23 | | - validationBody = fillUnitValidation(nm, prop, namespaceReg); |
24 | | - else % primitive type |
25 | | - validationBody = fillDtypeValidation(nm, prop, namespaceReg); |
26 | | - end |
| 18 | + elseif startsWith(class(prop), 'file.') % HDMF primitive type |
| 19 | + validationBody = fillUnitValidation(nm, prop, namespaceReg); |
| 20 | + else % MATLAB primitive type |
| 21 | + validationBody = fillDtypeValidation(nm, prop, namespaceReg); |
27 | 22 | end |
28 | 23 |
|
29 | 24 | headerStr = ['function val = validate_' nm '(obj, val)']; |
|
33 | 28 | functionStr = strjoin({headerStr ... |
34 | 29 | file.addSpaces(strtrim(validationBody), 4) 'end'}, newline); |
35 | 30 | end |
36 | | - validationStr = [validationStr newline functionStr]; |
| 31 | + validationStr = strcat(validationStr, [newline, functionStr]); |
37 | 32 | end |
38 | 33 | end |
39 | 34 |
|
|
61 | 56 | fillDtypeValidation(name, prop.dtype, namespaceReg)... |
62 | 57 | fillDimensionValidation(name, prop.shape)... |
63 | 58 | }, newline); |
64 | | - else % Link |
65 | | - fullTypeName = namespaceReg.getFullClassName(prop.type); |
66 | | - unitValidationStr = fillDtypeValidation(name, fullTypeName, namespaceReg); |
| 59 | + elseif isa(prop, 'file.Link') % Link |
| 60 | + unitValidationStr = fillLinkValidation(name, prop, namespaceReg); |
| 61 | + else |
| 62 | + error('NWB:InternalError', 'Unexpected property specification') |
67 | 63 | end |
68 | 64 | end |
69 | 65 |
|
|
77 | 73 | namedprops = struct(); |
78 | 74 | constraints = {}; |
79 | 75 | if isempty(prop.type) |
| 76 | + warning('NWB:FillValidators:ValidationNotImplemented', ... |
| 77 | + ['Detected a group-based data type (''%s'') with an untyped ', ... |
| 78 | + 'subgroup. Validation logic for checking shape or quantity ', ... |
| 79 | + 'of nested elements is not implemented '], prop.name) |
| 80 | + |
80 | 81 | %% process datasets |
81 | 82 | % if type, check if constrained |
82 | 83 | % if constrained, add to constr |
83 | 84 | % otherwise, check type once |
84 | 85 | % otherwise, check dtype |
85 | 86 | for iDataset = 1:length(prop.datasets) |
86 | | - dataset = p.datasets(iDataset); |
| 87 | + dataset = prop.datasets(iDataset); |
87 | 88 |
|
88 | 89 | if isempty(dataset.type) |
89 | 90 | namedprops.(dataset.name) = dataset.dtype; |
|
104 | 105 | % otherwise, error. This shouldn't happen. |
105 | 106 | for iSubGroup = 1:length(prop.subgroups) |
106 | 107 | subGroup = prop.subgroups(iSubGroup); |
| 108 | + assert(~isempty(subGroup.type), 'Unsupported case with two nested untyped groups'); |
107 | 109 | subGroupFullName = namespaceReg.getFullClassName(subGroup.type); |
108 | | - assert(~isempty(subGroup.type), 'Weird case with two untyped groups'); |
109 | 110 |
|
110 | 111 | if isempty(subGroup.name) |
111 | 112 | constraints{end+1} = subGroupFullName; |
|
124 | 125 | for iLink = 1:length(prop.links) |
125 | 126 | Link = prop.links(iLink); |
126 | 127 | namespace = namespaceReg.getNamespace(Link.type); |
127 | | - namedprops.(Link.name) = ['types.', namespace, '.', Link.type]; |
| 128 | + namedprops.(Link.name) = char(matnwb.common.composeFullClassName(namespace.name, Link.type)); |
128 | 129 | end |
129 | 130 | else |
130 | 131 | constraints{end+1} = namespaceReg.getFullClassName(prop.type); |
|
152 | 153 | end |
153 | 154 |
|
154 | 155 | function unitValidationStr = fillDatasetValidation(name, prop, namespaceReg) |
| 156 | + |
155 | 157 | unitValidationStr = ''; |
156 | | - if isempty(prop.type) |
157 | | - unitValidationStr = strjoin({unitValidationStr... |
158 | | - fillDtypeValidation(name, prop.dtype, namespaceReg)... |
159 | | - fillDimensionValidation(name, prop.shape)... |
160 | | - }, newline); |
161 | | - elseif prop.isConstrainedSet |
| 158 | + validationLines = {}; |
| 159 | + |
| 160 | + if prop.isConstrainedSet |
162 | 161 | fullname = getFullClassName(namespaceReg, prop.type, name); |
163 | 162 | if isempty(fullname) |
164 | 163 | return |
165 | 164 | end |
166 | 165 |
|
167 | | - unitValidationStr = strjoin({unitValidationStr... |
168 | | - ['constrained = { ''' fullname ''' };']... |
169 | | - ['types.util.checkSet(''' name ''', struct(), constrained, val);']... |
170 | | - }, newline); |
| 166 | + validationLines = [validationLines ... |
| 167 | + {['constrained = { ''' fullname ''' };']} ... |
| 168 | + {['types.util.checkSet(''' name ''', struct(), constrained, val);']} ... |
| 169 | + ]; |
171 | 170 | else |
172 | | - fullname = getFullClassName(namespaceReg, prop.type, name); |
173 | | - if isempty(fullname) |
174 | | - return |
| 171 | + if ~isempty(prop.type) % Dataset class like e.g. VectorData |
| 172 | + fullname = getFullClassName(namespaceReg, prop.type, name); |
| 173 | + if isempty(fullname) |
| 174 | + return |
| 175 | + end |
| 176 | + |
| 177 | + validationLines = [validationLines ... |
| 178 | + {fillTypeValidation(name, fullname)}]; |
| 179 | + |
| 180 | + datasetValidationLines = [... |
| 181 | + {fillDtypeValidation(name, prop.dtype, namespaceReg)}, ... |
| 182 | + {fillDimensionValidation(name, prop.shape)} ]; |
| 183 | + |
| 184 | + datasetValidationLines(cellfun('isempty', datasetValidationLines)) = []; |
| 185 | + datasetValidationLines = indentLines(datasetValidationLines, 4); |
| 186 | + |
| 187 | + if ~isempty(datasetValidationLines) |
| 188 | + validationLines{end+1} = 'if ~isempty(val)'; |
| 189 | + validationLines{end+1} = ' [val, originalVal] = types.util.unwrapValue(val);'; |
| 190 | + validationLines = [validationLines, datasetValidationLines]; |
| 191 | + validationLines{end+1} = ' val = types.util.rewrapValue(val, originalVal);'; |
| 192 | + validationLines{end+1} = 'end'; |
| 193 | + end |
| 194 | + else |
| 195 | + validationLines = [validationLines ... |
| 196 | + {fillDtypeValidation(name, prop.dtype, namespaceReg)} ... |
| 197 | + {fillDimensionValidation(name, prop.shape)} ]; |
175 | 198 | end |
176 | | - unitValidationStr = [unitValidationStr newline fillDtypeValidation(name, fullname, namespaceReg)]; |
177 | 199 | end |
| 200 | + validationLines(cellfun('isempty', validationLines)) = []; |
| 201 | + unitValidationStr = strjoin(validationLines, newline); |
178 | 202 | end |
179 | 203 |
|
180 | 204 | function validationStr = fillLinkValidation(name, prop, namespaceReg) |
|
186 | 210 | end |
187 | 211 |
|
188 | 212 | function fdvstr = fillDimensionValidation(name, shape) |
| 213 | + |
| 214 | + if isnumeric(shape) && isnan(shape) % Any shape is allowed |
| 215 | + fdvstr = ''; return |
| 216 | + end |
189 | 217 |
|
190 | 218 | if isnumeric(shape) && isnan(shape) % Any shape is allowed |
191 | 219 | fdvstr = ''; return |
|
213 | 241 | fdvstr = sprintf('types.util.validateShape(''%s'', %s, val)', name, validShapeStr); |
214 | 242 | end |
215 | 243 |
|
| 244 | +function validationStr = fillTypeValidation(name, type) |
| 245 | + validationStr = ['types.util.checkType(''' name ''', ''' type ''', val);']; |
| 246 | +end |
| 247 | + |
216 | 248 | %NOTE: can return empty strings |
217 | 249 | function fdvstr = fillDtypeValidation(name, type, namespaceReg) |
218 | 250 | if isstruct(type) |
219 | 251 | fnames = fieldnames(type); |
220 | 252 | fdvstr = strjoin({... |
221 | | - 'if isempty(val) || isa(val, ''types.untyped.DataStub'')'... |
222 | | - ' return;'... |
223 | | - 'end'... |
224 | | - 'if ~istable(val) && ~isstruct(val) && ~isa(val, ''containers.Map'')'... |
225 | | - [' error(''NWB:Type:InvalidPropertyType'', ''Property `' name '` must be a table, struct, or containers.Map.'');']... |
226 | | - 'end'... |
227 | | - 'vprops = struct();'... |
| 253 | + 'if isempty(val)'... |
| 254 | + ' % skip validation for empty values'... |
| 255 | + 'else'... |
| 256 | + ' vprops = struct();'... |
228 | 257 | }, newline); |
229 | 258 | vprops = cell(length(fnames),1); |
230 | 259 | for i=1:length(fnames) |
|
234 | 263 | else |
235 | 264 | typeval = type.(nm); |
236 | 265 | end |
237 | | - vprops{i} = ['vprops.' nm ' = ''' typeval ''';']; |
| 266 | + vprops{i} = [' vprops.' nm ' = ''' typeval ''';']; |
238 | 267 | end |
239 | 268 | fdvstr = [fdvstr, newline, strjoin(vprops, newline), newline, ... |
240 | | - 'val = types.util.checkDtype(''' name ''', vprops, val);']; |
| 269 | + ' val = types.util.checkDtype(''' name ''', vprops, val);' ... |
| 270 | + newline, 'end']; |
241 | 271 | elseif isReferenceType(type) |
242 | 272 | fdvstr = fillReferenceTypeValidation(name, type, namespaceReg); |
243 | 273 | else |
|
337 | 367 | end |
338 | 368 | fullReferenceClassName = ['types.untyped.' referenceClassName]; |
339 | 369 | end |
| 370 | + |
| 371 | +function indentedLines = indentLines(lines, numIndents) |
| 372 | + if iscell(lines) |
| 373 | + indentedLines = cellfun(@(c) indentSingle(c, numIndents), lines); |
| 374 | + else |
| 375 | + indentedLines = indentSingle(lines, numIndents); |
| 376 | + end |
| 377 | + |
| 378 | + function indentedLine = indentSingle(line, numIndents) |
| 379 | + splitLine = split(line, newline); |
| 380 | + splitLine = strcat({repmat(' ', 1, numIndents)}, splitLine); |
| 381 | + indentedLine = join(splitLine, newline); |
| 382 | + end |
| 383 | +end |
0 commit comments