www.gusucode.com > external 工具箱matlab源码程序 > external/interfaces/webservices/http/+matlab/+net/QueryParameter.m

    classdef (Sealed) QueryParameter
% QueryParameter A parameter in the query portion of a URI
%   This class assists in the creation of a URI query string of the form:
%     name1=value1&name2=value2&name3=value3
%   where each "name=value" segment is a QueryParameter, converted to a string
%   using the string method.  If you call the string method on a vector of
%   QueryParameter, the results are joined by the '&' character.  The string
%   method converts any values to strings and performs all necessary encoding
%   of special characters in the result.
%
%   The constructor lets you create a vector of QueryParameters given an
%   entire string as above, a list of names and values, or a structure.
%
%  QueryParameter properties:
%     Name     - name of the parameter
%     Value    - value of the parameter
%     Format   - format for encoding value if non-scalar or cell
%
%  QueryParameter methods:
%     QueryParameter - constructor
%     string, char   - convert to encoded string
%
%  See also char, string, URI, ArrayFormat

% Copyright 2015-2016 The Mathworks, Inc.
    
    properties
        % Name - the name of the parameter (string).
        Name string
        % Value - value of the parameter  
        %   This property may be a real number, logical, datetime (with value other
        %   than NaT), string, character array, or vector or cell vector of these.  If
        %   it is any other type, it is acceptable if it supports a string or char
        %   method that converts the value to a string, character vector, or cell
        %   array of character vectors.
        %
        %   When a URI containing a Query is converted to a string, or when you call
        %   the string method on this QueryParameter, a scalar Value will create a result
        %   such as "Name=Value".  If Value is "" or '' (an empty string), it will
        %   appear as "Name=".  If Value is an empty array (other than char or cell)
        %   it will appear as "Name".
        %
        %   If value is a cell array or non-scalar, it will be converted based on
        %   Format.
        % 
        %   You should not percent-encode strings you store here, because percent
        %   signs will be treated as literal symbols and will get improperly encoded
        %   when this QueryParameter is converted to a string.
        %
        % See also string, char, Format, matlab.net.URI.Query
        Value
        % Format - the format to use for encoding if Value is not a scalar or cell
        %   The value is an ArrayFormat enumeration.  Default is ArrayFormat.csv.
        %
        %   When converting a QueryParameter with a Value of {} to a string, the
        %   result will be:
        %     "Name=[]" or "Name=" for ArrayFormat.json or ArrayFormat.csv
        %     ""                   for ArrayFormat.php or ArrayFormat.repeating
        %
        %   If an individual value in a cell array is empty, is value will appear
        %   empty.  For example:
        %        >> string(QueryParameter('name',{1 [] 2},ArrayFormat.csv))
        %        ans =
        %        name=1,,2
        %        >> string(QueryParameter('name',{1 [] 2},ArrayFormat.repeating))
        %        ans = 
        %        name=1&name=&name=2
        %
        % See also Name, Value, ArrayFormat
        Format matlab.net.ArrayFormat = matlab.net.ArrayFormat.csv
    end
    
    methods
        function obj = set.Name(obj, name)
            obj.Name = matlab.net.internal.getString(name,mfilename,'Name');
        end
        
        function obj = set.Value(obj, value)
            % Allow anything: we'll error out when attempt is made to convert this to
            % a string.
            obj.Value = value;
         end
        
        function obj = QueryParameter(varargin)
        % QueryParameter Creates query parameters for use in a URI  
        %   QP = QueryParameter returns QP, an empty QueryParameter
        %
        %   QPs = QueryParameter(NAME1, VALUE1, ..., NAMEn, VALUEn) returns QPs, a
        %      vector of QueryParameters, one for each NAME,VALUE pair
        %           NAME    The name of the parameter (string)
        %           VALUE   The value of the parameter.  See description of the
        %                   Value property to determine data types permitted.
        %
        %   QPs = QueryParameter(STRUCT) returns QPs, a vector of QueryParameters,
        %      with NAME and VALUE equal to the fields of the structure STRUCT.
        %
        %   QPs = QueryParameter(___, FORMAT) specifies the FORMAT to be used for
        %      non-scalar VALUEs.  FORMAT is an ArrayFormat enumeration value.
        %      Default is matlab.net.ArrayFormat.csv.
        % 
        %   QPs = QueryParameter(QUERYSTR) parses the string QUERYSTR and returns a
        %      vector of QueryParameters representing the query.  The QUERYSTR is a
        %      completed, encoded query as it would appear in a URI, with leading '?'
        %      optional.  For example:
        %           '?foo=bar&one=2'
        %      The string will be split at '&' characters into individual
        %      QueryParameters, with the NAME and VALUE of each taken from the
        %      name=value pairs in the string.  
        %
        %      In QUERYSTR, it is assumed that triplets of characters consisting of a
        %      percent sign and two hex digits represent a percent-encoded byte, so
        %      sequences of these will be treated as UTF-8 encoded characters, which
        %      will be decoded to form the Name and Value properties of the
        %      QueryParameters.  Also, any '+' sign in QUERYSTR will be treated as if
        %      it was a space (as is '%20').  When the QueryParameter is converted
        %      back to a string, any required percent-encoding will be performed only
        %      on characters that should be encoded, and this will be done whether or
        %      not those characters were originally encoded in QUERYSTR, so the result
        %      from the string method may not exactly match QUERYSTR (although the
        %      meaning will be the same when used in a URI).
        %
        %      For example, the UTF-8 encoding for the euro sign (?) is the three hex
        %      bytes E2 82 AC, and the encoding for 'a' is 61.  
        %
        %         >> QueryParameter('foo=b %61%20r+%e2%82%ac')
        %         ans = 
        %            QueryParameter with properties:
        % 
        %                Name: "foo"
        %               Value: "b a r ?"
        %              Format: csv     
        %         >> string(ans)
        %         ans = 
        %         foo=b+a+r+%E2%82%AC
        %
        %     The above produces the same result as using these NAME,VALUE arguments:
        % 
        %        >> QueryParameter('foo','b a r ?')
        %
        % See also Name, Value, Format, string, char, ArrayFormat, URI
            if nargin > 0
                if nargin == 1 && isempty(varargin{1})
                    % Accept [] as only argument
                    obj = matlab.net.QueryParameter.empty;
                elseif nargin == 1 && (isstring(varargin{1}) || ischar(varargin{1}))
                    % One nonempty argument: expect a QUERYSTR
                    value = matlab.net.internal.getString(varargin{1}, mfilename, 'query string');
                    if value==''
                        obj = matlab.net.QueryParameter.empty;
                    else
                        % Parse the string
                        % skip leading '?', if any
                        if value.startsWith('?')
                            value = value.extractAfter(1);
                        end
                        % split value into name/value pairs of params, at = and &
                        % and get QueryParameter array
                        parsed = regexp(value, ...
                                        '([^=&]*)(=?)([^&]*)?(?:&|$)+', 'tokens');
                        % params is cell array of 3-element string vectors, containing
                        % [name, equals, value] where equals is "=" or "" depending on
                        % presence of an "=" sign.  Create cell array of name,value
                        % pairs, setting value to [] if there was no "=".
                        params{length(parsed)*2} = [];
                        for i = 1 : length(parsed)
                            paramx = 2*i;
                            params{paramx-1} = matlab.net.internal.urldecode(parsed{i}(1),true);
                            if parsed{i}(2)==''
                                params{paramx} = [];
                            else
                                params{paramx} = matlab.net.internal.urldecode(parsed{i}(3),true);
                            end
                        end
                        obj = matlab.net.QueryParameter(params{:});
                    end
                else
                    % More than one argument or arg isn't a string, expect a struct or NAME,VALUE
                    % pairs. 
                    %If last argument is ArrayFormat, save and strip it.
                    if isa(varargin{end},'matlab.net.ArrayFormat')
                        format = varargin{end};
                        varargin(end) = [];
                    else
                        format = matlab.net.ArrayFormat.csv;
                    end
                    if length(varargin) == 1 && isstruct(varargin{1})
                        % if first and only arg is a struct, convert to NAME,VALUE arglist
                        struc = varargin{1};
                        validateattributes(struc, {'struct'}, {'scalar'}, mfilename);
                        fn = fieldnames(struc);
                        len = 2*length(fn);
                        args(2:2:len) = struct2cell(struc);
                        args(1:2:len) = fn;
                    else
                        args = varargin;
                    end
                    % Assign back to front
                    for i = length(args) + mod(length(args),2) : -2 : 2
                        i2 = i/2;
                        % Assign Name first, as this will error out if name isn't stringifiable
                        obj(i2).Name = args{i-1};
                        if i > length(args)
                            % No value for last Name (odd number of arguments)
                            error(message('MATLAB:http:MissingValueForParam', char(obj(i2).Name)));
                        else
                            obj(i2).Value = args{i};
                        end
                        obj(i2).Format = format;
                    end
                end
            end
        end
        
        function res = string(obj)
        % string Return the QueryParameter as an encoded string
        %   STR = string(OBJ) converts the QueryParameter to a string of the form
        %   "name=value" where the name is the Name property and the value is the
        %   stringified Value property.  If the Value is a cell array or non-scalar
        %   other than a character vector, it will be converted based on the Format
        %   property, which may result in multiple name=value pairs separated by the
        %   "&" character and including other punctuation.  In addition, any special
        %   characters in Name or Value not permitted in a query are percent-encoded,
        %   except that a space is encoded as '+' (see the QUERYSTR argument to the
        %   QueryParameter constructor for more information on encoding).
        %
        %   If OBJ is a vector of QueryParameter, returns a single string joining the
        %   encoded members with '&'.
        %
        %   If OBJ is empty, returns "".
        %
        % See also Name, Value, Format, ArrayFormat, matlab.net.QueryParameter.QueryParameter
            if isempty(obj)
                res = string('');
                return;
            end
                
            validateattributes(obj,{'matlab.net.QueryParameter'},{'vector'},mfilename);
            
            if isscalar(obj)
                % handle the scalar obj case.
                name = queryEncode(obj.Name);
                % value may be any type; need to convert it to string before encoding
                value = obj.Value;
                if isempty(value) && ~ischar(value) && ~iscell(value)
                    % This is the case where Value is any empty array other than ''
                    % or {}.  It gets just the Name with no '=' sign.  This will
                    % not error out even if value is not a type we support (e.g., an
                    % empty struct array or table with no values).
                    res = name;
                else
                    % Get vector of strings by converting value array to strings.
                    % This may return a string scalar or string vector, URL-encoded.
                    values = convert(obj.Name, value);
                    if isscalar(values) && ~iscell(value)
                        % if we have a scalar not in a cell, we're done
                        res = name + '=' + values;
                    else
                        % Non-scalar values or any value in cell require use of
                        % ArrayFormat.  If value was {}, we'll get here with values =
                        % string.empty.
                        import matlab.net.ArrayFormat
                        % Add appropriate punctuation for arrays.  The characters =&, are in
                        % sub-delims which are allowed in pchar, but [] are not, so they need to be
                        % encoded.
                        switch obj.Format
                            case ArrayFormat.repeating
                                % name=v1&name=v2&name=v3...  or "" if string.empty
                                res = strjoin(name + '=' + values, '&');
                            case ArrayFormat.csv
                                % name=v1,v2,v3... or "name=" if string.empty
                                res = name + '=' + strjoin(values, ',');
                            case ArrayFormat.json
                                % name=[v1,v2,v3...] or "name=[]" if string.empty
                                res = name + '=%5B' + strjoin(values, ',') +'%5D';
                            case ArrayFormat.php
                                % name[]=v1&name[]=v2&name[]=v3... or "" if string.empty
                                res = strjoin(name + '%5B%5D=' + values, '&');
                        end
                    end
                end
            else
                res = arrayfun(@string, obj, 'UniformOutput', false);
                res = strjoin([res{:}], '&');
            end
        end
        
        function str = char(obj)
        % char Return the encoded QueryParameter as a character vector
        %   For more information, see the string method.
        %
        % See also string
            str = char(string(obj));
        end
    end
