function varargout = validateSyntax(fcn, inputs, varargin)
% Validate syntax of a distributed method by calling the in-memory equivalent.
%
% This function should ONLY be used for validating correctness of
% non-distributed input arguments. It will not validate the data values and
% should not be used if validation depends on the data. If validation depends on
% the size of actual data, you must use the DefaultSize property with an
% appropriate size.
%
% Syntax:
%
%  distributedutil.validateSyntax(FCN,INPUTS) converts any
%    distributed in cell array INPUTS to a fabricated sample of height 1 and
%    then invokes FCN on INPUTS with a single output argument.
%
%  distributedutil.validateSyntax(..'DefaultSize',SIZE) provides a default size
%    for distributed inputs. If unspecified, the default size is [1,1].
%
%  distributedutil.validateSyntax(..'NumOutputs',NUMOUTPUTS) specifies the
%    number of outputs to invoke the function with. If unspecified, the default
%    number of outputs is 1 or the number of captured outputs.
%
% This invokes the given function handle with all distributed input arguments
% replaced by fabricated samples. These samples are not guaranteed to be
% accurate:
%  1. The values will be not be accurate. They will be all ones.
%  2. The size will not always be accurate. If size is not known, a default
%     is used.
%
% Example of use:
%
%  function out = sort(tX, varargin)
%
%  distributedutil.validateSyntax(@sort, [{tX}, varargin]);
%
%  % We can now assume all parameters are correct.
%  if nargin == 1 || nargin >= 2 && ~isnumeric(varargin{1})
%      error(message('MATLAB:bigdata:array:SortDimRequired'));
%  end
%  dim = varargin{1};
%
%  %...
%

% Copyright 2018 The MathWorks, Inc.

assert(isa(fcn, 'function_handle'), ...
    'Assertion failed: validateSyntax expects FCN to be a function handle.');
assert(iscell(inputs), ...
    'Assertion failed: validateSyntax expects INPUTS to be a cell array of inputs.');

[defaultType, defaultSize, numOutputs] = iParseOtherInputs(varargin{:});

for ii = 1 : numel(inputs)
    if isdistributed(inputs{ii}) || iscodistributed(inputs{ii})
        adaptor = distributedutil.getAdaptor(inputs{ii});
        inputs{ii} = buildSample(adaptor, defaultType, defaultSize);
    end
end

varargout = cell(1, numOutputs);
try
    [varargout{:}] = fcn(inputs{:});
catch err
    throwAsCaller(err);
end

function [defaultType, defaultSize, numOutputs] = iParseOtherInputs(varargin)
% Parse the name-value pairs supported by validateSyntax.
p = inputParser;
p.addParameter('DefaultType', '');
p.addParameter('DefaultSize', [1, 1]);
p.addParameter('NumOutputs', 1);
p.parse(varargin{:});

defaultType = p.Results.DefaultType;
assert(matlab.internal.datatypes.isScalarText(defaultType), ...
    'Assertion Failed: validateSyntax expects DefaultType to be a string or character row vector.');
defaultType = char(defaultType);

defaultSize = p.Results.DefaultSize;
assert(isnumeric(defaultSize) && isrow(defaultSize), ...
    'Assertion Failed: validateSyntax expects DefaultSize to be a numeric row vector.');
defaultSize = double(defaultSize);

numOutputs = p.Results.NumOutputs;
assert(isnumeric(numOutputs) && isscalar(numOutputs) && mod(numOutputs, 1) == 0 && numOutputs >= 0, ...
    'Assertion Failed: validateSyntax expects NumOutputs to be a non-negative scalar integer.');
numOutputs = double(numOutputs);
