From a033816b56de483864ee3221b2c1f9935ce62ca4 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sat, 24 Jan 2026 18:23:12 +0100 Subject: [PATCH 01/16] Update fillClass.m --- +file/fillClass.m | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/+file/fillClass.m b/+file/fillClass.m index 41b55a4f6..1c89d370a 100644 --- a/+file/fillClass.m +++ b/+file/fillClass.m @@ -95,6 +95,8 @@ if isa(class, 'file.Group') && class.hasAnonGroups superclassNames{end+1} = 'matnwb.mixin.HasUnnamedGroups'; + elseif isa(class, 'file.Group') && class.hasAnonData + superclassNames{end+1} = 'matnwb.mixin.HasUnnamedGroups'; end %% return classfile string @@ -147,7 +149,7 @@ , propertyDefinitionBody ... }, newline); end - if isa(class, 'file.Group') && class.hasAnonGroups + if isa(class, 'file.Group') && (class.hasAnonGroups || class.hasAnonData) mixinPropertyBlock = createPropertyBlockForHasUnnamedGroupMixin(class); fullPropertyDefinition = strjoin(... @@ -201,8 +203,15 @@ function propertyBlockStr = createPropertyBlockForHasUnnamedGroupMixin(classInfo) isAnonGroup = arrayfun(@(x) isempty(x.name), classInfo.subgroups, 'uni', true); - anonNames = arrayfun(@(x) lower(x.type), classInfo.subgroups(isAnonGroup), 'uni', false); - + isAnonDataset = arrayfun(@(x) isempty(x.name), classInfo.datasets, 'uni', true); + + anonNames = [... + arrayfun(@(x) lower(x.type), classInfo.subgroups(isAnonGroup), 'uni', false), ... + arrayfun(@(x) lower(x.type), classInfo.datasets(isAnonDataset), 'uni', false), ... + ]; + if isempty(anonNames) + keyboard + end propertyBlockStr = strjoin({... 'properties (Access = protected)', ... sprintf(' GroupPropertyNames = {%s}', strjoin(strcat('''', anonNames, ''''), ', ') ), ... From 8b6f76e271098c13dfbafa91603d26fdc4eb98c8 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sat, 24 Jan 2026 21:24:11 +0100 Subject: [PATCH 02/16] Refactor HasUnnamedGroups property handling and add hierarchy support - Replaces the abstract protected GroupPropertyNames property with a private dependent property and a static protected method for retrieval. - Adds caching for GroupPropertyNames -Implements getGroupPropertyNamesAcrossTypeHierarchy to aggregate unnamed group property names across the class hierarchy. - Updates fillClass to generate appropriate method overrides for classes with anonymous groups or data. --- +file/fillClass.m | 24 +++++++++- +matnwb/+mixin/HasUnnamedGroups.m | 77 ++++++++++++++++++++++++++++++- 2 files changed, 99 insertions(+), 2 deletions(-) diff --git a/+file/fillClass.m b/+file/fillClass.m index 1c89d370a..fbae059c4 100644 --- a/+file/fillClass.m +++ b/+file/fillClass.m @@ -184,6 +184,16 @@ fullMethodBody = strjoin({'methods' ... file.addSpaces(methodBody, 4) 'end'}, newline); + + if isa(class, 'file.Group') && (class.hasAnonGroups || class.hasAnonData) + mixinMethodBlock = createMethodBlockForHasUnnamedGroupMixin(fullClassName); + + fullMethodBody = strjoin(... + {fullMethodBody, ... + '', ... + mixinMethodBlock}, newline); + end + template = strjoin({classDefinitionHeader fullPropertyDefinition fullMethodBody 'end'}, ... [newline newline]); end @@ -213,7 +223,19 @@ keyboard end propertyBlockStr = strjoin({... - 'properties (Access = protected)', ... + 'properties (Constant, Access = private)', ... sprintf(' GroupPropertyNames = {%s}', strjoin(strcat('''', anonNames, ''''), ', ') ), ... 'end'}, newline); end + +function methodBlockStr = createMethodBlockForHasUnnamedGroupMixin(fullClassName) + methodBlockStr = strjoin({... + '% Override getGroupPropertyNames of matnwb.mixin.HasUnnamedGroups superclass', ... + 'methods (Static, Access = protected)', ... + ' function propertyNames = getGroupPropertyNames()', ... + ' % getGroupPropertyNames - Get property names for all unnamed groups of this type', ... + sprintf(' propertyNames = matnwb.mixin.HasUnnamedGroups.getGroupPropertyNamesAcrossTypeHierarchy(''%s'');', fullClassName), ... + ' end', ... + 'end'}, newline); +end + diff --git a/+matnwb/+mixin/HasUnnamedGroups.m b/+matnwb/+mixin/HasUnnamedGroups.m index d0bccefad..d517d4f3a 100644 --- a/+matnwb/+mixin/HasUnnamedGroups.m +++ b/+matnwb/+mixin/HasUnnamedGroups.m @@ -46,9 +46,14 @@ % are no schemas in NWB where Anon sets are used, and this class therefore % does not support contained Anon sets. - properties (Abstract, Access = protected, Transient) + properties (Access = private, Dependent, Transient) % GroupPropertyNames - String array of property names that contain Sets GroupPropertyNames (1,:) string + end + + properties (Access = private) + % GroupPropertyNames_ - Cached value backing the dependent property GroupPropertyNames + GroupPropertyNames_ (1,:) string end properties (Access = private, Transient) @@ -56,6 +61,13 @@ PropertyManager matnwb.utility.DynamicPropertyManager end + methods (Static, Access = protected) + % Subclasses should override this method + function propertyNames = getGroupPropertyNames() + propertyNames = string.empty; + end + end + % Constructor methods function obj = HasUnnamedGroups() @@ -153,6 +165,16 @@ function setupHasUnnamedGroupsMixin(obj) obj.assignContainedSetCallbackFunctions() end end + + methods + function value = get.GroupPropertyNames(obj) + if isempty(obj.GroupPropertyNames_) + obj.GroupPropertyNames_ = obj.getGroupPropertyNames(); + end + value = obj.GroupPropertyNames_; + end + end + methods (Access = private) function initializeDynamicProperties(obj) % initializeDynamicProperties - Init dynamic properties from set entries @@ -515,6 +537,59 @@ function displayAliasWarning(obj) end end end + + methods (Static, Access = protected) + function groupPropertyNames = getGroupPropertyNamesAcrossTypeHierarchy(nwbTypeName) + % getGroupPropertyNamesAcrossTypeHierarchy - Retrieve property names of unnamed groups for a specific NWB type + % + % Syntax: + % groupTypes = getGroupPropertyNamesAcrossTypeHierarchy(nwbTypeName) + % This function retrieves property names of unnamed groups associated with + % the specified NWB type name, traversing the class hierarchy to also include + % property names of unnamed groups for parent types. + % + % Input Arguments: + % nwbTypeName (1,1) string - The name of the NWB type for which property + % names of unnamed groups are to be retrieved. + % + % Output Arguments: + % groupPropertyNames - An array of property names of unnamed groups + % associated with the specified NWB type. + + arguments + nwbTypeName (1,1) string + end + + groupPropertyNames = string.empty; % Initialize an empty cell array + currentType = nwbTypeName; % Start with the specific type + + % Iterate over class and superclasses to detect property names for + % unnamed groups across the type hierarchy. + while ~strcmp(currentType, 'types.untyped.MetaClass') + + % Use MetaClass information to get class information + metaClass = meta.class.fromName(currentType); + + % Get value of GroupPropertyNames if this class is a subclass of + % the HasUnnamedGroups subclass. + if any(strcmp({metaClass.SuperclassList.Name}, 'matnwb.mixin.HasUnnamedGroups')) + isProp = strcmp({metaClass.PropertyList.Name}, 'GroupPropertyNames'); + if any(isProp) + groupPropertyNames = [groupPropertyNames, ... + string(metaClass.PropertyList(isProp).DefaultValue)]; %#ok + end + end + + if isempty(metaClass.SuperclassList) + break; % Reached the base type + end + + % Get superclass for next iteration. NWB parent type should + % always be the first superclass in the list + currentType = metaClass.SuperclassList(1).Name; + end + end + end end function ME = getNameExistsException(name, typeName) From 441f9f630b5244b1c0f6fc751305a7485ad3510f Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sat, 24 Jan 2026 21:28:56 +0100 Subject: [PATCH 03/16] Simplify - no need for method injection in subclasses of mixin --- +file/fillClass.m | 25 +------------------------ +matnwb/+mixin/HasUnnamedGroups.m | 10 ++-------- 2 files changed, 3 insertions(+), 32 deletions(-) diff --git a/+file/fillClass.m b/+file/fillClass.m index fbae059c4..7021e162c 100644 --- a/+file/fillClass.m +++ b/+file/fillClass.m @@ -185,15 +185,6 @@ fullMethodBody = strjoin({'methods' ... file.addSpaces(methodBody, 4) 'end'}, newline); - if isa(class, 'file.Group') && (class.hasAnonGroups || class.hasAnonData) - mixinMethodBlock = createMethodBlockForHasUnnamedGroupMixin(fullClassName); - - fullMethodBody = strjoin(... - {fullMethodBody, ... - '', ... - mixinMethodBlock}, newline); - end - template = strjoin({classDefinitionHeader fullPropertyDefinition fullMethodBody 'end'}, ... [newline newline]); end @@ -219,23 +210,9 @@ arrayfun(@(x) lower(x.type), classInfo.subgroups(isAnonGroup), 'uni', false), ... arrayfun(@(x) lower(x.type), classInfo.datasets(isAnonDataset), 'uni', false), ... ]; - if isempty(anonNames) - keyboard - end + propertyBlockStr = strjoin({... 'properties (Constant, Access = private)', ... sprintf(' GroupPropertyNames = {%s}', strjoin(strcat('''', anonNames, ''''), ', ') ), ... 'end'}, newline); end - -function methodBlockStr = createMethodBlockForHasUnnamedGroupMixin(fullClassName) - methodBlockStr = strjoin({... - '% Override getGroupPropertyNames of matnwb.mixin.HasUnnamedGroups superclass', ... - 'methods (Static, Access = protected)', ... - ' function propertyNames = getGroupPropertyNames()', ... - ' % getGroupPropertyNames - Get property names for all unnamed groups of this type', ... - sprintf(' propertyNames = matnwb.mixin.HasUnnamedGroups.getGroupPropertyNamesAcrossTypeHierarchy(''%s'');', fullClassName), ... - ' end', ... - 'end'}, newline); -end - diff --git a/+matnwb/+mixin/HasUnnamedGroups.m b/+matnwb/+mixin/HasUnnamedGroups.m index d517d4f3a..bd8acf609 100644 --- a/+matnwb/+mixin/HasUnnamedGroups.m +++ b/+matnwb/+mixin/HasUnnamedGroups.m @@ -61,13 +61,6 @@ PropertyManager matnwb.utility.DynamicPropertyManager end - methods (Static, Access = protected) - % Subclasses should override this method - function propertyNames = getGroupPropertyNames() - propertyNames = string.empty; - end - end - % Constructor methods function obj = HasUnnamedGroups() @@ -169,7 +162,8 @@ function setupHasUnnamedGroupsMixin(obj) methods function value = get.GroupPropertyNames(obj) if isempty(obj.GroupPropertyNames_) - obj.GroupPropertyNames_ = obj.getGroupPropertyNames(); + className = class(obj); + obj.GroupPropertyNames_ = obj.getGroupPropertyNamesAcrossTypeHierarchy(className); end value = obj.GroupPropertyNames_; end From b59661d4cdf7c759d98abf46691ff9f9196af757 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sat, 24 Jan 2026 21:30:06 +0100 Subject: [PATCH 04/16] Regenerate types --- +types/+core/BehavioralEpochs.m | 2 +- +types/+core/BehavioralEvents.m | 2 +- +types/+core/BehavioralTimeSeries.m | 2 +- +types/+core/CompassDirection.m | 2 +- +types/+core/DfOverF.m | 2 +- +types/+core/EventWaveform.m | 2 +- +types/+core/EyeTracking.m | 2 +- +types/+core/FilteredEphys.m | 2 +- +types/+core/Fluorescence.m | 2 +- +types/+core/ImageSegmentation.m | 2 +- +types/+core/Images.m | 5 ++++- +types/+core/ImagingPlane.m | 2 +- +types/+core/LFP.m | 2 +- +types/+core/MotionCorrection.m | 2 +- +types/+core/Position.m | 2 +- +types/+core/ProcessingModule.m | 2 +- +types/+core/PupilTracking.m | 2 +- +types/+hdmf_common/AlignedDynamicTable.m | 2 +- +types/+hdmf_common/DynamicTable.m | 5 ++++- +types/+hdmf_common/SimpleMultiContainer.m | 4 ++-- 20 files changed, 27 insertions(+), 21 deletions(-) diff --git a/+types/+core/BehavioralEpochs.m b/+types/+core/BehavioralEpochs.m index 2d00c31d3..2ecdb6d32 100644 --- a/+types/+core/BehavioralEpochs.m +++ b/+types/+core/BehavioralEpochs.m @@ -9,7 +9,7 @@ properties intervalseries; % REQUIRED (IntervalSeries) IntervalSeries object containing start and stop times of epochs. end -properties (Access = protected) +properties (Constant, Access = private) GroupPropertyNames = {'intervalseries'} end diff --git a/+types/+core/BehavioralEvents.m b/+types/+core/BehavioralEvents.m index 8ee46485c..e617565c1 100644 --- a/+types/+core/BehavioralEvents.m +++ b/+types/+core/BehavioralEvents.m @@ -9,7 +9,7 @@ properties timeseries; % REQUIRED (TimeSeries) TimeSeries object containing behavioral events. end -properties (Access = protected) +properties (Constant, Access = private) GroupPropertyNames = {'timeseries'} end diff --git a/+types/+core/BehavioralTimeSeries.m b/+types/+core/BehavioralTimeSeries.m index 46f63a44b..a1107a84e 100644 --- a/+types/+core/BehavioralTimeSeries.m +++ b/+types/+core/BehavioralTimeSeries.m @@ -9,7 +9,7 @@ properties timeseries; % REQUIRED (TimeSeries) TimeSeries object containing continuous behavioral data. end -properties (Access = protected) +properties (Constant, Access = private) GroupPropertyNames = {'timeseries'} end diff --git a/+types/+core/CompassDirection.m b/+types/+core/CompassDirection.m index ab2bf141b..13407a863 100644 --- a/+types/+core/CompassDirection.m +++ b/+types/+core/CompassDirection.m @@ -9,7 +9,7 @@ properties spatialseries; % REQUIRED (SpatialSeries) SpatialSeries object containing direction of gaze travel. end -properties (Access = protected) +properties (Constant, Access = private) GroupPropertyNames = {'spatialseries'} end diff --git a/+types/+core/DfOverF.m b/+types/+core/DfOverF.m index 2e9aad419..935c36bd6 100644 --- a/+types/+core/DfOverF.m +++ b/+types/+core/DfOverF.m @@ -9,7 +9,7 @@ properties roiresponseseries; % REQUIRED (RoiResponseSeries) RoiResponseSeries object(s) containing dF/F for a ROI. end -properties (Access = protected) +properties (Constant, Access = private) GroupPropertyNames = {'roiresponseseries'} end diff --git a/+types/+core/EventWaveform.m b/+types/+core/EventWaveform.m index a2385e8cb..f86010462 100644 --- a/+types/+core/EventWaveform.m +++ b/+types/+core/EventWaveform.m @@ -9,7 +9,7 @@ properties spikeeventseries; % REQUIRED (SpikeEventSeries) SpikeEventSeries object(s) containing detected spike event waveforms. end -properties (Access = protected) +properties (Constant, Access = private) GroupPropertyNames = {'spikeeventseries'} end diff --git a/+types/+core/EyeTracking.m b/+types/+core/EyeTracking.m index a91e9eb1c..ec7256ca7 100644 --- a/+types/+core/EyeTracking.m +++ b/+types/+core/EyeTracking.m @@ -9,7 +9,7 @@ properties spatialseries; % REQUIRED (SpatialSeries) SpatialSeries object containing data measuring direction of gaze. end -properties (Access = protected) +properties (Constant, Access = private) GroupPropertyNames = {'spatialseries'} end diff --git a/+types/+core/FilteredEphys.m b/+types/+core/FilteredEphys.m index c5038249c..e622bb389 100644 --- a/+types/+core/FilteredEphys.m +++ b/+types/+core/FilteredEphys.m @@ -9,7 +9,7 @@ properties electricalseries; % REQUIRED (ElectricalSeries) ElectricalSeries object(s) containing filtered electrophysiology data. end -properties (Access = protected) +properties (Constant, Access = private) GroupPropertyNames = {'electricalseries'} end diff --git a/+types/+core/Fluorescence.m b/+types/+core/Fluorescence.m index ac09297b4..1a6952257 100644 --- a/+types/+core/Fluorescence.m +++ b/+types/+core/Fluorescence.m @@ -9,7 +9,7 @@ properties roiresponseseries; % REQUIRED (RoiResponseSeries) RoiResponseSeries object(s) containing fluorescence data for a ROI. end -properties (Access = protected) +properties (Constant, Access = private) GroupPropertyNames = {'roiresponseseries'} end diff --git a/+types/+core/ImageSegmentation.m b/+types/+core/ImageSegmentation.m index 11801451a..0202c1f30 100644 --- a/+types/+core/ImageSegmentation.m +++ b/+types/+core/ImageSegmentation.m @@ -9,7 +9,7 @@ properties planesegmentation; % REQUIRED (PlaneSegmentation) Results from image segmentation of a specific imaging plane. end -properties (Access = protected) +properties (Constant, Access = private) GroupPropertyNames = {'planesegmentation'} end diff --git a/+types/+core/Images.m b/+types/+core/Images.m index 458245ae8..add595185 100644 --- a/+types/+core/Images.m +++ b/+types/+core/Images.m @@ -1,4 +1,4 @@ -classdef Images < types.core.NWBDataInterface & types.untyped.GroupClass +classdef Images < types.core.NWBDataInterface & types.untyped.GroupClass & matnwb.mixin.HasUnnamedGroups % IMAGES - A collection of images with an optional way to specify the order of the images using the "order_of_images" dataset. An order must be specified if the images are referenced by index, e.g., from an IndexSeries. % % Required Properties: @@ -14,6 +14,9 @@ properties order_of_images; % (ImageReferences) Ordered dataset of references to BaseImage objects stored in the parent group. Each object in the Images group should be stored once and only once, so the dataset should have the same length as the number of images. end +properties (Constant, Access = private) + GroupPropertyNames = {'baseimage'} +end methods function obj = Images(varargin) diff --git a/+types/+core/ImagingPlane.m b/+types/+core/ImagingPlane.m index 426fd3b96..42f15ed4d 100644 --- a/+types/+core/ImagingPlane.m +++ b/+types/+core/ImagingPlane.m @@ -26,7 +26,7 @@ origin_coords_unit = "meters"; % (char) Measurement units for origin_coords. The default value is 'meters'. reference_frame; % (char) Describes reference frame of origin_coords and grid_spacing. For example, this can be a text description of the anatomical location and orientation of the grid defined by origin_coords and grid_spacing or the vectors needed to transform or rotate the grid to a common anatomical axis (e.g., AP/DV/ML). This field is necessary to interpret origin_coords and grid_spacing. If origin_coords and grid_spacing are not present, then this field is not required. For example, if the microscope takes 10 x 10 x 2 images, where the first value of the data matrix (index (0, 0, 0)) corresponds to (-1.2, -0.6, -2) mm relative to bregma, the spacing between pixels is 0.2 mm in x, 0.2 mm in y and 0.5 mm in z, and larger numbers in x means more anterior, larger numbers in y means more rightward, and larger numbers in z means more ventral, then enter the following -- origin_coords = (-1.2, -0.6, -2) grid_spacing = (0.2, 0.2, 0.5) reference_frame = "Origin coordinates are relative to bregma. First dimension corresponds to anterior-posterior axis (larger index = more anterior). Second dimension corresponds to medial-lateral axis (larger index = more rightward). Third dimension corresponds to dorsal-ventral axis (larger index = more ventral)." end -properties (Access = protected) +properties (Constant, Access = private) GroupPropertyNames = {'opticalchannel'} end diff --git a/+types/+core/LFP.m b/+types/+core/LFP.m index 921dedc4b..09e4823f7 100644 --- a/+types/+core/LFP.m +++ b/+types/+core/LFP.m @@ -9,7 +9,7 @@ properties electricalseries; % REQUIRED (ElectricalSeries) ElectricalSeries object(s) containing LFP data for one or more channels. end -properties (Access = protected) +properties (Constant, Access = private) GroupPropertyNames = {'electricalseries'} end diff --git a/+types/+core/MotionCorrection.m b/+types/+core/MotionCorrection.m index e3ed97e81..694caebbe 100644 --- a/+types/+core/MotionCorrection.m +++ b/+types/+core/MotionCorrection.m @@ -9,7 +9,7 @@ properties correctedimagestack; % REQUIRED (CorrectedImageStack) Results from motion correction of an image stack. end -properties (Access = protected) +properties (Constant, Access = private) GroupPropertyNames = {'correctedimagestack'} end diff --git a/+types/+core/Position.m b/+types/+core/Position.m index fe546460d..92c3d03a8 100644 --- a/+types/+core/Position.m +++ b/+types/+core/Position.m @@ -9,7 +9,7 @@ properties spatialseries; % REQUIRED (SpatialSeries) SpatialSeries object containing position data. end -properties (Access = protected) +properties (Constant, Access = private) GroupPropertyNames = {'spatialseries'} end diff --git a/+types/+core/ProcessingModule.m b/+types/+core/ProcessingModule.m index a87fd7336..10b578073 100644 --- a/+types/+core/ProcessingModule.m +++ b/+types/+core/ProcessingModule.m @@ -14,7 +14,7 @@ dynamictable; % (DynamicTable) Tables stored in this collection. nwbdatainterface; % (NWBDataInterface) Data objects stored in this collection. end -properties (Access = protected) +properties (Constant, Access = private) GroupPropertyNames = {'nwbdatainterface', 'dynamictable'} end diff --git a/+types/+core/PupilTracking.m b/+types/+core/PupilTracking.m index 4e066bf56..791667cdf 100644 --- a/+types/+core/PupilTracking.m +++ b/+types/+core/PupilTracking.m @@ -9,7 +9,7 @@ properties timeseries; % REQUIRED (TimeSeries) TimeSeries object containing time series data on pupil size. end -properties (Access = protected) +properties (Constant, Access = private) GroupPropertyNames = {'timeseries'} end diff --git a/+types/+hdmf_common/AlignedDynamicTable.m b/+types/+hdmf_common/AlignedDynamicTable.m index 892079b04..92b3b3b42 100644 --- a/+types/+hdmf_common/AlignedDynamicTable.m +++ b/+types/+hdmf_common/AlignedDynamicTable.m @@ -13,7 +13,7 @@ properties dynamictable; % (DynamicTable) A DynamicTable representing a particular category for columns in the AlignedDynamicTable parent container. The table MUST be aligned with (i.e., have the same number of rows) as all other DynamicTables stored in the AlignedDynamicTable parent container. The name of the category is given by the name of the DynamicTable and its description by the description attribute of the DynamicTable. end -properties (Access = protected) +properties (Constant, Access = private) GroupPropertyNames = {'dynamictable'} end diff --git a/+types/+hdmf_common/DynamicTable.m b/+types/+hdmf_common/DynamicTable.m index 1126c4953..6914afabe 100644 --- a/+types/+hdmf_common/DynamicTable.m +++ b/+types/+hdmf_common/DynamicTable.m @@ -1,4 +1,4 @@ -classdef DynamicTable < types.hdmf_common.Container & types.untyped.GroupClass +classdef DynamicTable < types.hdmf_common.Container & types.untyped.GroupClass & matnwb.mixin.HasUnnamedGroups % DYNAMICTABLE - A group containing multiple datasets that are aligned on the first dimension (Currently, this requirement if left up to APIs to check and enforce). These datasets represent different columns in the table. Apart from a column that contains unique identifiers for each row, there are no other required datasets. Users are free to add any number of custom VectorData objects (columns) here. DynamicTable also supports ragged array columns, where each element can be of a different size. To add a ragged array column, use a VectorIndex type to index the corresponding VectorData type. See documentation for VectorData and VectorIndex for more details. Unlike a compound data type, which is analogous to storing an array-of-structs, a DynamicTable can be thought of as a struct-of-arrays. This provides an alternative structure to choose from when optimizing storage for anticipated access patterns. Additionally, this type provides a way of creating a table without having to define a compound type up front. Although this convenience may be attractive, users should think carefully about how data will be accessed. DynamicTable is more appropriate for column-centric access, whereas a dataset with a compound type would be more appropriate for row-centric access. Finally, data size should also be taken into account. For small tables, performance loss may be an acceptable trade-off for the flexibility of a DynamicTable. % % Required Properties: @@ -15,6 +15,9 @@ properties vectordata; % (VectorData) Vector columns, including index columns, of this dynamic table. end +properties (Constant, Access = private) + GroupPropertyNames = {'vectordata'} +end methods function obj = DynamicTable(varargin) diff --git a/+types/+hdmf_common/SimpleMultiContainer.m b/+types/+hdmf_common/SimpleMultiContainer.m index baa722efb..6e9ea5bf2 100644 --- a/+types/+hdmf_common/SimpleMultiContainer.m +++ b/+types/+hdmf_common/SimpleMultiContainer.m @@ -10,8 +10,8 @@ container; % (Container) Container objects held within this SimpleMultiContainer. data; % (Data) Data objects held within this SimpleMultiContainer. end -properties (Access = protected) - GroupPropertyNames = {'container'} +properties (Constant, Access = private) + GroupPropertyNames = {'container', 'data'} end methods From 5e746fd8388e7b233422e0bdcaca65cea63c8f46 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Sat, 24 Jan 2026 21:31:44 +0100 Subject: [PATCH 05/16] Simplify condition for HasUnnamedGroups mixin Refactored the logic to add 'matnwb.mixin.HasUnnamedGroups' as a superclass by combining checks for 'hasAnonGroups' and 'hasAnonData' into a single condition. --- +file/fillClass.m | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/+file/fillClass.m b/+file/fillClass.m index 7021e162c..9ebf472bc 100644 --- a/+file/fillClass.m +++ b/+file/fillClass.m @@ -93,9 +93,7 @@ superclassNames{end+1} = 'types.untyped.DatasetClass'; end - if isa(class, 'file.Group') && class.hasAnonGroups - superclassNames{end+1} = 'matnwb.mixin.HasUnnamedGroups'; - elseif isa(class, 'file.Group') && class.hasAnonData + if isa(class, 'file.Group') && (class.hasAnonGroups || class.hasAnonData) superclassNames{end+1} = 'matnwb.mixin.HasUnnamedGroups'; end From b2dda0df6bd543dc732019a1b0a960ab53986e51 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Mon, 26 Jan 2026 12:41:27 +0100 Subject: [PATCH 06/16] Refine recursion logic in searchProperties function The recursive search should invoked for group-based classes and should only be skipped when the current object is a Set or Anon of a tye which inherits the HasUnnamedGroups mixin --- NwbFile.m | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/NwbFile.m b/NwbFile.m index 7391a4b7f..7cb5d6d20 100644 --- a/NwbFile.m +++ b/NwbFile.m @@ -475,15 +475,23 @@ function resolveReferences(obj, fid, references) pathToObjectMap(fullPath) = propValue; end - if isa(propValue, 'types.untyped.GroupClass')... - || isa(propValue, 'types.untyped.Set')... - || isa(propValue, 'types.untyped.Anon') - % recursible (even if there is a match!) + % Recursive search when a type is a group class + if isa(propValue, 'types.untyped.GroupClass') + searchProperties(pathToObjectMap, propValue, fullPath, typename, varargin{:}); + end + + if isa(propValue, 'types.untyped.Set')... + || isa(propValue, 'types.untyped.Anon') + if isa(obj, 'matnwb.mixin.HasUnnamedGroups') - % Recursive search on this object would yield duplicate objects - continue + % If the current property value is a Set of a type that inherits + % from the HasUnnamedGroups mixin, a recursive search would yield + % duplicate objects because the Set members are also exposed as + % dynamic properties of the type via the mixin. + else + % Recursive (even if there is a match!) + searchProperties(pathToObjectMap, propValue, fullPath, typename, varargin{:}); end - searchProperties(pathToObjectMap, propValue, fullPath, typename, varargin{:}); end end end From f62aae65f3164c810be6a0d381ea2cfbf541f48f Mon Sep 17 00:00:00 2001 From: ehennestad Date: Tue, 27 Jan 2026 11:09:05 +0100 Subject: [PATCH 07/16] Update fillConstructor.m --- +file/fillConstructor.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/+file/fillConstructor.m b/+file/fillConstructor.m index 020ec2caa..eaebabd40 100644 --- a/+file/fillConstructor.m +++ b/+file/fillConstructor.m @@ -169,7 +169,7 @@ fullBody = strjoin(fullBody, newline); bodystr(end+1:end+length(fullBody)+1) = [newline fullBody]; - if isa(class, 'file.Group') && class.hasAnonGroups + if isa(class, 'file.Group') && (class.hasAnonGroups || class.hasAnonData) % Include the setup function for the HasUnnamedGroups mixin bodystr = [bodystr, newline, 'obj.setupHasUnnamedGroupsMixin()', newline]; end From 428d8e65331cb471a14cc641189aaa99eb0503a7 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Tue, 27 Jan 2026 11:21:02 +0100 Subject: [PATCH 08/16] Update Anon.m Add methods that are present on the Set class, to support accessing Anon values from the HasUnnamedGroups mixin. --- +types/+untyped/Anon.m | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/+types/+untyped/Anon.m b/+types/+untyped/Anon.m index c2e21a185..bb38f0097 100644 --- a/+types/+untyped/Anon.m +++ b/+types/+untyped/Anon.m @@ -38,4 +38,21 @@ tf = strcmp(obj.name, name); end end -end \ No newline at end of file + + % Methods mirroring Set methods. + methods + function name = getPropertyName(obj, name) + % getPropertyName - Get property name given the actual name of an entry + assert(strcmp(obj.name, name), ... + 'NWB:Anon:InvalidName', ... + 'name `%s` is not part of Anon', name); + end + + function value = get(obj, name) + assert(strcmp(obj.name, name), ... + 'NWB:Anon:InvalidName', ... + 'name `%s` is not part of Anon', name); + value = obj.value; + end + end +end From 3ca7e02a8d7e09635cd5bf74501992eb60b949b4 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Tue, 27 Jan 2026 11:21:18 +0100 Subject: [PATCH 09/16] Regenerate core --- +types/+core/Images.m | 2 ++ +types/+hdmf_common/DynamicTable.m | 2 ++ 2 files changed, 4 insertions(+) diff --git a/+types/+core/Images.m b/+types/+core/Images.m index add595185..87be552c1 100644 --- a/+types/+core/Images.m +++ b/+types/+core/Images.m @@ -41,6 +41,8 @@ [obj.baseimage, ivarargin] = types.util.parseConstrained(obj,'baseimage', 'types.core.BaseImage', varargin{:}); varargin(ivarargin) = []; + obj.setupHasUnnamedGroupsMixin() + p = inputParser; p.KeepUnmatched = true; p.PartialMatching = false; diff --git a/+types/+hdmf_common/DynamicTable.m b/+types/+hdmf_common/DynamicTable.m index 6914afabe..215bf809a 100644 --- a/+types/+hdmf_common/DynamicTable.m +++ b/+types/+hdmf_common/DynamicTable.m @@ -44,6 +44,8 @@ [obj.vectordata, ivarargin] = types.util.parseConstrained(obj,'vectordata', 'types.hdmf_common.VectorData', varargin{:}); varargin(ivarargin) = []; + obj.setupHasUnnamedGroupsMixin() + p = inputParser; p.KeepUnmatched = true; p.PartialMatching = false; From 7db3bc9ebbea87efed2e003125d89a6c4312e812 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Tue, 27 Jan 2026 21:17:39 +0100 Subject: [PATCH 10/16] Refactor constructor validation and mixin setup logic - Moved mixin setup and DynamicTable validation logic from fillBody and fillCheck into the main constructor generation flow. - Introduced isDynamicTableDescendent helper for cleaner ancestry checks. - Removed the now-redundant fillCheck function. --- +file/fillConstructor.m | 77 +++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/+file/fillConstructor.m b/+file/fillConstructor.m index 020ec2caa..4c760da74 100644 --- a/+file/fillConstructor.m +++ b/+file/fillConstructor.m @@ -12,17 +12,28 @@ functionBody = [functionBody newline() bodyString]; end - functionBody = strjoin({functionBody, ... - sprintf('if strcmp(class(obj), ''%s'')', namespace.getFullClassName(name)), ... + % Build final validation/setup block that executes only for the target class, + % with conditional inclusion of mixin setup and dynamic table validation + constructorElements = {functionBody, ... + '', ... + '% Only execute validation/setup code when called directly in this class''', ... + '% constructor, not when invoked through superclass constructor chain', ... + sprintf('if strcmp(class(obj), ''%s'') %#ok', namespace.getFullClassName(name)), ... ' cellStringArguments = convertContainedStringsToChars(varargin(1:2:end));', ... - ' types.util.checkUnset(obj, unique(cellStringArguments));', ... - 'end'}, newline()); + ' types.util.checkUnset(obj, unique(cellStringArguments));'}; + + % Include the setup function for the HasUnnamedGroups mixin if applicable + if isa(class, 'file.Group') && class.hasAnonGroups + constructorElements{end+1} = ' obj.setupHasUnnamedGroupsMixin();'; + end - % insert check for DynamicTable class and child classes - bodyString = fillCheck(name, namespace); - if ~isempty(bodyString) - functionBody = [functionBody newline() bodyString]; + % Add custom validation for DynamicTable and its descendant classes + if isDynamicTableDescendent(name, namespace) + constructorElements{end+1} = ' types.util.dynamictable.checkConfig(obj);'; end + + constructorElements{end+1} = 'end'; + functionBody = strjoin(constructorElements, newline()); functionString = strjoin({... ['function obj = ' name '(varargin)']... @@ -169,11 +180,6 @@ fullBody = strjoin(fullBody, newline); bodystr(end+1:end+length(fullBody)+1) = [newline fullBody]; - if isa(class, 'file.Group') && class.hasAnonGroups - % Include the setup function for the HasUnnamedGroups mixin - bodystr = [bodystr, newline, 'obj.setupHasUnnamedGroupsMixin()', newline]; - end - parser = {... 'p = inputParser;',... 'p.KeepUnmatched = true;',... @@ -209,31 +215,6 @@ bodystr(end+1:end+length(parser)+1) = [newline parser]; end -function checkTxt = fillCheck(name, namespace) - checkTxt = []; - - % find if a dynamic table ancestry exists - ancestry = namespace.getRootBranch(name); - isDynamicTableDescendent = false; - for iAncestor = 1:length(ancestry) - ParentRaw = ancestry{iAncestor}; - % this is always true, we just use the proper index as typedefs may vary. - typeDefInd = isKey(ParentRaw, namespace.TYPEDEF_KEYS); - isDynamicTableDescendent = isDynamicTableDescendent ... - || strcmp('DynamicTable', ParentRaw(namespace.TYPEDEF_KEYS{typeDefInd})); - end - - if ~isDynamicTableDescendent - return; - end - - checkTxt = strjoin({ ... - sprintf('if strcmp(class(obj), ''%s'')', namespace.getFullClassName(name)), ... - ' types.util.dynamictable.checkConfig(obj);', ... - 'end',... - }, newline); -end - function docString = fillConstructorDocString(name, props, namespace, superClassProps) classVarName = name; classVarName(1) = lower(classVarName(1)); @@ -289,6 +270,26 @@ docString = char( strjoin(docString, newline) ); end +function tf = isDynamicTableDescendent(name, namespace) +% Check if name is DynamicTable or if name is for a type that inherits from DynamicTable + + tf = false; + + if strcmp(name, 'DynamicTable') + tf = true; + return + end + + ancestry = namespace.getRootBranch(name); + for iAncestor = 1:length(ancestry) + ParentRaw = ancestry{iAncestor}; + % this is always true, we just use the proper index as typedefs may vary. + typeDefInd = isKey(ParentRaw, namespace.TYPEDEF_KEYS); + ancestorName = ParentRaw(namespace.TYPEDEF_KEYS{typeDefInd}); + tf = tf || strcmp(ancestorName, 'DynamicTable'); + end +end + % Todo: Mostly duplicate code from file.fillProps. Should consolidate function typeStr = getTypeStr(prop) if ischar(prop) From d5467815dd51307758d7e1654cbbe31c1c360e3f Mon Sep 17 00:00:00 2001 From: ehennestad Date: Tue, 27 Jan 2026 21:20:39 +0100 Subject: [PATCH 11/16] Update fillConstructor.m Fix warning suppression in sprintf statement (add escape char) --- +file/fillConstructor.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/+file/fillConstructor.m b/+file/fillConstructor.m index 4c760da74..9d4094370 100644 --- a/+file/fillConstructor.m +++ b/+file/fillConstructor.m @@ -18,7 +18,7 @@ '', ... '% Only execute validation/setup code when called directly in this class''', ... '% constructor, not when invoked through superclass constructor chain', ... - sprintf('if strcmp(class(obj), ''%s'') %#ok', namespace.getFullClassName(name)), ... + sprintf('if strcmp(class(obj), ''%s'') %%#ok', namespace.getFullClassName(name)), ... ' cellStringArguments = convertContainedStringsToChars(varargin(1:2:end));', ... ' types.util.checkUnset(obj, unique(cellStringArguments));'}; From d68004b3c9e7d966c092de591abd797391193d79 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Tue, 27 Jan 2026 21:23:07 +0100 Subject: [PATCH 12/16] regenerate types --- +types/+core/AbstractFeatureSeries.m | 5 ++++- +types/+core/AnnotationSeries.m | 5 ++++- +types/+core/BaseImage.m | 5 ++++- +types/+core/BehavioralEpochs.m | 8 +++++--- +types/+core/BehavioralEvents.m | 8 +++++--- +types/+core/BehavioralTimeSeries.m | 8 +++++--- +types/+core/ClusterWaveforms.m | 5 ++++- +types/+core/Clustering.m | 5 ++++- +types/+core/CompassDirection.m | 8 +++++--- +types/+core/CorrectedImageStack.m | 5 ++++- +types/+core/CurrentClampSeries.m | 5 ++++- +types/+core/CurrentClampStimulusSeries.m | 5 ++++- +types/+core/DecompositionSeries.m | 5 ++++- +types/+core/Device.m | 5 ++++- +types/+core/DeviceModel.m | 5 ++++- +types/+core/DfOverF.m | 8 +++++--- +types/+core/ElectricalSeries.m | 5 ++++- +types/+core/ElectrodeGroup.m | 5 ++++- +types/+core/ElectrodesTable.m | 7 ++++--- +types/+core/EventDetection.m | 5 ++++- +types/+core/EventWaveform.m | 8 +++++--- +types/+core/ExperimentalConditionsTable.m | 7 ++++--- +types/+core/ExternalImage.m | 5 ++++- +types/+core/EyeTracking.m | 8 +++++--- +types/+core/FeatureExtraction.m | 5 ++++- +types/+core/FilteredEphys.m | 8 +++++--- +types/+core/Fluorescence.m | 8 +++++--- +types/+core/FrequencyBandsTable.m | 7 ++++--- +types/+core/GrayscaleImage.m | 5 ++++- +types/+core/IZeroClampSeries.m | 5 ++++- +types/+core/Image.m | 5 ++++- +types/+core/ImageMaskSeries.m | 5 ++++- +types/+core/ImageReferences.m | 5 ++++- +types/+core/ImageSegmentation.m | 8 +++++--- +types/+core/ImageSeries.m | 5 ++++- +types/+core/Images.m | 5 ++++- +types/+core/ImagingPlane.m | 8 +++++--- +types/+core/ImagingRetinotopy.m | 5 ++++- +types/+core/IndexSeries.m | 5 ++++- +types/+core/IntervalSeries.m | 5 ++++- +types/+core/IntracellularElectrode.m | 5 ++++- +types/+core/IntracellularElectrodesTable.m | 7 ++++--- +types/+core/IntracellularRecordingsTable.m | 7 ++++--- +types/+core/IntracellularResponsesTable.m | 7 ++++--- +types/+core/IntracellularStimuliTable.m | 7 ++++--- +types/+core/LFP.m | 8 +++++--- +types/+core/LabMetaData.m | 5 ++++- +types/+core/MotionCorrection.m | 8 +++++--- +types/+core/NWBContainer.m | 5 ++++- +types/+core/NWBData.m | 5 ++++- +types/+core/NWBDataInterface.m | 5 ++++- +types/+core/NWBFile.m | 5 ++++- +types/+core/OnePhotonSeries.m | 5 ++++- +types/+core/OpticalChannel.m | 5 ++++- +types/+core/OpticalSeries.m | 5 ++++- +types/+core/OptogeneticSeries.m | 5 ++++- +types/+core/OptogeneticStimulusSite.m | 5 ++++- +types/+core/PatchClampSeries.m | 5 ++++- +types/+core/PlaneSegmentation.m | 7 ++++--- +types/+core/Position.m | 8 +++++--- +types/+core/ProcessingModule.m | 8 +++++--- +types/+core/PupilTracking.m | 8 +++++--- +types/+core/RGBAImage.m | 5 ++++- +types/+core/RGBImage.m | 5 ++++- +types/+core/RepetitionsTable.m | 7 ++++--- +types/+core/RoiResponseSeries.m | 5 ++++- +types/+core/ScratchData.m | 5 ++++- +types/+core/SequentialRecordingsTable.m | 7 ++++--- +types/+core/SimultaneousRecordingsTable.m | 7 ++++--- +types/+core/SpatialSeries.m | 5 ++++- +types/+core/SpikeEventSeries.m | 5 ++++- +types/+core/Subject.m | 5 ++++- +types/+core/SweepTable.m | 7 ++++--- +types/+core/TimeIntervals.m | 7 ++++--- +types/+core/TimeSeries.m | 5 ++++- +types/+core/TimeSeriesReferenceVectorData.m | 5 ++++- +types/+core/TwoPhotonSeries.m | 5 ++++- +types/+core/Units.m | 7 ++++--- +types/+core/VoltageClampSeries.m | 5 ++++- +types/+core/VoltageClampStimulusSeries.m | 5 ++++- +types/+hdmf_common/AlignedDynamicTable.m | 10 +++++----- +types/+hdmf_common/CSRMatrix.m | 5 ++++- +types/+hdmf_common/Container.m | 5 ++++- +types/+hdmf_common/Data.m | 5 ++++- +types/+hdmf_common/DynamicTable.m | 6 +++++- +types/+hdmf_common/DynamicTableRegion.m | 5 ++++- +types/+hdmf_common/ElementIdentifiers.m | 5 ++++- +types/+hdmf_common/SimpleMultiContainer.m | 8 +++++--- +types/+hdmf_common/VectorData.m | 5 ++++- +types/+hdmf_common/VectorIndex.m | 5 ++++- +types/+hdmf_experimental/EnumData.m | 5 ++++- +types/+hdmf_experimental/HERD.m | 5 ++++- 92 files changed, 387 insertions(+), 158 deletions(-) diff --git a/+types/+core/AbstractFeatureSeries.m b/+types/+core/AbstractFeatureSeries.m index 153fa00ea..b9c051c7b 100644 --- a/+types/+core/AbstractFeatureSeries.m +++ b/+types/+core/AbstractFeatureSeries.m @@ -70,7 +70,10 @@ misc.parseSkipInvalidName(p, varargin); obj.feature_units = p.Results.feature_units; obj.features = p.Results.features; - if strcmp(class(obj), 'types.core.AbstractFeatureSeries') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.AbstractFeatureSeries') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/AnnotationSeries.m b/+types/+core/AnnotationSeries.m index 21853abac..b0a8a5b6c 100644 --- a/+types/+core/AnnotationSeries.m +++ b/+types/+core/AnnotationSeries.m @@ -50,7 +50,10 @@ p.PartialMatching = false; p.StructExpand = false; misc.parseSkipInvalidName(p, varargin); - if strcmp(class(obj), 'types.core.AnnotationSeries') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.AnnotationSeries') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/BaseImage.m b/+types/+core/BaseImage.m index cd814a2aa..10bd2f269 100644 --- a/+types/+core/BaseImage.m +++ b/+types/+core/BaseImage.m @@ -37,7 +37,10 @@ addParameter(p, 'description',[]); misc.parseSkipInvalidName(p, varargin); obj.description = p.Results.description; - if strcmp(class(obj), 'types.core.BaseImage') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.BaseImage') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/BehavioralEpochs.m b/+types/+core/BehavioralEpochs.m index 2d00c31d3..e29ff2487 100644 --- a/+types/+core/BehavioralEpochs.m +++ b/+types/+core/BehavioralEpochs.m @@ -32,16 +32,18 @@ [obj.intervalseries, ivarargin] = types.util.parseConstrained(obj,'intervalseries', 'types.core.IntervalSeries', varargin{:}); varargin(ivarargin) = []; - obj.setupHasUnnamedGroupsMixin() - p = inputParser; p.KeepUnmatched = true; p.PartialMatching = false; p.StructExpand = false; misc.parseSkipInvalidName(p, varargin); - if strcmp(class(obj), 'types.core.BehavioralEpochs') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.BehavioralEpochs') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); + obj.setupHasUnnamedGroupsMixin(); end end %% SETTERS diff --git a/+types/+core/BehavioralEvents.m b/+types/+core/BehavioralEvents.m index 8ee46485c..8d67d5b98 100644 --- a/+types/+core/BehavioralEvents.m +++ b/+types/+core/BehavioralEvents.m @@ -32,16 +32,18 @@ [obj.timeseries, ivarargin] = types.util.parseConstrained(obj,'timeseries', 'types.core.TimeSeries', varargin{:}); varargin(ivarargin) = []; - obj.setupHasUnnamedGroupsMixin() - p = inputParser; p.KeepUnmatched = true; p.PartialMatching = false; p.StructExpand = false; misc.parseSkipInvalidName(p, varargin); - if strcmp(class(obj), 'types.core.BehavioralEvents') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.BehavioralEvents') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); + obj.setupHasUnnamedGroupsMixin(); end end %% SETTERS diff --git a/+types/+core/BehavioralTimeSeries.m b/+types/+core/BehavioralTimeSeries.m index 46f63a44b..4ae00baef 100644 --- a/+types/+core/BehavioralTimeSeries.m +++ b/+types/+core/BehavioralTimeSeries.m @@ -32,16 +32,18 @@ [obj.timeseries, ivarargin] = types.util.parseConstrained(obj,'timeseries', 'types.core.TimeSeries', varargin{:}); varargin(ivarargin) = []; - obj.setupHasUnnamedGroupsMixin() - p = inputParser; p.KeepUnmatched = true; p.PartialMatching = false; p.StructExpand = false; misc.parseSkipInvalidName(p, varargin); - if strcmp(class(obj), 'types.core.BehavioralTimeSeries') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.BehavioralTimeSeries') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); + obj.setupHasUnnamedGroupsMixin(); end end %% SETTERS diff --git a/+types/+core/ClusterWaveforms.m b/+types/+core/ClusterWaveforms.m index 263c403e2..ef39228df 100644 --- a/+types/+core/ClusterWaveforms.m +++ b/+types/+core/ClusterWaveforms.m @@ -50,7 +50,10 @@ obj.waveform_filtering = p.Results.waveform_filtering; obj.waveform_mean = p.Results.waveform_mean; obj.waveform_sd = p.Results.waveform_sd; - if strcmp(class(obj), 'types.core.ClusterWaveforms') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.ClusterWaveforms') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/Clustering.m b/+types/+core/Clustering.m index 31789a2e6..ab953b34c 100644 --- a/+types/+core/Clustering.m +++ b/+types/+core/Clustering.m @@ -50,7 +50,10 @@ obj.num = p.Results.num; obj.peak_over_rms = p.Results.peak_over_rms; obj.times = p.Results.times; - if strcmp(class(obj), 'types.core.Clustering') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.Clustering') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/CompassDirection.m b/+types/+core/CompassDirection.m index ab2bf141b..4c0de38f3 100644 --- a/+types/+core/CompassDirection.m +++ b/+types/+core/CompassDirection.m @@ -32,16 +32,18 @@ [obj.spatialseries, ivarargin] = types.util.parseConstrained(obj,'spatialseries', 'types.core.SpatialSeries', varargin{:}); varargin(ivarargin) = []; - obj.setupHasUnnamedGroupsMixin() - p = inputParser; p.KeepUnmatched = true; p.PartialMatching = false; p.StructExpand = false; misc.parseSkipInvalidName(p, varargin); - if strcmp(class(obj), 'types.core.CompassDirection') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.CompassDirection') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); + obj.setupHasUnnamedGroupsMixin(); end end %% SETTERS diff --git a/+types/+core/CorrectedImageStack.m b/+types/+core/CorrectedImageStack.m index 87b083a6f..5b5d1e084 100644 --- a/+types/+core/CorrectedImageStack.m +++ b/+types/+core/CorrectedImageStack.m @@ -45,7 +45,10 @@ obj.corrected = p.Results.corrected; obj.original = p.Results.original; obj.xy_translation = p.Results.xy_translation; - if strcmp(class(obj), 'types.core.CorrectedImageStack') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.CorrectedImageStack') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/CurrentClampSeries.m b/+types/+core/CurrentClampSeries.m index 682c03a63..8d55d9753 100644 --- a/+types/+core/CurrentClampSeries.m +++ b/+types/+core/CurrentClampSeries.m @@ -78,7 +78,10 @@ obj.bias_current = p.Results.bias_current; obj.bridge_balance = p.Results.bridge_balance; obj.capacitance_compensation = p.Results.capacitance_compensation; - if strcmp(class(obj), 'types.core.CurrentClampSeries') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.CurrentClampSeries') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/CurrentClampStimulusSeries.m b/+types/+core/CurrentClampStimulusSeries.m index 50bb59f03..dfcd44f28 100644 --- a/+types/+core/CurrentClampStimulusSeries.m +++ b/+types/+core/CurrentClampStimulusSeries.m @@ -60,7 +60,10 @@ p.PartialMatching = false; p.StructExpand = false; misc.parseSkipInvalidName(p, varargin); - if strcmp(class(obj), 'types.core.CurrentClampStimulusSeries') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.CurrentClampStimulusSeries') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/DecompositionSeries.m b/+types/+core/DecompositionSeries.m index efef4ed9a..78ee84bd2 100644 --- a/+types/+core/DecompositionSeries.m +++ b/+types/+core/DecompositionSeries.m @@ -80,7 +80,10 @@ obj.metric = p.Results.metric; obj.source_channels = p.Results.source_channels; obj.source_timeseries = p.Results.source_timeseries; - if strcmp(class(obj), 'types.core.DecompositionSeries') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.DecompositionSeries') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/Device.m b/+types/+core/Device.m index 3f2477491..5648c7f2b 100644 --- a/+types/+core/Device.m +++ b/+types/+core/Device.m @@ -60,7 +60,10 @@ obj.model_name = p.Results.model_name; obj.model_number = p.Results.model_number; obj.serial_number = p.Results.serial_number; - if strcmp(class(obj), 'types.core.Device') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.Device') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/DeviceModel.m b/+types/+core/DeviceModel.m index 379d8f333..a9de73281 100644 --- a/+types/+core/DeviceModel.m +++ b/+types/+core/DeviceModel.m @@ -48,7 +48,10 @@ obj.description = p.Results.description; obj.manufacturer = p.Results.manufacturer; obj.model_number = p.Results.model_number; - if strcmp(class(obj), 'types.core.DeviceModel') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.DeviceModel') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/DfOverF.m b/+types/+core/DfOverF.m index 2e9aad419..12f3914b8 100644 --- a/+types/+core/DfOverF.m +++ b/+types/+core/DfOverF.m @@ -32,16 +32,18 @@ [obj.roiresponseseries, ivarargin] = types.util.parseConstrained(obj,'roiresponseseries', 'types.core.RoiResponseSeries', varargin{:}); varargin(ivarargin) = []; - obj.setupHasUnnamedGroupsMixin() - p = inputParser; p.KeepUnmatched = true; p.PartialMatching = false; p.StructExpand = false; misc.parseSkipInvalidName(p, varargin); - if strcmp(class(obj), 'types.core.DfOverF') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.DfOverF') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); + obj.setupHasUnnamedGroupsMixin(); end end %% SETTERS diff --git a/+types/+core/ElectricalSeries.m b/+types/+core/ElectricalSeries.m index 45c2e5bbd..1923e4932 100644 --- a/+types/+core/ElectricalSeries.m +++ b/+types/+core/ElectricalSeries.m @@ -79,7 +79,10 @@ obj.channel_conversion_axis = p.Results.channel_conversion_axis; obj.electrodes = p.Results.electrodes; obj.filtering = p.Results.filtering; - if strcmp(class(obj), 'types.core.ElectricalSeries') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.ElectricalSeries') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/ElectrodeGroup.m b/+types/+core/ElectrodeGroup.m index eccdc0e1e..e16acd164 100644 --- a/+types/+core/ElectrodeGroup.m +++ b/+types/+core/ElectrodeGroup.m @@ -53,7 +53,10 @@ obj.device = p.Results.device; obj.location = p.Results.location; obj.position = p.Results.position; - if strcmp(class(obj), 'types.core.ElectrodeGroup') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.ElectrodeGroup') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/ElectrodesTable.m b/+types/+core/ElectrodesTable.m index 5fab86da8..a3e304b54 100644 --- a/+types/+core/ElectrodesTable.m +++ b/+types/+core/ElectrodesTable.m @@ -101,11 +101,12 @@ obj.x = p.Results.x; obj.y = p.Results.y; obj.z = p.Results.z; - if strcmp(class(obj), 'types.core.ElectrodesTable') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.ElectrodesTable') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); - end - if strcmp(class(obj), 'types.core.ElectrodesTable') types.util.dynamictable.checkConfig(obj); end end diff --git a/+types/+core/EventDetection.m b/+types/+core/EventDetection.m index 4edb90245..bb8406fe4 100644 --- a/+types/+core/EventDetection.m +++ b/+types/+core/EventDetection.m @@ -60,7 +60,10 @@ obj.source_idx = p.Results.source_idx; obj.times = p.Results.times; obj.times_unit = p.Results.times_unit; - if strcmp(class(obj), 'types.core.EventDetection') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.EventDetection') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/EventWaveform.m b/+types/+core/EventWaveform.m index a2385e8cb..bde42ee52 100644 --- a/+types/+core/EventWaveform.m +++ b/+types/+core/EventWaveform.m @@ -32,16 +32,18 @@ [obj.spikeeventseries, ivarargin] = types.util.parseConstrained(obj,'spikeeventseries', 'types.core.SpikeEventSeries', varargin{:}); varargin(ivarargin) = []; - obj.setupHasUnnamedGroupsMixin() - p = inputParser; p.KeepUnmatched = true; p.PartialMatching = false; p.StructExpand = false; misc.parseSkipInvalidName(p, varargin); - if strcmp(class(obj), 'types.core.EventWaveform') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.EventWaveform') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); + obj.setupHasUnnamedGroupsMixin(); end end %% SETTERS diff --git a/+types/+core/ExperimentalConditionsTable.m b/+types/+core/ExperimentalConditionsTable.m index 132f22f17..29edd99c2 100644 --- a/+types/+core/ExperimentalConditionsTable.m +++ b/+types/+core/ExperimentalConditionsTable.m @@ -48,11 +48,12 @@ misc.parseSkipInvalidName(p, varargin); obj.repetitions = p.Results.repetitions; obj.repetitions_index = p.Results.repetitions_index; - if strcmp(class(obj), 'types.core.ExperimentalConditionsTable') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.ExperimentalConditionsTable') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); - end - if strcmp(class(obj), 'types.core.ExperimentalConditionsTable') types.util.dynamictable.checkConfig(obj); end end diff --git a/+types/+core/ExternalImage.m b/+types/+core/ExternalImage.m index 85c0f871e..01fa048a3 100644 --- a/+types/+core/ExternalImage.m +++ b/+types/+core/ExternalImage.m @@ -47,7 +47,10 @@ misc.parseSkipInvalidName(p, varargin); obj.image_format = p.Results.image_format; obj.image_mode = p.Results.image_mode; - if strcmp(class(obj), 'types.core.ExternalImage') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.ExternalImage') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/EyeTracking.m b/+types/+core/EyeTracking.m index a91e9eb1c..bac1956e2 100644 --- a/+types/+core/EyeTracking.m +++ b/+types/+core/EyeTracking.m @@ -32,16 +32,18 @@ [obj.spatialseries, ivarargin] = types.util.parseConstrained(obj,'spatialseries', 'types.core.SpatialSeries', varargin{:}); varargin(ivarargin) = []; - obj.setupHasUnnamedGroupsMixin() - p = inputParser; p.KeepUnmatched = true; p.PartialMatching = false; p.StructExpand = false; misc.parseSkipInvalidName(p, varargin); - if strcmp(class(obj), 'types.core.EyeTracking') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.EyeTracking') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); + obj.setupHasUnnamedGroupsMixin(); end end %% SETTERS diff --git a/+types/+core/FeatureExtraction.m b/+types/+core/FeatureExtraction.m index afb8f4202..9ef17978e 100644 --- a/+types/+core/FeatureExtraction.m +++ b/+types/+core/FeatureExtraction.m @@ -50,7 +50,10 @@ obj.electrodes = p.Results.electrodes; obj.features = p.Results.features; obj.times = p.Results.times; - if strcmp(class(obj), 'types.core.FeatureExtraction') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.FeatureExtraction') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/FilteredEphys.m b/+types/+core/FilteredEphys.m index c5038249c..b22bc460b 100644 --- a/+types/+core/FilteredEphys.m +++ b/+types/+core/FilteredEphys.m @@ -32,16 +32,18 @@ [obj.electricalseries, ivarargin] = types.util.parseConstrained(obj,'electricalseries', 'types.core.ElectricalSeries', varargin{:}); varargin(ivarargin) = []; - obj.setupHasUnnamedGroupsMixin() - p = inputParser; p.KeepUnmatched = true; p.PartialMatching = false; p.StructExpand = false; misc.parseSkipInvalidName(p, varargin); - if strcmp(class(obj), 'types.core.FilteredEphys') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.FilteredEphys') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); + obj.setupHasUnnamedGroupsMixin(); end end %% SETTERS diff --git a/+types/+core/Fluorescence.m b/+types/+core/Fluorescence.m index ac09297b4..6bb7a1bbc 100644 --- a/+types/+core/Fluorescence.m +++ b/+types/+core/Fluorescence.m @@ -32,16 +32,18 @@ [obj.roiresponseseries, ivarargin] = types.util.parseConstrained(obj,'roiresponseseries', 'types.core.RoiResponseSeries', varargin{:}); varargin(ivarargin) = []; - obj.setupHasUnnamedGroupsMixin() - p = inputParser; p.KeepUnmatched = true; p.PartialMatching = false; p.StructExpand = false; misc.parseSkipInvalidName(p, varargin); - if strcmp(class(obj), 'types.core.Fluorescence') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.Fluorescence') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); + obj.setupHasUnnamedGroupsMixin(); end end %% SETTERS diff --git a/+types/+core/FrequencyBandsTable.m b/+types/+core/FrequencyBandsTable.m index 22d9d07bb..e1b387240 100644 --- a/+types/+core/FrequencyBandsTable.m +++ b/+types/+core/FrequencyBandsTable.m @@ -61,11 +61,12 @@ obj.band_mean = p.Results.band_mean; obj.band_name = p.Results.band_name; obj.band_stdev = p.Results.band_stdev; - if strcmp(class(obj), 'types.core.FrequencyBandsTable') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.FrequencyBandsTable') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); - end - if strcmp(class(obj), 'types.core.FrequencyBandsTable') types.util.dynamictable.checkConfig(obj); end end diff --git a/+types/+core/GrayscaleImage.m b/+types/+core/GrayscaleImage.m index f948d6eea..4a765fe08 100644 --- a/+types/+core/GrayscaleImage.m +++ b/+types/+core/GrayscaleImage.m @@ -33,7 +33,10 @@ p.PartialMatching = false; p.StructExpand = false; misc.parseSkipInvalidName(p, varargin); - if strcmp(class(obj), 'types.core.GrayscaleImage') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.GrayscaleImage') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/IZeroClampSeries.m b/+types/+core/IZeroClampSeries.m index 67c2983e0..62a363415 100644 --- a/+types/+core/IZeroClampSeries.m +++ b/+types/+core/IZeroClampSeries.m @@ -58,7 +58,10 @@ p.PartialMatching = false; p.StructExpand = false; misc.parseSkipInvalidName(p, varargin); - if strcmp(class(obj), 'types.core.IZeroClampSeries') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.IZeroClampSeries') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/Image.m b/+types/+core/Image.m index aef7d3dc2..3ea4f18d0 100644 --- a/+types/+core/Image.m +++ b/+types/+core/Image.m @@ -39,7 +39,10 @@ addParameter(p, 'resolution',[]); misc.parseSkipInvalidName(p, varargin); obj.resolution = p.Results.resolution; - if strcmp(class(obj), 'types.core.Image') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.Image') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/ImageMaskSeries.m b/+types/+core/ImageMaskSeries.m index 2ac80633e..eee77cefc 100644 --- a/+types/+core/ImageMaskSeries.m +++ b/+types/+core/ImageMaskSeries.m @@ -71,7 +71,10 @@ addParameter(p, 'masked_imageseries',[]); misc.parseSkipInvalidName(p, varargin); obj.masked_imageseries = p.Results.masked_imageseries; - if strcmp(class(obj), 'types.core.ImageMaskSeries') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.ImageMaskSeries') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/ImageReferences.m b/+types/+core/ImageReferences.m index 2b18b6c99..df235ead4 100644 --- a/+types/+core/ImageReferences.m +++ b/+types/+core/ImageReferences.m @@ -29,7 +29,10 @@ p.PartialMatching = false; p.StructExpand = false; misc.parseSkipInvalidName(p, varargin); - if strcmp(class(obj), 'types.core.ImageReferences') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.ImageReferences') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/ImageSegmentation.m b/+types/+core/ImageSegmentation.m index 11801451a..8a73aabfd 100644 --- a/+types/+core/ImageSegmentation.m +++ b/+types/+core/ImageSegmentation.m @@ -32,16 +32,18 @@ [obj.planesegmentation, ivarargin] = types.util.parseConstrained(obj,'planesegmentation', 'types.core.PlaneSegmentation', varargin{:}); varargin(ivarargin) = []; - obj.setupHasUnnamedGroupsMixin() - p = inputParser; p.KeepUnmatched = true; p.PartialMatching = false; p.StructExpand = false; misc.parseSkipInvalidName(p, varargin); - if strcmp(class(obj), 'types.core.ImageSegmentation') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.ImageSegmentation') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); + obj.setupHasUnnamedGroupsMixin(); end end %% SETTERS diff --git a/+types/+core/ImageSeries.m b/+types/+core/ImageSeries.m index cb6b27780..18169425f 100644 --- a/+types/+core/ImageSeries.m +++ b/+types/+core/ImageSeries.m @@ -81,7 +81,10 @@ obj.external_file = p.Results.external_file; obj.external_file_starting_frame = p.Results.external_file_starting_frame; obj.format = p.Results.format; - if strcmp(class(obj), 'types.core.ImageSeries') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.ImageSeries') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/Images.m b/+types/+core/Images.m index 458245ae8..7569ebffe 100644 --- a/+types/+core/Images.m +++ b/+types/+core/Images.m @@ -47,7 +47,10 @@ misc.parseSkipInvalidName(p, varargin); obj.description = p.Results.description; obj.order_of_images = p.Results.order_of_images; - if strcmp(class(obj), 'types.core.Images') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.Images') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/ImagingPlane.m b/+types/+core/ImagingPlane.m index 426fd3b96..3d29a994f 100644 --- a/+types/+core/ImagingPlane.m +++ b/+types/+core/ImagingPlane.m @@ -78,8 +78,6 @@ [obj.opticalchannel, ivarargin] = types.util.parseConstrained(obj,'opticalchannel', 'types.core.OpticalChannel', varargin{:}); varargin(ivarargin) = []; - obj.setupHasUnnamedGroupsMixin() - p = inputParser; p.KeepUnmatched = true; p.PartialMatching = false; @@ -113,9 +111,13 @@ obj.origin_coords = p.Results.origin_coords; obj.origin_coords_unit = p.Results.origin_coords_unit; obj.reference_frame = p.Results.reference_frame; - if strcmp(class(obj), 'types.core.ImagingPlane') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.ImagingPlane') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); + obj.setupHasUnnamedGroupsMixin(); end end %% SETTERS diff --git a/+types/+core/ImagingRetinotopy.m b/+types/+core/ImagingRetinotopy.m index 47cd7bb84..89bd9bf05 100644 --- a/+types/+core/ImagingRetinotopy.m +++ b/+types/+core/ImagingRetinotopy.m @@ -188,7 +188,10 @@ obj.vasculature_image_dimension = p.Results.vasculature_image_dimension; obj.vasculature_image_field_of_view = p.Results.vasculature_image_field_of_view; obj.vasculature_image_format = p.Results.vasculature_image_format; - if strcmp(class(obj), 'types.core.ImagingRetinotopy') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.ImagingRetinotopy') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/IndexSeries.m b/+types/+core/IndexSeries.m index 9e07114fa..7328b2969 100644 --- a/+types/+core/IndexSeries.m +++ b/+types/+core/IndexSeries.m @@ -65,7 +65,10 @@ misc.parseSkipInvalidName(p, varargin); obj.indexed_images = p.Results.indexed_images; obj.indexed_timeseries = p.Results.indexed_timeseries; - if strcmp(class(obj), 'types.core.IndexSeries') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.IndexSeries') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/IntervalSeries.m b/+types/+core/IntervalSeries.m index 4e7296bd5..e75f66ece 100644 --- a/+types/+core/IntervalSeries.m +++ b/+types/+core/IntervalSeries.m @@ -50,7 +50,10 @@ p.PartialMatching = false; p.StructExpand = false; misc.parseSkipInvalidName(p, varargin); - if strcmp(class(obj), 'types.core.IntervalSeries') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.IntervalSeries') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/IntracellularElectrode.m b/+types/+core/IntracellularElectrode.m index 390f290d8..16cc83602 100644 --- a/+types/+core/IntracellularElectrode.m +++ b/+types/+core/IntracellularElectrode.m @@ -78,7 +78,10 @@ obj.resistance = p.Results.resistance; obj.seal = p.Results.seal; obj.slice = p.Results.slice; - if strcmp(class(obj), 'types.core.IntracellularElectrode') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.IntracellularElectrode') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/IntracellularElectrodesTable.m b/+types/+core/IntracellularElectrodesTable.m index 4cedc578b..b1b4aee81 100644 --- a/+types/+core/IntracellularElectrodesTable.m +++ b/+types/+core/IntracellularElectrodesTable.m @@ -42,11 +42,12 @@ addParameter(p, 'electrode',[]); misc.parseSkipInvalidName(p, varargin); obj.electrode = p.Results.electrode; - if strcmp(class(obj), 'types.core.IntracellularElectrodesTable') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.IntracellularElectrodesTable') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); - end - if strcmp(class(obj), 'types.core.IntracellularElectrodesTable') types.util.dynamictable.checkConfig(obj); end end diff --git a/+types/+core/IntracellularRecordingsTable.m b/+types/+core/IntracellularRecordingsTable.m index 5f2c68db5..7836cb175 100644 --- a/+types/+core/IntracellularRecordingsTable.m +++ b/+types/+core/IntracellularRecordingsTable.m @@ -56,11 +56,12 @@ obj.electrodes = p.Results.electrodes; obj.responses = p.Results.responses; obj.stimuli = p.Results.stimuli; - if strcmp(class(obj), 'types.core.IntracellularRecordingsTable') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.IntracellularRecordingsTable') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); - end - if strcmp(class(obj), 'types.core.IntracellularRecordingsTable') types.util.dynamictable.checkConfig(obj); end end diff --git a/+types/+core/IntracellularResponsesTable.m b/+types/+core/IntracellularResponsesTable.m index ef5f9ef61..ff1cc2e5b 100644 --- a/+types/+core/IntracellularResponsesTable.m +++ b/+types/+core/IntracellularResponsesTable.m @@ -42,11 +42,12 @@ addParameter(p, 'response',[]); misc.parseSkipInvalidName(p, varargin); obj.response = p.Results.response; - if strcmp(class(obj), 'types.core.IntracellularResponsesTable') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.IntracellularResponsesTable') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); - end - if strcmp(class(obj), 'types.core.IntracellularResponsesTable') types.util.dynamictable.checkConfig(obj); end end diff --git a/+types/+core/IntracellularStimuliTable.m b/+types/+core/IntracellularStimuliTable.m index afc1da5ac..dbfe6fe2c 100644 --- a/+types/+core/IntracellularStimuliTable.m +++ b/+types/+core/IntracellularStimuliTable.m @@ -50,11 +50,12 @@ misc.parseSkipInvalidName(p, varargin); obj.stimulus = p.Results.stimulus; obj.stimulus_template = p.Results.stimulus_template; - if strcmp(class(obj), 'types.core.IntracellularStimuliTable') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.IntracellularStimuliTable') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); - end - if strcmp(class(obj), 'types.core.IntracellularStimuliTable') types.util.dynamictable.checkConfig(obj); end end diff --git a/+types/+core/LFP.m b/+types/+core/LFP.m index 921dedc4b..aeec8fb97 100644 --- a/+types/+core/LFP.m +++ b/+types/+core/LFP.m @@ -32,16 +32,18 @@ [obj.electricalseries, ivarargin] = types.util.parseConstrained(obj,'electricalseries', 'types.core.ElectricalSeries', varargin{:}); varargin(ivarargin) = []; - obj.setupHasUnnamedGroupsMixin() - p = inputParser; p.KeepUnmatched = true; p.PartialMatching = false; p.StructExpand = false; misc.parseSkipInvalidName(p, varargin); - if strcmp(class(obj), 'types.core.LFP') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.LFP') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); + obj.setupHasUnnamedGroupsMixin(); end end %% SETTERS diff --git a/+types/+core/LabMetaData.m b/+types/+core/LabMetaData.m index 6f9cb0aea..6e4d5862c 100644 --- a/+types/+core/LabMetaData.m +++ b/+types/+core/LabMetaData.m @@ -17,7 +17,10 @@ % - labMetaData (types.core.LabMetaData) - A LabMetaData object obj = obj@types.core.NWBContainer(varargin{:}); - if strcmp(class(obj), 'types.core.LabMetaData') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.LabMetaData') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/MotionCorrection.m b/+types/+core/MotionCorrection.m index e3ed97e81..073c574c7 100644 --- a/+types/+core/MotionCorrection.m +++ b/+types/+core/MotionCorrection.m @@ -32,16 +32,18 @@ [obj.correctedimagestack, ivarargin] = types.util.parseConstrained(obj,'correctedimagestack', 'types.core.CorrectedImageStack', varargin{:}); varargin(ivarargin) = []; - obj.setupHasUnnamedGroupsMixin() - p = inputParser; p.KeepUnmatched = true; p.PartialMatching = false; p.StructExpand = false; misc.parseSkipInvalidName(p, varargin); - if strcmp(class(obj), 'types.core.MotionCorrection') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.MotionCorrection') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); + obj.setupHasUnnamedGroupsMixin(); end end %% SETTERS diff --git a/+types/+core/NWBContainer.m b/+types/+core/NWBContainer.m index 02b6d2b17..b8b5b4c2b 100644 --- a/+types/+core/NWBContainer.m +++ b/+types/+core/NWBContainer.m @@ -17,7 +17,10 @@ % - nWBContainer (types.core.NWBContainer) - A NWBContainer object obj = obj@types.hdmf_common.Container(varargin{:}); - if strcmp(class(obj), 'types.core.NWBContainer') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.NWBContainer') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/NWBData.m b/+types/+core/NWBData.m index 348806d8b..a3b973a78 100644 --- a/+types/+core/NWBData.m +++ b/+types/+core/NWBData.m @@ -29,7 +29,10 @@ p.PartialMatching = false; p.StructExpand = false; misc.parseSkipInvalidName(p, varargin); - if strcmp(class(obj), 'types.core.NWBData') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.NWBData') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/NWBDataInterface.m b/+types/+core/NWBDataInterface.m index 1012dc86e..e98fea6cf 100644 --- a/+types/+core/NWBDataInterface.m +++ b/+types/+core/NWBDataInterface.m @@ -17,7 +17,10 @@ % - nWBDataInterface (types.core.NWBDataInterface) - A NWBDataInterface object obj = obj@types.core.NWBContainer(varargin{:}); - if strcmp(class(obj), 'types.core.NWBDataInterface') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.NWBDataInterface') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/NWBFile.m b/+types/+core/NWBFile.m index bdb882e40..4daeb834c 100644 --- a/+types/+core/NWBFile.m +++ b/+types/+core/NWBFile.m @@ -290,7 +290,10 @@ obj.stimulus_templates = p.Results.stimulus_templates; obj.timestamps_reference_time = p.Results.timestamps_reference_time; obj.units = p.Results.units; - if strcmp(class(obj), 'types.core.NWBFile') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.NWBFile') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/OnePhotonSeries.m b/+types/+core/OnePhotonSeries.m index bfbeba610..991211f8c 100644 --- a/+types/+core/OnePhotonSeries.m +++ b/+types/+core/OnePhotonSeries.m @@ -104,7 +104,10 @@ obj.pmt_gain = p.Results.pmt_gain; obj.power = p.Results.power; obj.scan_line_rate = p.Results.scan_line_rate; - if strcmp(class(obj), 'types.core.OnePhotonSeries') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.OnePhotonSeries') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/OpticalChannel.m b/+types/+core/OpticalChannel.m index 1e3c8aa6c..3de6cc29a 100644 --- a/+types/+core/OpticalChannel.m +++ b/+types/+core/OpticalChannel.m @@ -40,7 +40,10 @@ misc.parseSkipInvalidName(p, varargin); obj.description = p.Results.description; obj.emission_lambda = p.Results.emission_lambda; - if strcmp(class(obj), 'types.core.OpticalChannel') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.OpticalChannel') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/OpticalSeries.m b/+types/+core/OpticalSeries.m index 766ad3683..af49ffbef 100644 --- a/+types/+core/OpticalSeries.m +++ b/+types/+core/OpticalSeries.m @@ -81,7 +81,10 @@ obj.distance = p.Results.distance; obj.field_of_view = p.Results.field_of_view; obj.orientation = p.Results.orientation; - if strcmp(class(obj), 'types.core.OpticalSeries') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.OpticalSeries') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/OptogeneticSeries.m b/+types/+core/OptogeneticSeries.m index 0cea5f364..f8bf1a5bd 100644 --- a/+types/+core/OptogeneticSeries.m +++ b/+types/+core/OptogeneticSeries.m @@ -60,7 +60,10 @@ addParameter(p, 'site',[]); misc.parseSkipInvalidName(p, varargin); obj.site = p.Results.site; - if strcmp(class(obj), 'types.core.OptogeneticSeries') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.OptogeneticSeries') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/OptogeneticStimulusSite.m b/+types/+core/OptogeneticStimulusSite.m index 4dacc81ae..d0adb46ea 100644 --- a/+types/+core/OptogeneticStimulusSite.m +++ b/+types/+core/OptogeneticStimulusSite.m @@ -50,7 +50,10 @@ obj.device = p.Results.device; obj.excitation_lambda = p.Results.excitation_lambda; obj.location = p.Results.location; - if strcmp(class(obj), 'types.core.OptogeneticStimulusSite') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.OptogeneticStimulusSite') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/PatchClampSeries.m b/+types/+core/PatchClampSeries.m index d0070a546..abceee5f0 100644 --- a/+types/+core/PatchClampSeries.m +++ b/+types/+core/PatchClampSeries.m @@ -79,7 +79,10 @@ obj.gain = p.Results.gain; obj.stimulus_description = p.Results.stimulus_description; obj.sweep_number = p.Results.sweep_number; - if strcmp(class(obj), 'types.core.PatchClampSeries') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.PatchClampSeries') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/PlaneSegmentation.m b/+types/+core/PlaneSegmentation.m index e97b322a2..5f2d0fb48 100644 --- a/+types/+core/PlaneSegmentation.m +++ b/+types/+core/PlaneSegmentation.m @@ -76,11 +76,12 @@ obj.reference_images = p.Results.reference_images; obj.voxel_mask = p.Results.voxel_mask; obj.voxel_mask_index = p.Results.voxel_mask_index; - if strcmp(class(obj), 'types.core.PlaneSegmentation') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.PlaneSegmentation') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); - end - if strcmp(class(obj), 'types.core.PlaneSegmentation') types.util.dynamictable.checkConfig(obj); end end diff --git a/+types/+core/Position.m b/+types/+core/Position.m index fe546460d..b2231d06d 100644 --- a/+types/+core/Position.m +++ b/+types/+core/Position.m @@ -32,16 +32,18 @@ [obj.spatialseries, ivarargin] = types.util.parseConstrained(obj,'spatialseries', 'types.core.SpatialSeries', varargin{:}); varargin(ivarargin) = []; - obj.setupHasUnnamedGroupsMixin() - p = inputParser; p.KeepUnmatched = true; p.PartialMatching = false; p.StructExpand = false; misc.parseSkipInvalidName(p, varargin); - if strcmp(class(obj), 'types.core.Position') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.Position') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); + obj.setupHasUnnamedGroupsMixin(); end end %% SETTERS diff --git a/+types/+core/ProcessingModule.m b/+types/+core/ProcessingModule.m index a87fd7336..2558d6540 100644 --- a/+types/+core/ProcessingModule.m +++ b/+types/+core/ProcessingModule.m @@ -43,8 +43,6 @@ [obj.nwbdatainterface, ivarargin] = types.util.parseConstrained(obj,'nwbdatainterface', 'types.core.NWBDataInterface', varargin{:}); varargin(ivarargin) = []; - obj.setupHasUnnamedGroupsMixin() - p = inputParser; p.KeepUnmatched = true; p.PartialMatching = false; @@ -52,9 +50,13 @@ addParameter(p, 'description',[]); misc.parseSkipInvalidName(p, varargin); obj.description = p.Results.description; - if strcmp(class(obj), 'types.core.ProcessingModule') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.ProcessingModule') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); + obj.setupHasUnnamedGroupsMixin(); end end %% SETTERS diff --git a/+types/+core/PupilTracking.m b/+types/+core/PupilTracking.m index 4e066bf56..f5d5ffb4e 100644 --- a/+types/+core/PupilTracking.m +++ b/+types/+core/PupilTracking.m @@ -32,16 +32,18 @@ [obj.timeseries, ivarargin] = types.util.parseConstrained(obj,'timeseries', 'types.core.TimeSeries', varargin{:}); varargin(ivarargin) = []; - obj.setupHasUnnamedGroupsMixin() - p = inputParser; p.KeepUnmatched = true; p.PartialMatching = false; p.StructExpand = false; misc.parseSkipInvalidName(p, varargin); - if strcmp(class(obj), 'types.core.PupilTracking') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.PupilTracking') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); + obj.setupHasUnnamedGroupsMixin(); end end %% SETTERS diff --git a/+types/+core/RGBAImage.m b/+types/+core/RGBAImage.m index 284628a36..8f8721bf7 100644 --- a/+types/+core/RGBAImage.m +++ b/+types/+core/RGBAImage.m @@ -33,7 +33,10 @@ p.PartialMatching = false; p.StructExpand = false; misc.parseSkipInvalidName(p, varargin); - if strcmp(class(obj), 'types.core.RGBAImage') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.RGBAImage') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/RGBImage.m b/+types/+core/RGBImage.m index 3480051d0..7cc26e60b 100644 --- a/+types/+core/RGBImage.m +++ b/+types/+core/RGBImage.m @@ -33,7 +33,10 @@ p.PartialMatching = false; p.StructExpand = false; misc.parseSkipInvalidName(p, varargin); - if strcmp(class(obj), 'types.core.RGBImage') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.RGBImage') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/RepetitionsTable.m b/+types/+core/RepetitionsTable.m index f1f2ca7a2..eb6b28a12 100644 --- a/+types/+core/RepetitionsTable.m +++ b/+types/+core/RepetitionsTable.m @@ -48,11 +48,12 @@ misc.parseSkipInvalidName(p, varargin); obj.sequential_recordings = p.Results.sequential_recordings; obj.sequential_recordings_index = p.Results.sequential_recordings_index; - if strcmp(class(obj), 'types.core.RepetitionsTable') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.RepetitionsTable') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); - end - if strcmp(class(obj), 'types.core.RepetitionsTable') types.util.dynamictable.checkConfig(obj); end end diff --git a/+types/+core/RoiResponseSeries.m b/+types/+core/RoiResponseSeries.m index 419e4679f..0d0dce3e4 100644 --- a/+types/+core/RoiResponseSeries.m +++ b/+types/+core/RoiResponseSeries.m @@ -61,7 +61,10 @@ addParameter(p, 'rois',[]); misc.parseSkipInvalidName(p, varargin); obj.rois = p.Results.rois; - if strcmp(class(obj), 'types.core.RoiResponseSeries') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.RoiResponseSeries') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/ScratchData.m b/+types/+core/ScratchData.m index ca010396e..987842738 100644 --- a/+types/+core/ScratchData.m +++ b/+types/+core/ScratchData.m @@ -37,7 +37,10 @@ addParameter(p, 'notes',[]); misc.parseSkipInvalidName(p, varargin); obj.notes = p.Results.notes; - if strcmp(class(obj), 'types.core.ScratchData') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.ScratchData') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/SequentialRecordingsTable.m b/+types/+core/SequentialRecordingsTable.m index 16d681487..905a05147 100644 --- a/+types/+core/SequentialRecordingsTable.m +++ b/+types/+core/SequentialRecordingsTable.m @@ -53,11 +53,12 @@ obj.simultaneous_recordings = p.Results.simultaneous_recordings; obj.simultaneous_recordings_index = p.Results.simultaneous_recordings_index; obj.stimulus_type = p.Results.stimulus_type; - if strcmp(class(obj), 'types.core.SequentialRecordingsTable') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.SequentialRecordingsTable') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); - end - if strcmp(class(obj), 'types.core.SequentialRecordingsTable') types.util.dynamictable.checkConfig(obj); end end diff --git a/+types/+core/SimultaneousRecordingsTable.m b/+types/+core/SimultaneousRecordingsTable.m index bd643b0c5..2302e3962 100644 --- a/+types/+core/SimultaneousRecordingsTable.m +++ b/+types/+core/SimultaneousRecordingsTable.m @@ -48,11 +48,12 @@ misc.parseSkipInvalidName(p, varargin); obj.recordings = p.Results.recordings; obj.recordings_index = p.Results.recordings_index; - if strcmp(class(obj), 'types.core.SimultaneousRecordingsTable') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.SimultaneousRecordingsTable') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); - end - if strcmp(class(obj), 'types.core.SimultaneousRecordingsTable') types.util.dynamictable.checkConfig(obj); end end diff --git a/+types/+core/SpatialSeries.m b/+types/+core/SpatialSeries.m index ecdab93f9..c562ff7ee 100644 --- a/+types/+core/SpatialSeries.m +++ b/+types/+core/SpatialSeries.m @@ -62,7 +62,10 @@ addParameter(p, 'reference_frame',[]); misc.parseSkipInvalidName(p, varargin); obj.reference_frame = p.Results.reference_frame; - if strcmp(class(obj), 'types.core.SpatialSeries') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.SpatialSeries') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/SpikeEventSeries.m b/+types/+core/SpikeEventSeries.m index 39ce891ff..f1f6d303a 100644 --- a/+types/+core/SpikeEventSeries.m +++ b/+types/+core/SpikeEventSeries.m @@ -58,7 +58,10 @@ p.PartialMatching = false; p.StructExpand = false; misc.parseSkipInvalidName(p, varargin); - if strcmp(class(obj), 'types.core.SpikeEventSeries') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.SpikeEventSeries') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/Subject.m b/+types/+core/Subject.m index 63f36880d..6b59153a1 100644 --- a/+types/+core/Subject.m +++ b/+types/+core/Subject.m @@ -81,7 +81,10 @@ obj.strain = p.Results.strain; obj.subject_id = p.Results.subject_id; obj.weight = p.Results.weight; - if strcmp(class(obj), 'types.core.Subject') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.Subject') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/SweepTable.m b/+types/+core/SweepTable.m index a755c88d8..5d90ebadb 100644 --- a/+types/+core/SweepTable.m +++ b/+types/+core/SweepTable.m @@ -53,11 +53,12 @@ obj.series = p.Results.series; obj.series_index = p.Results.series_index; obj.sweep_number = p.Results.sweep_number; - if strcmp(class(obj), 'types.core.SweepTable') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.SweepTable') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); - end - if strcmp(class(obj), 'types.core.SweepTable') types.util.dynamictable.checkConfig(obj); end end diff --git a/+types/+core/TimeIntervals.m b/+types/+core/TimeIntervals.m index 5f96fdac6..cba948d97 100644 --- a/+types/+core/TimeIntervals.m +++ b/+types/+core/TimeIntervals.m @@ -71,11 +71,12 @@ obj.tags_index = p.Results.tags_index; obj.timeseries = p.Results.timeseries; obj.timeseries_index = p.Results.timeseries_index; - if strcmp(class(obj), 'types.core.TimeIntervals') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.TimeIntervals') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); - end - if strcmp(class(obj), 'types.core.TimeIntervals') types.util.dynamictable.checkConfig(obj); end end diff --git a/+types/+core/TimeSeries.m b/+types/+core/TimeSeries.m index 265307fc4..53eaa076c 100644 --- a/+types/+core/TimeSeries.m +++ b/+types/+core/TimeSeries.m @@ -111,7 +111,10 @@ obj.timestamps = p.Results.timestamps; obj.timestamps_interval = p.Results.timestamps_interval; obj.timestamps_unit = p.Results.timestamps_unit; - if strcmp(class(obj), 'types.core.TimeSeries') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.TimeSeries') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/TimeSeriesReferenceVectorData.m b/+types/+core/TimeSeriesReferenceVectorData.m index 354f517f8..08de1a707 100644 --- a/+types/+core/TimeSeriesReferenceVectorData.m +++ b/+types/+core/TimeSeriesReferenceVectorData.m @@ -31,7 +31,10 @@ p.PartialMatching = false; p.StructExpand = false; misc.parseSkipInvalidName(p, varargin); - if strcmp(class(obj), 'types.core.TimeSeriesReferenceVectorData') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.TimeSeriesReferenceVectorData') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/TwoPhotonSeries.m b/+types/+core/TwoPhotonSeries.m index 828a63017..29862728c 100644 --- a/+types/+core/TwoPhotonSeries.m +++ b/+types/+core/TwoPhotonSeries.m @@ -89,7 +89,10 @@ obj.imaging_plane = p.Results.imaging_plane; obj.pmt_gain = p.Results.pmt_gain; obj.scan_line_rate = p.Results.scan_line_rate; - if strcmp(class(obj), 'types.core.TwoPhotonSeries') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.TwoPhotonSeries') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/Units.m b/+types/+core/Units.m index fb32fb3ac..3df0f68ec 100644 --- a/+types/+core/Units.m +++ b/+types/+core/Units.m @@ -98,11 +98,12 @@ obj.waveforms = p.Results.waveforms; obj.waveforms_index = p.Results.waveforms_index; obj.waveforms_index_index = p.Results.waveforms_index_index; - if strcmp(class(obj), 'types.core.Units') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.Units') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); - end - if strcmp(class(obj), 'types.core.Units') types.util.dynamictable.checkConfig(obj); end end diff --git a/+types/+core/VoltageClampSeries.m b/+types/+core/VoltageClampSeries.m index 003bd54ef..3e4335687 100644 --- a/+types/+core/VoltageClampSeries.m +++ b/+types/+core/VoltageClampSeries.m @@ -122,7 +122,10 @@ obj.whole_cell_capacitance_comp_unit = p.Results.whole_cell_capacitance_comp_unit; obj.whole_cell_series_resistance_comp = p.Results.whole_cell_series_resistance_comp; obj.whole_cell_series_resistance_comp_unit = p.Results.whole_cell_series_resistance_comp_unit; - if strcmp(class(obj), 'types.core.VoltageClampSeries') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.VoltageClampSeries') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+core/VoltageClampStimulusSeries.m b/+types/+core/VoltageClampStimulusSeries.m index 678bf0fea..9dc88eb74 100644 --- a/+types/+core/VoltageClampStimulusSeries.m +++ b/+types/+core/VoltageClampStimulusSeries.m @@ -60,7 +60,10 @@ p.PartialMatching = false; p.StructExpand = false; misc.parseSkipInvalidName(p, varargin); - if strcmp(class(obj), 'types.core.VoltageClampStimulusSeries') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.core.VoltageClampStimulusSeries') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+hdmf_common/AlignedDynamicTable.m b/+types/+hdmf_common/AlignedDynamicTable.m index 892079b04..c9191e519 100644 --- a/+types/+hdmf_common/AlignedDynamicTable.m +++ b/+types/+hdmf_common/AlignedDynamicTable.m @@ -46,8 +46,6 @@ [obj.dynamictable, ivarargin] = types.util.parseConstrained(obj,'dynamictable', 'types.hdmf_common.DynamicTable', varargin{:}); varargin(ivarargin) = []; - obj.setupHasUnnamedGroupsMixin() - p = inputParser; p.KeepUnmatched = true; p.PartialMatching = false; @@ -55,11 +53,13 @@ addParameter(p, 'categories',[]); misc.parseSkipInvalidName(p, varargin); obj.categories = p.Results.categories; - if strcmp(class(obj), 'types.hdmf_common.AlignedDynamicTable') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.hdmf_common.AlignedDynamicTable') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); - end - if strcmp(class(obj), 'types.hdmf_common.AlignedDynamicTable') + obj.setupHasUnnamedGroupsMixin(); types.util.dynamictable.checkConfig(obj); end end diff --git a/+types/+hdmf_common/CSRMatrix.m b/+types/+hdmf_common/CSRMatrix.m index a2c93a2d9..521b8576c 100644 --- a/+types/+hdmf_common/CSRMatrix.m +++ b/+types/+hdmf_common/CSRMatrix.m @@ -50,7 +50,10 @@ obj.indices = p.Results.indices; obj.indptr = p.Results.indptr; obj.shape = p.Results.shape; - if strcmp(class(obj), 'types.hdmf_common.CSRMatrix') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.hdmf_common.CSRMatrix') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+hdmf_common/Container.m b/+types/+hdmf_common/Container.m index 80468457a..055f43c5b 100644 --- a/+types/+hdmf_common/Container.m +++ b/+types/+hdmf_common/Container.m @@ -17,7 +17,10 @@ % - container (types.hdmf_common.Container) - A Container object obj = obj@types.untyped.MetaClass(varargin{:}); - if strcmp(class(obj), 'types.hdmf_common.Container') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.hdmf_common.Container') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+hdmf_common/Data.m b/+types/+hdmf_common/Data.m index 87276371c..07fe4b8f9 100644 --- a/+types/+hdmf_common/Data.m +++ b/+types/+hdmf_common/Data.m @@ -35,7 +35,10 @@ addParameter(p, 'data',[]); misc.parseSkipInvalidName(p, varargin); obj.data = p.Results.data; - if strcmp(class(obj), 'types.hdmf_common.Data') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.hdmf_common.Data') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+hdmf_common/DynamicTable.m b/+types/+hdmf_common/DynamicTable.m index 1126c4953..2b335013a 100644 --- a/+types/+hdmf_common/DynamicTable.m +++ b/+types/+hdmf_common/DynamicTable.m @@ -52,9 +52,13 @@ obj.colnames = p.Results.colnames; obj.description = p.Results.description; obj.id = p.Results.id; - if strcmp(class(obj), 'types.hdmf_common.DynamicTable') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.hdmf_common.DynamicTable') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); + types.util.dynamictable.checkConfig(obj); end end %% SETTERS diff --git a/+types/+hdmf_common/DynamicTableRegion.m b/+types/+hdmf_common/DynamicTableRegion.m index ad44d2c33..a94755165 100644 --- a/+types/+hdmf_common/DynamicTableRegion.m +++ b/+types/+hdmf_common/DynamicTableRegion.m @@ -43,7 +43,10 @@ addParameter(p, 'table',[]); misc.parseSkipInvalidName(p, varargin); obj.table = p.Results.table; - if strcmp(class(obj), 'types.hdmf_common.DynamicTableRegion') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.hdmf_common.DynamicTableRegion') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+hdmf_common/ElementIdentifiers.m b/+types/+hdmf_common/ElementIdentifiers.m index be665c602..0ca63875d 100644 --- a/+types/+hdmf_common/ElementIdentifiers.m +++ b/+types/+hdmf_common/ElementIdentifiers.m @@ -29,7 +29,10 @@ p.PartialMatching = false; p.StructExpand = false; misc.parseSkipInvalidName(p, varargin); - if strcmp(class(obj), 'types.hdmf_common.ElementIdentifiers') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.hdmf_common.ElementIdentifiers') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+hdmf_common/SimpleMultiContainer.m b/+types/+hdmf_common/SimpleMultiContainer.m index baa722efb..dc7d7309b 100644 --- a/+types/+hdmf_common/SimpleMultiContainer.m +++ b/+types/+hdmf_common/SimpleMultiContainer.m @@ -37,16 +37,18 @@ [obj.data, ivarargin] = types.util.parseConstrained(obj,'data', 'types.hdmf_common.Data', varargin{:}); varargin(ivarargin) = []; - obj.setupHasUnnamedGroupsMixin() - p = inputParser; p.KeepUnmatched = true; p.PartialMatching = false; p.StructExpand = false; misc.parseSkipInvalidName(p, varargin); - if strcmp(class(obj), 'types.hdmf_common.SimpleMultiContainer') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.hdmf_common.SimpleMultiContainer') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); + obj.setupHasUnnamedGroupsMixin(); end end %% SETTERS diff --git a/+types/+hdmf_common/VectorData.m b/+types/+hdmf_common/VectorData.m index f28c2f47b..78a77b2c9 100644 --- a/+types/+hdmf_common/VectorData.m +++ b/+types/+hdmf_common/VectorData.m @@ -57,7 +57,10 @@ obj.resolution = p.Results.resolution; obj.sampling_rate = p.Results.sampling_rate; obj.unit = p.Results.unit; - if strcmp(class(obj), 'types.hdmf_common.VectorData') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.hdmf_common.VectorData') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+hdmf_common/VectorIndex.m b/+types/+hdmf_common/VectorIndex.m index 1b8b53cce..72429f355 100644 --- a/+types/+hdmf_common/VectorIndex.m +++ b/+types/+hdmf_common/VectorIndex.m @@ -43,7 +43,10 @@ addParameter(p, 'target',[]); misc.parseSkipInvalidName(p, varargin); obj.target = p.Results.target; - if strcmp(class(obj), 'types.hdmf_common.VectorIndex') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.hdmf_common.VectorIndex') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+hdmf_experimental/EnumData.m b/+types/+hdmf_experimental/EnumData.m index a723bfdb1..9f6967644 100644 --- a/+types/+hdmf_experimental/EnumData.m +++ b/+types/+hdmf_experimental/EnumData.m @@ -39,7 +39,10 @@ addParameter(p, 'elements',[]); misc.parseSkipInvalidName(p, varargin); obj.elements = p.Results.elements; - if strcmp(class(obj), 'types.hdmf_experimental.EnumData') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.hdmf_experimental.EnumData') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end diff --git a/+types/+hdmf_experimental/HERD.m b/+types/+hdmf_experimental/HERD.m index 95fa06188..d00c7e17c 100644 --- a/+types/+hdmf_experimental/HERD.m +++ b/+types/+hdmf_experimental/HERD.m @@ -60,7 +60,10 @@ obj.keys = p.Results.keys; obj.object_keys = p.Results.object_keys; obj.objects = p.Results.objects; - if strcmp(class(obj), 'types.hdmf_experimental.HERD') + + % Only execute validation/setup code when called directly in this class' + % constructor, not when invoked through superclass constructor chain + if strcmp(class(obj), 'types.hdmf_experimental.HERD') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); end From 80cddd746a193b1c2a2fba36a784caf45e81d808 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Tue, 27 Jan 2026 21:32:04 +0100 Subject: [PATCH 13/16] fix spelling --- +file/fillConstructor.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/+file/fillConstructor.m b/+file/fillConstructor.m index 9d4094370..e45adcd13 100644 --- a/+file/fillConstructor.m +++ b/+file/fillConstructor.m @@ -28,7 +28,7 @@ end % Add custom validation for DynamicTable and its descendant classes - if isDynamicTableDescendent(name, namespace) + if isDynamicTableDescendant(name, namespace) constructorElements{end+1} = ' types.util.dynamictable.checkConfig(obj);'; end @@ -270,7 +270,7 @@ docString = char( strjoin(docString, newline) ); end -function tf = isDynamicTableDescendent(name, namespace) +function tf = isDynamicTableDescendant(name, namespace) % Check if name is DynamicTable or if name is for a type that inherits from DynamicTable tf = false; From dd3b6d6845acbe046297a42a90ca6db38d6509ea Mon Sep 17 00:00:00 2001 From: ehennestad Date: Tue, 27 Jan 2026 21:42:31 +0100 Subject: [PATCH 14/16] regenerate type classes --- +types/+core/Images.m | 3 +-- +types/+hdmf_common/DynamicTable.m | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/+types/+core/Images.m b/+types/+core/Images.m index 910d4eebd..a75a996c4 100644 --- a/+types/+core/Images.m +++ b/+types/+core/Images.m @@ -41,8 +41,6 @@ [obj.baseimage, ivarargin] = types.util.parseConstrained(obj,'baseimage', 'types.core.BaseImage', varargin{:}); varargin(ivarargin) = []; - obj.setupHasUnnamedGroupsMixin() - p = inputParser; p.KeepUnmatched = true; p.PartialMatching = false; @@ -58,6 +56,7 @@ if strcmp(class(obj), 'types.core.Images') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); + obj.setupHasUnnamedGroupsMixin(); end end %% SETTERS diff --git a/+types/+hdmf_common/DynamicTable.m b/+types/+hdmf_common/DynamicTable.m index c11ab1274..3d3ce85b8 100644 --- a/+types/+hdmf_common/DynamicTable.m +++ b/+types/+hdmf_common/DynamicTable.m @@ -44,8 +44,6 @@ [obj.vectordata, ivarargin] = types.util.parseConstrained(obj,'vectordata', 'types.hdmf_common.VectorData', varargin{:}); varargin(ivarargin) = []; - obj.setupHasUnnamedGroupsMixin() - p = inputParser; p.KeepUnmatched = true; p.PartialMatching = false; @@ -63,6 +61,7 @@ if strcmp(class(obj), 'types.hdmf_common.DynamicTable') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); + obj.setupHasUnnamedGroupsMixin(); types.util.dynamictable.checkConfig(obj); end end From c1814e7eac76f69549c1857a057dfb71dd834e0b Mon Sep 17 00:00:00 2001 From: ehennestad Date: Tue, 10 Mar 2026 13:46:34 +0100 Subject: [PATCH 15/16] Update TutorialTest.m --- +tests/+system/+tutorial/TutorialTest.m | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/+tests/+system/+tutorial/TutorialTest.m b/+tests/+system/+tutorial/TutorialTest.m index 8e2ff6cfd..5f6302e87 100644 --- a/+tests/+system/+tutorial/TutorialTest.m +++ b/+tests/+system/+tutorial/TutorialTest.m @@ -153,7 +153,11 @@ function inspectTutorialFileWithNwbInspector(testCase) function resultsOut = filterNWBInspectorResults(resultsIn) CHECK_IGNORE = [... "check_image_series_external_file_valid", ... - "check_regular_timestamps" + "check_regular_timestamps", ... + "check_subject_exists", ... + "check_subject_id_exists", ... + "check_subject_sex", ... + "check_subject_age" ]; [resultsIn(:).ignore] = deal(false); for i = 1:numel(resultsIn) From 68740a8efbc4e981dc5227a42344bcf9b502dca8 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Mon, 13 Apr 2026 11:19:43 +0200 Subject: [PATCH 16/16] Fix merge error --- +file/fillConstructor.m | 7 +------ +types/+core/Images.m | 3 +-- +types/+core/Units.m | 2 +- +types/+hdmf_common/DynamicTable.m | 3 +-- 4 files changed, 4 insertions(+), 11 deletions(-) diff --git a/+file/fillConstructor.m b/+file/fillConstructor.m index 53c3e1a9e..2a893d88b 100644 --- a/+file/fillConstructor.m +++ b/+file/fillConstructor.m @@ -23,7 +23,7 @@ ' types.util.checkUnset(obj, unique(cellStringArguments));'}; % Include the setup function for the HasUnnamedGroups mixin if applicable - if isa(class, 'file.Group') && class.hasAnonGroups + if isa(class, 'file.Group') && (class.hasAnonGroups || class.hasAnonData) constructorElements{end+1} = ' obj.setupHasUnnamedGroupsMixin();'; end @@ -180,11 +180,6 @@ fullBody = strjoin(fullBody, newline); bodystr(end+1:end+length(fullBody)+1) = [newline fullBody]; - if isa(class, 'file.Group') && (class.hasAnonGroups || class.hasAnonData) - % Include the setup function for the HasUnnamedGroups mixin - bodystr = [bodystr, newline, 'obj.setupHasUnnamedGroupsMixin()', newline]; - end - parser = {... 'p = inputParser;',... 'p.KeepUnmatched = true;',... diff --git a/+types/+core/Images.m b/+types/+core/Images.m index 246050ec4..f60e065d4 100644 --- a/+types/+core/Images.m +++ b/+types/+core/Images.m @@ -41,8 +41,6 @@ [obj.baseimage, ivarargin] = types.util.parseConstrained(obj,'baseimage', 'types.core.BaseImage', varargin{:}); varargin(ivarargin) = []; - obj.setupHasUnnamedGroupsMixin() - p = inputParser; p.KeepUnmatched = true; p.PartialMatching = false; @@ -58,6 +56,7 @@ if strcmp(class(obj), 'types.core.Images') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); + obj.setupHasUnnamedGroupsMixin(); end end %% SETTERS diff --git a/+types/+core/Units.m b/+types/+core/Units.m index e6af0fb04..a09fa714e 100644 --- a/+types/+core/Units.m +++ b/+types/+core/Units.m @@ -131,7 +131,7 @@ obj.waveforms_index_index = p.Results.waveforms_index_index; obj.waveforms_sampling_rate = p.Results.waveforms_sampling_rate; obj.waveforms_unit = p.Results.waveforms_unit; - + % Only execute validation/setup code when called directly in this class's % constructor, not when invoked through superclass constructor chain if strcmp(class(obj), 'types.core.Units') %#ok diff --git a/+types/+hdmf_common/DynamicTable.m b/+types/+hdmf_common/DynamicTable.m index 7e95a2796..9a9d5a095 100644 --- a/+types/+hdmf_common/DynamicTable.m +++ b/+types/+hdmf_common/DynamicTable.m @@ -44,8 +44,6 @@ [obj.vectordata, ivarargin] = types.util.parseConstrained(obj,'vectordata', 'types.hdmf_common.VectorData', varargin{:}); varargin(ivarargin) = []; - obj.setupHasUnnamedGroupsMixin() - p = inputParser; p.KeepUnmatched = true; p.PartialMatching = false; @@ -63,6 +61,7 @@ if strcmp(class(obj), 'types.hdmf_common.DynamicTable') %#ok cellStringArguments = convertContainedStringsToChars(varargin(1:2:end)); types.util.checkUnset(obj, unique(cellStringArguments)); + obj.setupHasUnnamedGroupsMixin(); types.util.dynamictable.checkConfig(obj); end end