end

function strings = convert(name, values)
% Convert the array of values to a vector of URL-encoded strings.  Values can be
% numerous types, including cell arrays.  Any empty members of values (e.g., if it's
% a cell array with empty elements) become "".  However, if values is a char array,
% each row is treated as one element.
%
% If values is [], returns "".  If values is {}, returns string.empty.  The input
% value of [] would not occur on the call from QueryParameter, but could on the
% recursive call below when a member of a cell array is [].
%
% We try to be liberal about what we accept as values, attempting to convert numbers
% to strings and even invoking the string or char function in case those are
% implemented for the type of values.  The length of strings is not necessarily equal
% to the length of values if calling string or char on the entire values array
% returns more or fewer strings than the length of values.  

    if isempty(values) 
        if isnumeric(values)
            strings = string('');
            return
        elseif iscell(values)
            strings = string.empty;
            return
        end
    end
    if isa(values, 'datetime') 
        % datetime gets special processing
        if any(isnat(values))
            error(message('MATLAB:http:NaTNotAllowed',char(name)));
        end
        if isnan(tzoffset(values))
            % Make sure datetimes have time zones, just in case they have a
            % Format that requires it.  Otherwise we'd get *** in the output
            % which would surely cause problems with servers.  Note the TimeZone
            % property applies to the whole array, not just to individual
            % members.
            values.TimeZone = 'local';
        end
        strings = string(char(values)); % TBD remove char when datetime supports string
    else
        try
            % First, just try to convert the whole ball of wax using the string function.
            % This will do the right thing for string, real numeric, logical and char
            % arrays, cellstrs and numeric arrays. 

            % TBD For numbers or logicals, it's not compatible with webread/webwrite, the
            % old urlencode behavior. To get existing tests to pass, we need to use
            % num2str instead.  Not clear whether to preserve this legacy behavior or
            % not.
            if isnumeric(values) || islogical(values)
                values = arrayfun(@num2str, values, 'UniformOutput', false);
            end
            if iscell(values) && any(cellfun(@isempty, values))
                throw(MException('a:b','c')); % go to catch block if any empty cells
            end
            strings = string(values);
        catch 
            % Conversion failed; see if char works.  
            try
                strings = string(char(values));
            catch e
                if string(e.identifier).startsWith('MATLAB:http')
                    % let HTTP errors (those created by classes in the http packages)
                    % get thrown normally, as this is the error the user would
                    % expect.
                    throw(e);
                end

                % Neither string nor char works.  It must be a cell array other than
                % cellstr; or a non-scalar for a type on which string/char only works
                % for scalars, or a type for which nothing works.
                if iscell(values) || ~isscalar(values)
                    % convert cell members or non-scalar members individually
                    if iscell(values)
                        fun = @cellfun;
                    else
                        fun = @arrayfun;
                    end
                    % Convert each member to string vector.  Note members could
                    % themselves be arrays, cell arrays, or empty.
                    res = fun(@(v)convert(name,v), values, 'UniformOutput', false);
                    % If we get [], it means the value couldn't be converted to a
                    % string array or values was actually empty in the first place.
                    if isempty(res)
                        error(message('MATLAB:http:CannotConvertQueryToString', ...
                                  char(name), class(values)));
                    end
                    % concatenate results into one vector; allows cells to contain
                    % different length string vectors
                    strings = [res{:}];
                    % above convert call has already encoded each value, so done
                    return;
                elseif isnumeric(values) && ~isreal(values)
                    % complex scalar needs special conversion
                    strings = string(num2str(values));
                else
                    % if a scalar and we couldn't convert it using string or char, 
                    % unconvertible
                    error(message('MATLAB:http:CannotConvertQueryToString', ...
                                  char(name), class(values)));
                end
            end
        end
    end
    if ~isstring(strings) 
        % this case means that the call to string above returned something other than
        % string array -- unlikely but possible
        error(message('MATLAB:http:stringNotReturnString', char(name), ...
                      class(values), class(strings)));
    end
    % whatever we end up with, flatten to row vector and encode
    strings = string(queryEncode(strings(:)'));
end
    
function res = queryEncode(str)
% Encode a name or value in the query.  This is intended to encode the name or
% value so that it is preserved as the user entered it.  Encodes anything not
% a pchar or /?, as per RFC 3986.
%    query  = *( pchar / "/" / "?" )
% but also characters that mean something special in the query: & = +
% (It's not clear if servers treat an encoded &, = or + different from an
% unencoded one, but encoding them can't hurt.)
%
% This is a minimalist encoding: we don't encode some characters that other
% encoders like java.net.URLEncoder and Javascript's encodeURIComponent do,
% because those encoders are designed to encode any component of a URI, not just
% query names and values.  Here we try to encode only what's really necessary
% according to the RFC.
    persistent noEncodeChars
    if isempty(noEncodeChars)
        % characters not to encode start out with pchar
        % remove &=+ from pchar and add /? that query allows
        noEncodeChars = [regexprep(matlab.net.URI.Pchar, '[&=+]', '') '/?'];
    end
    res = matlab.net.internal.urlencode(str, noEncodeChars, true);
end