## Copyright (C) 2011-2012 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{parser} =} inputParser () ## Create object @var{parser} of the inputParser class. ## ## This class is designed to allow easy parsing of function arguments. This class ## supports four types of arguments: ## ## @enumerate ## @item mandatory (see @command{@@inputParser/addRequired}); ## @item optional (see @command{@@inputParser/addOptional}); ## @item named (see @command{@@inputParser/addParamValue}); ## @item switch (see @command{@@inputParser/addSwitch}). ## @end enumerate ## ## After defining the function API with this methods, the supplied arguments can ## be parsed with the @command{@@inputParser/parse} method and the parsing results ## accessed with the @command{Results} accessor. ## ## @deftypefnx {Accessor method} parser.Parameters ## Return list of parameters name already defined. ## ## @deftypefnx {Accessor method} parser.Results ## Return structure with argument names as fieldnames and corresponding values. ## ## @deftypefnx {Accessor method} parser.Unmatched ## Return structure similar to @command{Results} for unmatched parameters. See ## the @command{KeepUnmatched} property. ## ## @deftypefnx {Accessor method} parser.UsingDefaults ## Return cell array with the names of arguments that are using default values. ## ## @deftypefnx {Class property} parser.CaseSensitive = @var{boolean} ## Set whether matching of argument names should be case sensitive. Defaults to false. ## ## @deftypefnx {Class property} parser.FunctionName = @var{name} ## Set function name to be used on error messages. Defauls to empty string. ## ## @deftypefnx {Class property} parser.KeepUnmatched = @var{boolean} ## Set whether an error should be given for non-defined arguments. Defaults to ## false. If set to true, the extra arguments can be accessed through ## @code{Unmatched} after the @code{parse} method. Note that since @command{Switch} ## and @command{ParamValue} arguments can be mixed, it is not possible to know ## the unmatched type. If argument is found unmatched it is assumed to be of the ## @command{ParamValue} type and it is expected to be followed by a value. ## ## @deftypefnx {Class property} parser.StructExpand = @var{boolean} ## Set whether a structure can be passed to the function instead of parameter ## value pairs. Defaults to true. Not implemented yet. ## ## The following example shows how to use this class: ## ## @example ## @group ## function check (pack, path, mat, varargin) ## p = inputParser; # create object ## p.FunctionName = "check"; # set function name ## p = p.addRequired ("pack", @@ischar); # create mandatory argument ## ## p = p.addOptional ("path", pwd(), @@ischar); # create optional argument ## ## ## one can create a function handle to anonymous functions for validators ## val_mat = @@(x)isvector(x) && all( x <= 1) && all(x >= 0); ## p = p.addOptional ("mat", [0 0], val_mat); ## ## ## create two ParamValue type of arguments ## val_type = @@(x) ischar(x) && any(strcmp(x, @{"linear", "quadratic"@}); ## p = p.addParamValue ("type", "linear", @@val_type); ## val_verb = @@(x) ischar(x) && any(strcmp(x, @{"low", "medium", "high"@}); ## p = p.addParamValue ("tolerance", "low", @@val_verb); ## ## ## create a switch type of argument ## p = p.addSwitch ("verbose"); ## ## p = p.parse (pack, path, mat, varargin@{:@}); ## ## ## the rest of the function can access the input by accessing p.Results ## ## for example, to access the value of tolerance, use p.Results.tolerance ## endfunction ## ## check ("mech"); # valid, will use defaults for other arguments ## check (); # error since at least one argument is mandatory ## check (1); # error since !ischar ## check ("mech", "~/dev"); # valid, will use defaults for other arguments ## ## check ("mech", "~/dev", [0 1 0 0], "type", "linear"); # valid ## ## ## the following is also valid. Note how the Switch type of argument can be ## ## mixed into or before the ParamValue (but still after Optional) ## check ("mech", "~/dev", [0 1 0 0], "verbose", "tolerance", "high"); ## ## ## the following returns an error since not all optional arguments, `path' and ## ## `mat', were given before the named argument `type'. ## check ("mech", "~/dev", "type", "linear"); ## @end group ## @end example ## ## @emph{Note 1}: a function can have any mixture of the four API types but they ## must appear in a specific order. @command{Required} arguments must be the very ## first which can be followed by @command{Optional} arguments. Only the ## @command{ParamValue} and @command{Switch} arguments can be mixed together but ## must be at the end. ## ## @emph{Note 2}: if both @command{Optional} and @command{ParamValue} arguments ## are mixed in a function API, once a string Optional argument fails to validate ## against, it will be considered the end of @command{Optional} arguments and the ## first key for a @command{ParamValue} and @command{Switch} arguments. ## ## @seealso{@@inputParser/addOptional, @@inputParser/addSwitch, ## @@inputParser/addParamValue, @@inputParser/addRequired, ## @@inputParser/createCopy, @@inputParser/parse} ## @end deftypefn function inPar = inputParser if (nargin != 0) print_usage; endif inPar = struct; ## these are not to be accessed by users. Each will have a field whose names ## are the argnames which will also be a struct with fieldnames 'validator' ## and 'default' inPar.ParamValue = struct; inPar.Optional = struct; inPar.Required = struct; inPar.Switch = struct; ## this will be filled when the methodd parse is used and will be a struct whose ## fieldnames are the argnames that return their value inPar.Results = struct; ## an 1xN cell array with argnames. It is read only by the user and its order ## showws the order that they were added to the object (which is the order they ## will be expected) inPar.Parameters = {}; inPar.CaseSensitive = false; inPar.FunctionName = ''; # name of the function for the error message inPar.KeepUnmatched = false; inPar.StructExpand = true; inPar.Unmatched = struct; inPar.UsingDefaults = {}; inPar = class (inPar, 'inputParser'); endfunction %!shared p, out %! p = inputParser; %! p = p.addRequired ("req1", @(x) ischar (x)); %! p = p.addOptional ("op1", "val", @(x) ischar (x) && any (strcmp (x, {"val", "foo"}))); %! p = p.addOptional ("op2", 78, @(x) (x) > 50); %! p = p.addSwitch ("verbose"); %! p = p.addParamValue ("line", "tree", @(x) ischar (x) && any (strcmp (x, {"tree", "circle"}))); %! ## check normal use, only required are given %! out = p.parse ("file"); %!assert ({out.Results.req1, out.Results.op1, out.Results.op2, out.Results.verbose, out.Results.line}, %! {"file" , "val" , 78 , false , "tree"}); %!assert (out.UsingDefaults, {"op1", "op2", "verbose", "line"}); %! ## check normal use, but give values different than defaults %! out = p.parse ("file", "foo", 80, "line", "circle", "verbose"); %!assert ({out.Results.req1, out.Results.op1, out.Results.op2, out.Results.verbose, out.Results.line}, %! {"file" , "foo" , 80 , true , "circle"}); %! ## check optional is skipped and considered ParamValue if unvalidated string %! out = p.parse ("file", "line", "circle"); %!assert ({out.Results.req1, out.Results.op1, out.Results.op2, out.Results.verbose, out.Results.line}, %! {"file" , "val" , 78 , false , "circle"}); %! ## check case insensitivity %! out = p.parse ("file", "foo", 80, "LiNE", "circle", "vERbOSe"); %!assert ({out.Results.req1, out.Results.op1, out.Results.op2, out.Results.verbose, out.Results.line}, %! {"file" , "foo" , 80 , true , "circle"}); %! ## check KeepUnmatched %! p.KeepUnmatched = true; %! out = p.parse ("file", "foo", 80, "line", "circle", "verbose", "extra", 50); %!assert (out.Unmatched.extra, 50) %! ## check error when missing required %!error(p.parse()) %! ## check error when given required do not validate %!error(p.parse(50)) %! ## check error when given optional do not validate %!error(p.parse("file", "no-val")) %! ## check error when given ParamValue do not validate %!error(p.parse("file", "foo", 51, "line", "round")) ## check alternative method (obj), ...) API %!shared p, out %! p = inputParser; %! p = addRequired (p, "req1", @(x) ischar (x)); %! p = addOptional (p, "op1", "val", @(x) ischar (x) && any (strcmp (x, {"val", "foo"}))); %! p = addOptional (p, "op2", 78, @(x) (x) > 50); %! p = addSwitch (p, "verbose"); %! p = addParamValue (p, "line", "tree", @(x) ischar (x) && any (strcmp (x, {"tree", "circle"}))); %! ## check normal use, only required are given %! out = parse (p, "file"); %!assert ({out.Results.req1, out.Results.op1, out.Results.op2, out.Results.verbose, out.Results.line}, %! {"file" , "val" , 78 , false , "tree"}); %!assert (out.UsingDefaults, {"op1", "op2", "verbose", "line"}); %! ## check normal use, but give values different than defaults %! out = parse (p, "file", "foo", 80, "line", "circle", "verbose"); %!assert ({out.Results.req1, out.Results.op1, out.Results.op2, out.Results.verbose, out.Results.line}, %! {"file" , "foo" , 80 , true , "circle"}); ## if we were matlab compatible... %!shared p, out %! p = inputParser; %! p = p.addOptional ("op1", "val"); %! p = p.addParamValue ("line", "tree"); %!xtest assert (getfield (p.parse("line", "circle"), "Results"), struct ("op1", "val", "line", "circle"));