Home > matpower7.1 > mp-opt-model > lib > nested_struct_copy.m

nested_struct_copy

PURPOSE ^

NESTED_STRUCT_COPY Copies values from one nested struct to another.

SYNOPSIS ^

function d = nested_struct_copy(d, s, opt, parent)

DESCRIPTION ^

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.

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SOURCE CODE ^

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

Generated on Fri 09-Oct-2020 11:21:31 by m2html © 2005