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 - 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 - struct containing, the heirarchy of all of (and 0030 % only) the valid field names (field values are ignored) 0031 % exceptions - a struct array, with the following fields, defining 0032 % exceptions to the top-level options 0033 % name - name (can be multi-level) of field to which 0034 % exception applies 0035 % check - same as OPT.check, only for specified field 0036 % copy_mode - same as OPT.copy_mode, only for specified field 0037 % valid_fields- same as OPT.valid_fields, for specified field 0038 % PARENT - cell array of parent field names used by NESTED_STRUCT_COPY 0039 % with recursive calls to allow checking of multi-level field 0040 % field names in exceptions, e.g. when called recursively to 0041 % assign the field S.info.address.home the value of PARENT would 0042 % be {'info', 'address'}. 0043 % 0044 % Output: 0045 % DS - the combined destination struct 0046 % 0047 % Examples: 0048 % See T_NESTED_STRUCT_COPY (t_nested_struct_copy.m). 0049 % 0050 % TO DO: Finish example. 0051 % Implement an error that passes back the full field string of 0052 % an invalid field so that mpoption can refer to it as option foo. 0053 0054 % MATPOWER 0055 % $Id: nested_struct_copy.m 2229 2013-12-11 01:28:09Z ray $ 0056 % by Ray Zimmerman, PSERC Cornell 0057 % Copyright (c) 2013 by Power System Engineering Research Center (PSERC) 0058 % 0059 % This file is part of MATPOWER. 0060 % See http://www.pserc.cornell.edu/matpower/ for more info. 0061 % 0062 % MATPOWER is free software: you can redistribute it and/or modify 0063 % it under the terms of the GNU General Public License as published 0064 % by the Free Software Foundation, either version 3 of the License, 0065 % or (at your option) any later version. 0066 % 0067 % MATPOWER is distributed in the hope that it will be useful, 0068 % but WITHOUT ANY WARRANTY; without even the implied warranty of 0069 % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0070 % GNU General Public License for more details. 0071 % 0072 % You should have received a copy of the GNU General Public License 0073 % along with MATPOWER. If not, see <http://www.gnu.org/licenses/>. 0074 % 0075 % Additional permission under GNU GPL version 3 section 7 0076 % 0077 % If you modify MATPOWER, or any covered work, to interface with 0078 % other modules (such as MATLAB code and MEX-files) available in a 0079 % MATLAB(R) or comparable environment containing parts covered 0080 % under other licensing terms, the licensors of MATPOWER grant 0081 % you additional permission to convey the resulting work. 0082 0083 DEBUG = 0; 0084 0085 %% set default input args 0086 if nargin < 4 0087 parent = {}; 0088 if nargin < 3 0089 opt = struct; 0090 end 0091 end 0092 0093 %% set up options 0094 if isfield(opt, 'check') 0095 check = opt.check; 0096 else 0097 check = 0; 0098 end 0099 if isfield(opt, 'copy_mode') 0100 copy_mode = opt.copy_mode; 0101 else 0102 copy_mode = ''; 0103 end 0104 if isfield(opt, 'valid_fields') 0105 valid_fields = opt.valid_fields; 0106 else 0107 valid_fields = d; 0108 end 0109 if isfield(opt, 'exceptions') 0110 exceptions = opt.exceptions; 0111 else 0112 exceptions = struct('name', {}); 0113 end 0114 0115 %% form parent string 0116 if DEBUG, fprintf('nested_struct_copy() : parent = %s\n', strjoin(parent, '.')); end 0117 if nargin > 3 && ~isempty(parent) 0118 tmp = cell(2, length(parent)); 0119 tmp(1,:) = parent; 0120 tmp(2,1:length(parent)-1) = {'.'}; 0121 parentstr = [tmp{:}]; 0122 else 0123 parentstr = ''; 0124 end 0125 0126 %% process fields 0127 fields = fieldnames(s); 0128 for f = 1:length(fields) 0129 ff = fields{f}; 0130 [ck, cm, vf] = deal(check, copy_mode, valid_fields); 0131 0132 %% form full field name 0133 if isempty(parentstr) 0134 str = ff; 0135 else 0136 str = [parentstr '.' ff]; 0137 end 0138 0139 %% field doesn't exist in valid_fields 0140 if ~isfield(valid_fields, ff) 0141 if ck > 0 %% throw an error 0142 error('nested_struct_copy: ''%s'' is not a valid field name', str); 0143 elseif ck < 0 %% skip to next field 0144 continue; 0145 end 0146 end 0147 0148 %% look for an exception that matches this field 0149 if isempty(exceptions) 0150 k = []; 0151 else 0152 k = find(cellfun(@strcmp, {exceptions.name}', ... 0153 cellstr(char(ones(length(exceptions),1) * str)) )); 0154 if ~isempty(k) 0155 if isfield(exceptions, 'copy_mode') && ... 0156 ( ischar(exceptions(k).copy_mode) || ... 0157 ~isempty(exceptions(k).copy_mode) ) 0158 cm = exceptions(k).copy_mode; 0159 end 0160 if isfield(exceptions, 'check') && ... 0161 ~isempty(exceptions(k).check) 0162 ck = exceptions(k).check; 0163 end 0164 if isfield(exceptions, 'valid_fields') && ... 0165 ~isempty(exceptions(k).valid_fields) 0166 vf = exceptions(k).valid_fields; 0167 end 0168 end 0169 end 0170 0171 %% copy the field 0172 if strcmp(class(cm), 'function_handle') 0173 %% assign via function handle 0174 d.(ff) = cm(s.(ff)); 0175 elseif ~isstruct(s.(ff)) || (ischar(cm) && strcmp(cm, '=')) 0176 %% non-struct or struct with cm == '=', assign directly 0177 d.(ff) = s.(ff); 0178 elseif isstruct(s.(ff)) && isempty(cm) 0179 %% assign via recursive call to nested_struct_copy() 0180 if isfield(vf, ff) 0181 newvf = vf.(ff); %% use sub-field of valid_fields if present 0182 else 0183 newvf = struct; 0184 end 0185 newopt = struct( ... 0186 'check', ck, ... 0187 'copy_mode', cm, ... 0188 'valid_fields', newvf, ... 0189 'exceptions', exceptions ... 0190 ); 0191 if ~isfield(d, ff) %% create field if it doesn't exist in d 0192 d.(ff) = struct; 0193 end 0194 d.(ff) = nested_struct_copy(d.(ff), s.(ff), newopt, {parent{:}, ff}); 0195 else 0196 error('nested_struct_copy: OPT.copy_mode must be '''', ''='', or a function handle\n'); 0197 end 0198 end