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 % Copyright (c) 2013-2016, Power Systems Engineering Research Center (PSERC) 0056 % by Ray Zimmerman, PSERC Cornell 0057 % 0058 % This file is part of MATPOWER. 0059 % Covered by the 3-clause BSD License (see LICENSE file for details). 0060 % See http://www.pserc.cornell.edu/matpower/ for more info. 0061 0062 DEBUG = 0; 0063 0064 %% set default input args 0065 if nargin < 4 0066 parent = {}; 0067 if nargin < 3 0068 opt = struct; 0069 end 0070 end 0071 0072 %% set up options 0073 if isfield(opt, 'check') 0074 check = opt.check; 0075 else 0076 check = 0; 0077 end 0078 if isfield(opt, 'copy_mode') 0079 copy_mode = opt.copy_mode; 0080 else 0081 copy_mode = ''; 0082 end 0083 if isfield(opt, 'valid_fields') 0084 valid_fields = opt.valid_fields; 0085 else 0086 valid_fields = d; 0087 end 0088 if isfield(opt, 'exceptions') 0089 exceptions = opt.exceptions; 0090 else 0091 exceptions = struct('name', {}); 0092 end 0093 0094 %% form parent string 0095 if DEBUG, fprintf('nested_struct_copy() : parent = %s\n', strjoin(parent, '.')); end 0096 if nargin > 3 && ~isempty(parent) 0097 pl = length(parent); 0098 tmp = cell(2, pl); 0099 tmp(1,:) = parent; 0100 tmp(2,1:pl-1) = {'.'}; 0101 tmp(2,pl) = {''}; 0102 parentstr = [tmp{:}]; 0103 else 0104 parentstr = ''; 0105 end 0106 0107 %% process fields 0108 fields = fieldnames(s); 0109 for f = 1:length(fields) 0110 ff = fields{f}; 0111 0112 %% form full field name 0113 if isempty(parentstr) 0114 str = ff; 0115 else 0116 str = [parentstr '.' ff]; 0117 end 0118 0119 %% field doesn't exist in valid_fields 0120 if ~isfield(valid_fields, ff) 0121 if check > 0 %% throw an error 0122 error('nested_struct_copy: ''%s'' is not a valid field name', str); 0123 elseif check < 0 %% skip to next field 0124 continue; 0125 end 0126 end 0127 0128 ck = check; 0129 cm = copy_mode; 0130 vf = valid_fields; 0131 0132 %% look for an exception that matches this field 0133 if isempty(exceptions) 0134 k = []; 0135 else 0136 k = find(strcmp(str, {exceptions.name}'), 1); 0137 if ~isempty(k) 0138 if isfield(exceptions, 'copy_mode') && ... 0139 ( ischar(exceptions(k).copy_mode) || ... 0140 ~isempty(exceptions(k).copy_mode) ) 0141 cm = exceptions(k).copy_mode; 0142 end 0143 if isfield(exceptions, 'check') && ... 0144 ~isempty(exceptions(k).check) 0145 ck = exceptions(k).check; 0146 end 0147 if isfield(exceptions, 'valid_fields') && ... 0148 ~isempty(exceptions(k).valid_fields) 0149 vf = exceptions(k).valid_fields; 0150 end 0151 end 0152 end 0153 0154 %% copy the field 0155 if strcmp(class(cm), 'function_handle') 0156 %% assign via function handle 0157 d.(ff) = cm(s.(ff)); 0158 elseif ~isstruct(s.(ff)) || (ischar(cm) && strcmp(cm, '=')) || ... 0159 (isfield(d, ff) && ~isstruct(d.(ff))) 0160 %% non-struct OR struct with cm == '=' OR struct to non-struct copy 0161 %% assign directly 0162 d.(ff) = s.(ff); 0163 elseif isstruct(s.(ff)) && isempty(cm) 0164 %% assign via recursive call to nested_struct_copy() 0165 if isfield(vf, ff) 0166 newvf = vf.(ff); %% use sub-field of valid_fields if present 0167 else 0168 newvf = struct; 0169 end 0170 newopt = struct( ... 0171 'check', ck, ... 0172 'copy_mode', cm, ... 0173 'valid_fields', newvf, ... 0174 'exceptions', exceptions ... 0175 ); 0176 if ~isfield(d, ff) %% create field if it doesn't exist in d 0177 d.(ff) = struct; 0178 end 0179 d.(ff) = nested_struct_copy(d.(ff), s.(ff), newopt, {parent{:}, ff}); 0180 else 0181 error('nested_struct_copy: OPT.copy_mode must be '''', ''='', or a function handle\n'); 0182 end 0183 end