Home > functions > internal > dsCheckOptions.m

dsCheckOptions

PURPOSE ^

CHECKOPTIONS - organize key/value pairs in structure with default or user-supplied values according to a schema

SYNOPSIS ^

function [parms, params_unspecified ] = dsCheckOptions(options, options_schema, strict)

DESCRIPTION ^

CHECKOPTIONS - organize key/value pairs in structure with default or user-supplied values according to a schema

 Usage:
   options = dsCheckOptions(keyvals, options_schema, [strict])

 Inputs:
   - keyvals: list of key/value pairs ('option1',value1,'option2',value2,...)
   - options_schema: cell array containing 3 values per known 'option':
     - option name
     - default value
     - allowed values:
         - vector of true/false values
         - vector of min/max values
         - vector of allowed values (more than 2 elements)
         - cell array of allowed values
         - empty to specify no limitations.
   - strict (default: true): whether to fail if options not specified in the
       options_schema are found.

 Note: this function was adapted from one developed "in-house" years ago...

 Outputs:
   - options: structure with options (using default values if not supplied)

 See also: dsOptions2Keyval, dsCheckSpecification, dsCheckModel, dsCheckData

 Author: Jason Sherfey, PhD <jssherfey@gmail.com>
 Copyright (C) 2016 Jason Sherfey, Boston University, USA

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SOURCE CODE ^

