1
- classdef MetaClass < handle
1
+ classdef MetaClass < handle & matlab . mixin . CustomDisplay
2
2
properties (Hidden , SetAccess = private )
3
3
metaClass_fullPath ;
4
4
end
5
-
5
+
6
+ properties (Constant , Transient , Access = protected )
7
+ REQUIRED containers.Map = containers.Map
8
+ end
9
+
6
10
methods
7
11
function obj = MetaClass(varargin )
8
12
end
43
47
44
48
methods
45
49
function refs = export(obj , fid , fullpath , refs )
50
+ % throwErrorIfCustomConstraintUnfulfilled is intentionally placed
51
+ % before throwErrorIfMissingRequiredProps.
52
+ % See file.fillCustomConstraint
53
+ obj .throwErrorIfCustomConstraintUnfulfilled(fullpath )
54
+ obj .throwErrorIfMissingRequiredProps(fullpath )
46
55
obj.metaClass_fullPath = fullpath ;
47
56
% find reference properties
48
57
propnames = properties(obj );
@@ -107,4 +116,100 @@ function warnIfPropertyAttributeNotExported(obj, propName, depPropName, fullpath
107
116
warning(warningId , warningMessage ) % #ok<SPWRN>
108
117
end
109
118
end
110
- end
119
+
120
+ methods (Access = protected ) % Override matlab.mixin.CustomDisplay
121
+ function str = getFooter(obj )
122
+ obj .displayWarningIfMissingRequiredProps();
123
+ str = ' ' ;
124
+ end
125
+ end
126
+
127
+ methods (Access = protected )
128
+ function missingRequiredProps = checkRequiredProps(obj )
129
+ missingRequiredProps = {};
130
+ requiredProps = obj .getRequiredProperties();
131
+
132
+ for i = 1 : numel(requiredProps )
133
+ thisPropName = requiredProps{i };
134
+ if isempty(obj.(thisPropName ))
135
+ missingRequiredProps{end + 1 } = thisPropName ; % #ok<AGROW>
136
+ end
137
+ end
138
+ end
139
+
140
+ function displayWarningIfMissingRequiredProps(obj )
141
+ missingRequiredProps = obj .checkRequiredProps();
142
+
143
+ % Exception: 'file_create_date' is automatically added by the
144
+ % matnwb API on export, so no need to warn if it is missing.
145
+ if isa(obj , ' types.core.NWBFile' )
146
+ missingRequiredProps = setdiff(missingRequiredProps , ' file_create_date' , ' stable' );
147
+ end
148
+
149
+ % Exception: 'id' of DynamicTable is automatically assigned if not
150
+ % specified, so no need to warn if it is missing.
151
+ if isa(obj , ' types.hdmf_common.DynamicTable' )
152
+ missingRequiredProps = setdiff(missingRequiredProps , ' id' , ' stable' );
153
+ end
154
+
155
+ if ~isempty( missingRequiredProps )
156
+ warnState = warning(' backtrace' , ' off' );
157
+ cleanerObj = onCleanup(@(s ) warning(warnState ));
158
+
159
+ propertyListStr = obj .prettyPrintPropertyList(missingRequiredProps );
160
+ warning(' NWB:RequiredPropertyMissing' , ...
161
+ ' The following required properties are missing for instance for type "%s ":\n%s ' , class(obj ), propertyListStr )
162
+ end
163
+ end
164
+
165
+ function throwErrorIfMissingRequiredProps(obj , fullpath )
166
+ missingRequiredProps = obj .checkRequiredProps();
167
+ if ~isempty( missingRequiredProps )
168
+ propertyListStr = obj .prettyPrintPropertyList(missingRequiredProps );
169
+ error(' NWB:RequiredPropertyMissing' , ...
170
+ ' The following required properties are missing for instance for type "%s " at file location "%s ":\n%s ' , class(obj ), fullpath , propertyListStr )
171
+ end
172
+ end
173
+
174
+ function throwErrorIfCustomConstraintUnfulfilled(obj , fullpath )
175
+ try
176
+ obj .checkCustomConstraint()
177
+ catch ME
178
+ error(' NWB:CustomConstraintUnfulfilled' , ...
179
+ ' The following error was caught when exporting type "%s " at file location "%s ":\n%s ' , class(obj ), fullpath , ME .message )
180
+ end
181
+ end
182
+ end
183
+
184
+ methods
185
+ function checkCustomConstraint(obj ) % #ok<MANU>
186
+ % This method is meant to be overridden by subclasses that have
187
+ % custom constraints that can not be inferred from the nwb schema.
188
+ end
189
+ end
190
+
191
+ methods (Access = private )
192
+ function requiredProps = getRequiredProperties(obj )
193
+
194
+ % Introspectively retrieve required properties and add to
195
+ % persistent map.
196
+
197
+ if isKey(obj .REQUIRED , class(obj ) )
198
+ requiredProps = obj .REQUIRED( class(obj ) );
199
+ else
200
+ mc = metaclass(obj );
201
+ propertyDescription = {mc .PropertyList .Description };
202
+ isRequired = startsWith(propertyDescription , ' REQUIRED' );
203
+ requiredProps = {mc .PropertyList(isRequired ).Name};
204
+ obj .REQUIRED( class(obj ) ) = requiredProps ;
205
+ end
206
+ end
207
+ end
208
+
209
+ methods (Static , Access = private )
210
+ function propertyListStr = prettyPrintPropertyList(propertyNames )
211
+ propertyListStr = compose(' %s ' , string(propertyNames ));
212
+ propertyListStr = strjoin(propertyListStr , newline );
213
+ end
214
+ end
215
+ end
0 commit comments