NESTED_STRUCT_COPY Copies values from one nested struct to another. DS = NESTED_STRUCT_COPY(D, S) DS = NESTED_STRUCT_COPY(D, S, OPT) DS = NESTED_STRUCT_COPY(D, S, OPT, PARENT) DS = NESTED_STRUCT_COPY(D, S) copies values from a source struct S to a destination struct D in a nested, recursive manner. That is, the value of each field in S is copied directly to the corresponding field in D, unless that value is itself a struct, in which case the copy is done via a recursive call to NESTED_STRUCT_COPY. Inputs: D - the destination struct that values are copied to S - the source struct containing the values to be copied from OPT - (optional) options struct controlling copy behavior, with fields: check - check that field name is valid, by looking for it in OPT.valid_fields (defaults to D), before copying 0 - (default) do not do any field name checking 1 - fatal error if S contains an invalid field name -1 - skip any invalid fields in S copy_mode - how to handle assignment of fields that are structs '' - (default) recursive call to nested_struct_copy() '=' - direct assignment, D.<field> = S.<field> @<function> - pointer to a function to be called with field from S, returning field to assign to D D.<field> = <function>(S.<field>) valid_fields - (default = D) struct containing the heirarchy of all of (and only) the valid field names (field values are ignored) exceptions - a struct array, with the following fields, defining exceptions to the top-level options name - name (can be multi-level) of field to which exception applies check - same as OPT.check, only for specified field copy_mode - same as OPT.copy_mode, only for specified field valid_fields- same as OPT.valid_fields, for specified field PARENT - cell array of parent field names used by NESTED_STRUCT_COPY with recursive calls to allow checking of multi-level field field names in exceptions, e.g. when called recursively to assign the field S.info.address.home the value of PARENT would be {'info', 'address'}. Output: DS - the combined destination struct Examples: See T_NESTED_STRUCT_COPY (t_nested_struct_copy.m). TO DO: Finish example. Implement an error that passes back the full field string of an invalid field so that mpoption can refer to it as option foo.
0001 function d = nested_struct_copy(d, s, opt, parent) 0002 %NESTED_STRUCT_COPY Copies values from one nested struct to another. 0003 % 0004 % DS = NESTED_STRUCT_COPY(D, S) 0005 % DS = NESTED_STRUCT_COPY(D, S, OPT) 0006 % DS = NESTED_STRUCT_COPY(D, S, OPT, PARENT) 0007 % 0008 % DS = NESTED_STRUCT_COPY(D, S) copies values from a source struct S to 0009 % a destination struct D in a nested, recursive manner. That is, the value 0010 % of each field in S is copied directly to the corresponding field in D, 0011 % unless that value is itself a struct, in which case the copy is done 0012 % via a recursive call to NESTED_STRUCT_COPY. 0013 % 0014 % Inputs: 0015 % D - the destination struct that values are copied to 0016 % S - the source struct containing the values to be copied from 0017 % OPT - (optional) options struct controlling copy behavior, with fields: 0018 % check - check that field name is valid, by looking for it in 0019 % OPT.valid_fields (defaults to D), before copying 0020 % 0 - (default) do not do any field name checking 0021 % 1 - fatal error if S contains an invalid field name 0022 % -1 - skip any invalid fields in S 0023 % copy_mode - how to handle assignment of fields that are structs 0024 % '' - (default) recursive call to nested_struct_copy() 0025 % '=' - direct assignment, D.<field> = S.<field> 0026 % @<function> - pointer to a function to be called with field 0027 % from S, returning field to assign to D 0028 % D.<field> = <function>(S.<field>) 0029 % valid_fields - (default = D) struct containing the heirarchy 0030 % of all of (and only) the valid field names 0031 % (field values are ignored) 0032 % exceptions - a struct array, with the following fields, defining 0033 % exceptions to the top-level options 0034 % name - name (can be multi-level) of field to which 0035 % exception applies 0036 % check - same as OPT.check, only for specified field 0037 % copy_mode - same as OPT.copy_mode, only for specified field 0038 % valid_fields- same as OPT.valid_fields, for specified field 0039 % PARENT - cell array of parent field names used by NESTED_STRUCT_COPY 0040 % with recursive calls to allow checking of multi-level field 0041 % field names in exceptions, e.g. when called recursively to 0042 % assign the field S.info.address.home the value of PARENT would 0043 % be {'info', 'address'}. 0044 % 0045 % Output: 0046 % DS - the combined destination struct 0047 % 0048 % Examples: 0049 % See T_NESTED_STRUCT_COPY (t_nested_struct_copy.m). 0050 % 0051 % TO DO: Finish example. 0052 % Implement an error that passes back the full field string of 0053 % an invalid field so that mpoption can refer to it as option foo. 0054 0055 % MP-Opt-Model 0056 % Copyright (c) 2013-2020, Power Systems Engineering Research Center (PSERC) 0057 % by Ray Zimmerman, PSERC Cornell 0058 % 0059 % This file is part of MP-Opt-Model. 0060 % Covered by the 3-clause BSD License (see LICENSE file for details). 0061 % See https://github.com/MATPOWER/mp-opt-model for more info. 0062 0063 DEBUG = 0; 0064 0065 %% set default input args 0066 if nargin < 4 0067 parent = {}; 0068 if nargin < 3 0069 opt = struct; 0070 end 0071 end 0072 0073 %% set up options 0074 if isfield(opt, 'check') 0075 check = opt.check; 0076 else 0077 check = 0; 0078 end 0079 if isfield(opt, 'copy_mode') 0080 copy_mode = opt.copy_mode; 0081 else 0082 copy_mode = ''; 0083 end 0084 if isfield(opt, 'valid_fields') 0085 valid_fields = opt.valid_fields; 0086 else 0087 valid_fields = d; 0088 end 0089 if isfield(opt, 'exceptions') 0090 exceptions = opt.exceptions; 0091 else 0092 exceptions = struct('name', {}); 0093 end 0094 0095 %% form parent string 0096 if DEBUG, fprintf('nested_struct_copy() : parent = %s\n', strjoin(parent, '.')); end 0097 if nargin > 3 && ~isempty(parent) 0098 pl = length(parent); 0099 tmp = cell(2, pl); 0100 tmp(1,:) = parent; 0101 tmp(2,1:pl-1) = {'.'}; 0102 tmp(2,pl) = {''}; 0103 parentstr = [tmp{:}]; 0104 else 0105 parentstr = ''; 0106 end 0107 0108 %% process fields 0109 fields = fieldnames(s); 0110 for f = 1:length(fields) 0111 ff = fields{f}; 0112 0113 %% form full field name 0114 if isempty(parentstr) 0115 str = ff; 0116 else 0117 str = [parentstr '.' ff]; 0118 end 0119 0120 %% field doesn't exist in valid_fields 0121 if ~isfield(valid_fields, ff) 0122 if check > 0 %% throw an error 0123 error('nested_struct_copy: ''%s'' is not a valid field name', str); 0124 elseif check < 0 %% skip to next field 0125 continue; 0126 end 0127 end 0128 0129 ck = check; 0130 cm = copy_mode; 0131 vf = valid_fields; 0132 0133 %% look for an exception that matches this field 0134 if isempty(exceptions) 0135 k = []; 0136 else 0137 k = find(strcmp(str, {exceptions.name}'), 1); 0138 if ~isempty(k) 0139 if isfield(exceptions, 'copy_mode') && ... 0140 ( ischar(exceptions(k).copy_mode) || ... 0141 ~isempty(exceptions(k).copy_mode) ) 0142 cm = exceptions(k).copy_mode; 0143 end 0144 if isfield(exceptions, 'check') && ... 0145 ~isempty(exceptions(k).check) 0146 ck = exceptions(k).check; 0147 end 0148 if isfield(exceptions, 'valid_fields') && ... 0149 ~isempty(exceptions(k).valid_fields) 0150 vf = exceptions(k).valid_fields; 0151 end 0152 end 0153 end 0154 0155 %% copy the field 0156 if strcmp(class(cm), 'function_handle') 0157 %% assign via function handle 0158 d.(ff) = cm(s.(ff)); 0159 elseif ~isstruct(s.(ff)) || (ischar(cm) && strcmp(cm, '=')) || ... 0160 (isfield(d, ff) && ~isstruct(d.(ff))) 0161 %% non-struct OR struct with cm == '=' OR struct to non-struct copy 0162 %% assign directly 0163 d.(ff) = s.(ff); 0164 elseif isstruct(s.(ff)) && isempty(cm) 0165 %% assign via recursive call to nested_struct_copy() 0166 if isfield(vf, ff) 0167 newvf = vf.(ff); %% use sub-field of valid_fields if present 0168 else 0169 newvf = struct; 0170 end 0171 newopt = struct( ... 0172 'check', ck, ... 0173 'copy_mode', cm, ... 0174 'valid_fields', newvf, ... 0175 'exceptions', exceptions ... 0176 ); 0177 if ~isfield(d, ff) %% create field if it doesn't exist in d 0178 d.(ff) = struct; 0179 end 0180 ss = s.(ff); 0181 if length(ss) > 1 0182 d.(ff) = ss; 0183 else 0184 d.(ff) = nested_struct_copy(d.(ff), ss, newopt, {parent{:}, ff}); 0185 end 0186 else 0187 error('nested_struct_copy: OPT.copy_mode must be '''', ''='', or a function handle\n'); 0188 end 0189 end