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