0001 function [parms, params_unspecified ] = dsCheckOptions(options, options_schema, strict)
0002 %CHECKOPTIONS - organize key/value pairs in structure with default or user-supplied values according to a schema
0003 %
0004 % Usage:
0005 %   options = dsCheckOptions(keyvals, options_schema, [strict])
0006 %
0007 % Inputs:
0008 %   - keyvals: list of key/value pairs ('option1',value1,'option2',value2,...)
0009 %   - options_schema: cell array containing 3 values per known 'option':
0010 %     - option name
0011 %     - default value
0012 %     - allowed values:
0013 %         - vector of true/false values
0014 %         - vector of min/max values
0015 %         - vector of allowed values (more than 2 elements)
0016 %         - cell array of allowed values
0017 %         - empty to specify no limitations.
0018 %   - strict (default: true): whether to fail if options not specified in the
0019 %       options_schema are found.
0020 %
0021 % Note: this function was adapted from one developed "in-house" years ago...
0022 %
0023 % Outputs:
0024 %   - options: structure with options (using default values if not supplied)
0025 %
0026 % See also: dsOptions2Keyval, dsCheckSpecification, dsCheckModel, dsCheckData
0027 %
0028 % Author: Jason Sherfey, PhD <jssherfey@gmail.com>
0029 % Copyright (C) 2016 Jason Sherfey, Boston University, USA
0030 
0031 
0032 % Convert cell argument to struct if contains struct (Leave as is if already a struct)
0033 if length(options) == 1 && ~isstruct(options) && isstruct(options{1})
0034   options = options{1};
0035 end
0036 
0037 if ~isstruct(options) && (0 ~= mod(length(options),2))     %   Validate that the # of args is even
0038   error('List of arguments must be even (must have name/value pair)');
0039 end
0040 if (0 ~= mod(length(options_schema),3))    %   Validate the options_schema info is right
0041   error('Programming error: list of default arguments must be even (must have name/value pair)');
0042 end
0043 if (~exist('strict', 'var'))
0044   strict = true;
0045 end
0046 
0047 parms = [];
0048 
0049 %   Rip all cell arguments into non-cell arguments?
0050 %     NOTE: currently just converts empty cells into empty arrays
0051 if ~isstruct(options)
0052   for ind = 1:length(options)
0053     if iscell(options{ind})
0054       if isempty(options{ind})
0055         options{ind}=[];
0056       elseif ~iscell(options{ind}{1})
0057         %options{index} = { options{index} };
0058       end
0059     end
0060   end
0061 else
0062   flds = fieldnames(options);
0063   for ind = 1:length(flds)
0064     fld = flds{ind};
0065     if iscell(options.(fld))
0066       if isempty(options.(fld))
0067         options.(fld)=[];
0068       elseif ~iscell(options.(fld){1})
0069         %options{index} = { options{index} };
0070       end
0071     end
0072   end
0073 end
0074 
0075 if ~isstruct(options) % if arguments given as list
0076   input_fields  = options(1:2:end);
0077 else
0078   input_fields  = fieldnames(options);
0079 end
0080 valid_fields    = options_schema(1:3:end);
0081 unknown_fields  = setdiff(input_fields, valid_fields);
0082 
0083 %   Validate that there are no extraneous params sent in
0084 if (strict && ~isempty(unknown_fields))
0085   error('The following unrecogized options were passed in: %s', sprintf('%s ',unknown_fields{:}));
0086 end
0087 
0088 if ~isstruct(options) %convert to struct if arguments given as list
0089   if (~isempty( options ))
0090     for f=1:length(options)/2
0091       parms.(options{2*f-1})=options{2*f};
0092     end
0093     %parms=struct(options{:});
0094   end
0095 else
0096   parms = options;
0097 end
0098 
0099 %   This allows 'pass-through' of parameters;
0100 %   remove any fields that are unknown
0101 %   unless no schema is defined.
0102 
0103 params_unspecified = struct;
0104 if (~strict && ~isempty(parms) && ~isempty(options_schema))
0105   for i = 1:length(unknown_fields)
0106       params_unspecified.(unknown_fields{i}) = parms.(unknown_fields{i});
0107   end
0108   parms = rmfield(parms,unknown_fields);
0109 end
0110 
0111 %   Check arg values and set defaults
0112 for f=1:3:length(options_schema)
0113   %   The value has been set explicitly by the caller;
0114   %   Validate the input parameters by the 'range' field
0115   if  (isfield(parms,options_schema{f}))
0116     param_name        = options_schema{f};
0117     param_value        = getfield(parms,param_name);
0118     param_range   = options_schema{f+2};
0119 
0120     % no value was specified,
0121     if isempty(param_value)
0122       parms = setfield(parms, options_schema{f}, options_schema{f+1});
0123     % no range was specified,
0124     elseif isempty(param_range)
0125       continue;
0126      %    param range is a cell array of strings; make sure the current value is within that range.
0127     elseif iscell(param_range)
0128       num_flag = 0;
0129       char_flag = 0;
0130       
0131       for i=1:length(param_range)
0132         if isnumeric(param_range{i}), num_flag=1; end
0133         if ischar(param_range{i}), char_flag=1; end
0134       end
0135       
0136       if num_flag && char_flag
0137         error('type of parameter range (cell array of numbers and strings) specified for parameter ''%s'' is currently unsupported', options_schema{f});
0138       elseif char_flag
0139         if iscell(param_value)
0140           for i=1:length(param_value)
0141             if ~ischar(param_value{i})
0142               error('parameter ''%s'' must be string or cell array of strings', options_schema{f});
0143             end
0144           end
0145         elseif ~ischar(param_value)
0146           error('parameter ''%s'' must be string or cell array of strings', options_schema{f});
0147         else
0148           param_value = {param_value};
0149         end
0150         
0151         if length(find(ismember(param_value,param_range))) ~= length(param_value)
0152           error('parameter ''%s'' value must be one of the following: { %s}', ...
0153             param_name, sprintf('''%s'' ',param_range{:}));
0154         end
0155       elseif num_flag
0156         param_range = cell2mat(param_range);
0157         
0158         if ~isnumeric(param_value)
0159           error('parameter ''%s'' must be numeric', options_schema{f});
0160         end
0161         
0162         if length(find(ismember(param_value,param_range))) ~= length(param_value)
0163           error('parameter ''%s'' value must be one of the following: { %s}', ...
0164             param_name,sprintf('%d ',param_range));
0165         end
0166       else
0167         error('type of parameter range specified for parameter ''%s'' is currently unsupported', options_schema{f});
0168       end
0169     %  param range is logical and has two elements (i.e. true/false)
0170     elseif islogical(param_range) && length(param_range)==2
0171       if ~ismember(param_value,param_range)
0172         error('parameter %s value must be true (1) or false (0)', options_schema{f});
0173       end
0174     %  param range is numeric and has two elements (i.e. min and max)
0175     elseif isnumeric(param_range) && length(param_range)==2
0176       %  param range is numeric or logical, and within a specified range,
0177       if ~isempty(find(param_value < param_range(1))) || ...
0178          ~isempty(find(param_value > param_range(2)))
0179         if int64(param_range(1))==param_range(1) && int64(param_range(2))==param_range(2)
0180           error('parameter %s value must be between %d and %d',...
0181             options_schema{f},param_range(1),param_range(2));
0182         else
0183           error('parameter %s value must be between %0.4f and %0.4f',...
0184             options_schema{f},param_range(1),param_range(2));
0185         end
0186       end
0187     %  param range is numeric and has more than two elements (allowed values)
0188     elseif isnumeric(param_range)
0189       if ~isnumeric(param_value)
0190         error('parameter ''%s'' must be numeric', options_schema{f});
0191       end
0192       
0193       if length(find(ismember(param_value,param_range))) ~= length(param_value)
0194         error('parameter ''%s'' value must be one of the following: [ %s]', ...
0195           param_name,sprintf('%d ',param_range));
0196       end
0197     %  param range is of a type we currently don't support.
0198     else
0199       error('type of parameter range specified for parameter ''%s'' is currently unsupported', options_schema{f});
0200     end
0201   % field not found, so set the default value.
0202   else
0203     %parms = setfield(parms, options_schema{f}, options_schema{f+1});
0204     parms.(options_schema{f})=options_schema{f+1};
0205   end
0206 end

Generated on Tue 12-Dec-2017 11:32:10 by m2html © 2005