001    package com.thetransactioncompany.jsonrpc2.util;
002    
003    
004    import java.util.HashMap;
005    import java.util.LinkedList;
006    import java.util.List;
007    import java.util.Map;
008    import java.util.Set;
009    
010    import com.thetransactioncompany.jsonrpc2.JSONRPC2Error;
011    
012    
013    /**
014     * Utility class for retrieving JSON-RPC 2.0 named parameters (key-value pairs
015     * packed into a JSON Object). 
016     *
017     * <p>Provides a set of getter methods according to the expected parameter type
018     * (number, string, etc.) and whether the parameter is mandatory or optional:
019     *
020     * <ul>
021     *     <li>{@code getXXX(param_name)} for mandatory parameters, where 
022     *         {@code XXX} is the expected parameter type.
023     *     <li>{@code getOptXXX(param_name, default_value)} for optional 
024     *         parameters, specifying a default value.
025     * </ul>
026     * 
027     * <p>There are also generic getter methods that let you do the type conversion 
028     * yourself.
029     *
030     * <p>If a parameter cannot be retrieved, e.g. due to a missing mandatory 
031     * parameter or bad type, a 
032     * {@link com.thetransactioncompany.jsonrpc2.JSONRPC2Error#INVALID_PARAMS}
033     * exception is thrown.
034     *
035     * <p>Example: suppose you have a method with 3 named parameters "name", "age"
036     * and "sex", where the last is optional and defaults to "female":
037     *
038     * <pre>
039     * // Parse received request string
040     * JSONRPC2Request request = null;
041     *
042     * try {
043     *         request = JSONRPC2Request.parse(jsonString);
044     * } catch (JSONRPC2ParseException e) {
045     *         // handle exception...
046     * }
047     *
048     * // Create a new retriever for named parameters
049     * Map params = (Map)request.getParams();
050     * NamedParamsRetriever r = new NamedParamsRetriever(params);
051     *
052     * try {
053     *         // Extract "name" string parameter
054     *         String name = r.getString("name");
055     *
056     *         // Extract "age" integer parameter
057     *         int age = r.getInt("age");
058     *
059     *         // Extract optional "sex" string parameter which defaults to "female"
060     *         String sex = r.getOptString("sex", "female");
061     *
062     * } catch (JSONRPC2Error e) {
063     *         // A JSONRPC2Error.INVALID_PARAMS will be thrown to indicate
064     *         // an unexpected parameter type or a missing mandatory parameter.
065     *         // You can use it straight away to create the appropriate
066     *         // JSON-RPC 2.0 error response.
067     *         JSONRPC2Response response = new JSONRPC2Response(e, null);
068     * }
069     * 
070     * </pre>
071     *
072     * @author Vladimir Dzhuvinov
073     * @version 1.34.3 (2012-12-22)
074     */
075    public class NamedParamsRetriever 
076            extends ParamsRetriever {
077    
078            
079            /** 
080             * The named parameters interface. 
081             */
082            private Map<String,Object> params = null;
083    
084    
085            /**
086             * Throws a JSON-RPC 2.0 error indicating one or more missing named
087             * parameters.
088             *
089             * @param names The parameter names. Must not be {@code null}.
090             *
091             * @throws JSONRPC2Error Formatted JSON-RPC 2.0 error.
092             */
093            private static void throwMissingParameterException(final String... names)
094                    throws JSONRPC2Error {
095    
096                    if (names.length == 1)
097                            throw JSONRPC2Error.INVALID_PARAMS.
098                                    appendMessage(": Missing \"" + names[0] + "\" parameter");
099    
100                    // Compose list of missing parameters
101                    StringBuilder list = new StringBuilder();
102    
103                    for (int i=0; i < names.length; i++) {
104    
105                            if (list.length() > 0)
106                                    list.append(',');
107    
108                            list.append('"');
109                            list.append(names[i]);
110                            list.append('"');
111                    }
112    
113                    throw JSONRPC2Error.INVALID_PARAMS.
114                            appendMessage(": Missing " + list.toString() + " parameters");
115            }
116    
117    
118            /**
119             * Throws a JSON-RPC 2.0 error indicating a named parameter with an
120             * unexpected {@code null} value.
121             *
122             * @param name The parameter name. Must not be {@code null}.
123             *
124             * @throws JSONRPC2Error Formatted JSON-RPC 2.0 error.
125             */
126            private static void throwNullParameterException(final String name)
127                    throws JSONRPC2Error {
128    
129                    throw JSONRPC2Error.INVALID_PARAMS.
130                            appendMessage(": Parameter \"" + name + "\" must not be null");
131            }
132    
133    
134            /**
135             * Throws a JSON-RPC 2.0 error indicating a named parameter with an
136             * unexpected enumerated value.
137             *
138             * @param name        The parameter name.
139             * @param enumStrings The acceptable string values. Must not be 
140             *                    {@code null}.
141             *
142             * @throws JSONRPC2Error Formatted JSON-RPC 2.0 error.
143             */
144            private static void throwEnumParameterException(final String name, 
145                                                            final String[] enumStrings)
146                    throws JSONRPC2Error {
147    
148                    StringBuilder msg = new StringBuilder(": Enumerated parameter \"" + name + "\" must have values ");
149    
150                    for (int i=0; i < enumStrings.length; i++) {
151    
152                            if (i > 0 && i == enumStrings.length - 1)
153                                    msg.append(" or ");
154    
155                            else if (i > 0)
156                                    msg.append(", ");
157    
158                            msg.append('"');
159                            msg.append(enumStrings[i]);
160                            msg.append('"');
161                    }
162    
163                    throw JSONRPC2Error.INVALID_PARAMS.appendMessage(msg.toString());
164            }
165    
166    
167            /**
168             * Throws a JSON-RPC 2.0 error indicating a named parameter with an
169             * unexpected enumerated value.
170             *
171             * @param name      The parameter name.
172             * @param enumClass The enumeration class specifying the acceptable 
173             *                  string values. Must not be {@code null}.
174             *
175             * @throws JSONRPC2Error Formatted JSON-RPC 2.0 error.
176             */
177            private static <T extends Enum<T>> void throwEnumParameterException(final String name, 
178                                                                                final Class<T> enumClass)
179                    throws JSONRPC2Error {
180    
181                    StringBuilder msg = new StringBuilder(": Enumerated parameter \"" + name + "\" must have values ");
182    
183                    T[] constants = enumClass.getEnumConstants();
184    
185                    for (int i = 0; i < constants.length; i++) {
186                    
187                            if (i > 0 && i == constants.length - 1)
188                                    msg.append(" or ");
189    
190                            else if (i > 0)
191                                    msg.append(", ");
192    
193                            msg.append('"');
194                            msg.append(constants[i].toString());
195                            msg.append('"');
196                    }
197    
198                    throw JSONRPC2Error.INVALID_PARAMS.appendMessage(msg.toString());
199            }
200    
201    
202            /**
203             * Creates a JSON-RPC 2.0 error indicating a named parameter with an
204             * unexpected JSON type.
205             *
206             * @param name The parameter name. Must not be {@code null}.
207             *
208             * @return Formatted JSON-RPC 2.0 error.
209             */
210            private static JSONRPC2Error newUnexpectedParameterTypeException(final String name) {
211    
212                    return JSONRPC2Error.INVALID_PARAMS.
213                            appendMessage(": Parameter \"" + name + "\" has an unexpected JSON type");
214            }
215    
216    
217            /**
218             * Creates a JSON-RPC 2.0 error indicating an array exception.
219             *
220             * @param name The parameter name. Must not be {@code null}.
221             *
222             * @return Formatted JSON-RPC 2.0 error.
223             */
224            private static JSONRPC2Error newArrayException(final String name) {
225    
226                    return JSONRPC2Error.INVALID_PARAMS.
227                            appendMessage(": Parameter \"" + name + "\" caused an array exception");
228            }
229    
230    
231            /** 
232             * Creates a new named parameters retriever from the specified 
233             * key-value map.
234             *
235             * @param params The named parameters map. Must not be {@code null}.
236             */
237            public NamedParamsRetriever(final Map<String,Object> params) {
238            
239                    if (params == null)
240                            throw new IllegalArgumentException("The parameters map must not be null");
241    
242                    this.params = params;
243            }
244    
245    
246            /**
247             * Gets the named parameters for this retriever.
248             *
249             * @return The named parameters.
250             */
251            public Map<String,Object> getParams() {
252    
253                    return params;
254            }
255            
256            
257            @Override
258            public int size() {
259            
260                    return params.size();
261            }
262            
263            
264            /** 
265             * Returns {@code true} if a parameter by the specified name exists, 
266             * else {@code false}.
267             *
268             * @param name The parameter name.
269             *
270             * @return {@code true} if the parameter exists, else {@code false}.
271             */
272            public boolean hasParam(final String name) {
273                    
274                    if (params.containsKey(name))
275                            return true;
276                    else
277                            return false;
278            }
279    
280    
281            /**
282             * @see #hasParam
283             */
284            @Deprecated
285            public boolean hasParameter(final String name) {
286    
287                    return hasParam(name);
288            }
289            
290            
291            /**
292             * Returns {@code true} if the parameters by the specified names exist,
293             * else {@code false}.
294             *
295             * @param names The parameter names. Must not be {@code null}.
296             *
297             * @return {@code true} if the parameters exist, else {@code false}.
298             */
299            public boolean hasParams(final String[] names) {
300            
301                    return hasParams(names, null);
302            }
303    
304    
305            /**
306             * @see #hasParams(String[])
307             */
308            @Deprecated
309            public boolean hasParameters(final String[] names) {
310    
311                    return hasParams(names);
312            }
313            
314            
315            /**
316             * Returns {@code true} if the parameters by the specified mandatory
317             * names exist, {@code false} if any mandatory name is missing or a 
318             * name outside the mandatory and optional is present.
319             *
320             * @param mandatoryNames The expected mandatory parameter names. Must
321             *                       not be {@code null}.
322             * @param optionalNames  The expected optional parameter names,
323             *                       empty array or {@code null} if none.
324             *
325             * @return {@code true} if the specified mandatory names and only any 
326             *         of the optional are present, else {@code false}.
327             */
328            public boolean hasParams(final String[] mandatoryNames, 
329                                     final String[] optionalNames) {
330            
331                    // Do shallow copy of params
332                    Map paramsCopy = (Map)((HashMap)params).clone();
333            
334                    // Pop the mandatory names
335                    for (String name: mandatoryNames) {
336                            
337                            if (paramsCopy.containsKey(name))
338                                    paramsCopy.remove(name);
339                            else
340                                    return false;
341                    }
342                    
343                    // Pop the optional names (if any specified)
344                    if (optionalNames != null) {
345                    
346                            for (String name: optionalNames) {
347    
348                                    if (paramsCopy.containsKey(name))
349                                            paramsCopy.remove(name);
350                            }
351                    }
352                    
353                    // Any remaining keys that shouldn't be there?
354                    int remainingKeys = paramsCopy.size();
355                    
356                    if (remainingKeys == 0)
357                            return true;
358                    else
359                            return false;
360            }
361    
362    
363            /**
364             * @see #hasParams(String[], String[])
365             */
366            @Deprecated
367            public boolean hasParameters(final String[] mandatoryNames, 
368                                         final String[] optionalNames) {
369    
370                    return hasParams(mandatoryNames, optionalNames);
371            }
372            
373            
374            /**
375             * Returns the names of all available parameters.
376             *
377             * @return The parameter names.
378             */
379            public String[] getNames() {
380            
381                    Set<String> keyset = params.keySet();
382                    
383                    return keyset.toArray(new String[]{});
384            }
385            
386            
387            /**
388             * Throws a {@code JSONRPC2Error.INVALID_PARAMS} if the specified
389             * names aren't present in the parameters, or names outside the
390             * specified are contained.
391             *
392             * <p>You may use this method to a fire a proper JSON-RPC 2.0 error
393             * on a missing or unexpected mandatory parameter name.
394             *
395             * @param mandatoryNames The expected parameter names. Must not be
396             *                       {@code null}.
397             *
398             * @throws JSONRPC2Error On a missing parameter name or names outside
399             *                       the specified 
400             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
401             */
402            public void ensureParams(final String[] mandatoryNames)
403                    throws JSONRPC2Error {
404            
405                    ensureParameters(mandatoryNames, null);
406            }
407    
408    
409            /**
410             * @see #ensureParams(String[])
411             */
412            @Deprecated
413            public void ensureParameters(final String[] mandatoryNames)
414                    throws JSONRPC2Error {
415    
416                    ensureParams(mandatoryNames);
417            }
418            
419            
420            /**
421             * Throws a {@code JSONRPC2Error.INVALID_PARAMS} if the specified
422             * mandatory names aren't contained in the parameters, or names outside 
423             * the specified mandatory and optional are present.
424             *
425             * <p>You may use this method to a fire a proper JSON-RPC 2.0 error
426             * on a missing or unexpected mandatory parameter name.
427             *
428             * @param mandatoryNames The expected mandatory parameter names. Must
429             *                       not be {@code null}.
430             * @param optionalNames  The expected optional parameter names,
431             *                       empty array or {@code null} if none.
432             *
433             * @throws JSONRPC2Error On a missing mandatory parameter name or names 
434             *                       outside the specified mandatory and optional
435             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
436             */
437            public void ensureParams(final String[] mandatoryNames, 
438                                     final String[] optionalNames)
439                    throws JSONRPC2Error {
440            
441                    if (! hasParameters(mandatoryNames, optionalNames))
442                            throwMissingParameterException(mandatoryNames);
443            }
444    
445    
446            /**
447             * @see #ensureParams(String[], String[])
448             */
449            @Deprecated
450            public void ensureParameters(final String[] mandatoryNames, 
451                                         final String[] optionalNames)
452                    throws JSONRPC2Error {
453    
454                    ensureParams(mandatoryNames, optionalNames);
455            }
456            
457            
458            /**
459             * Throws a {@code JSONRPC2Error.INVALID_PARAMS} exception if there is
460             * no parameter by the specified name.
461             *
462             * <p>You may use this method to fire the proper JSON-RPC 2.0 error
463             * on a missing mandatory parameter.
464             *
465             * @param name The parameter name.
466             *
467             * @throws JSONRPC2Error On a missing parameter
468             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
469             */
470            public void ensureParam(final String name)
471                    throws JSONRPC2Error {
472                    
473                    if (! hasParameter(name))
474                            throwMissingParameterException(name);
475            }
476    
477    
478            /**
479             * @see #ensureParam(String)
480             */
481            @Deprecated
482            public void ensureParameter(final String name)
483                    throws JSONRPC2Error {
484    
485                    ensureParam(name);
486            }
487            
488            
489            /**
490             * Throws a {@code JSONRPC2Error.INVALID_PARAMS} exception if there is
491             * no parameter by the specified name, its value is {@code null}, or 
492             * its type doesn't map to the specified.
493             * 
494             * <p>You may use this method to fire the proper JSON-RPC 2.0 error
495             * on a missing or badly-typed mandatory parameter.
496             *
497             * @param name  The parameter name.
498             * @param clazz The corresponding Java class that the parameter should 
499             *              map to (any one of the return types of the 
500             *              {@code getXXX()} getter methods. Set to 
501             *              {@code Object.class} to allow any type. Must not be
502             *              {@code null}.
503             *
504             * @throws JSONRPC2Error On a missing parameter, {@code null} value or 
505             *                       bad type ({@link JSONRPC2Error#INVALID_PARAMS}).
506             */
507            public <T> void ensureParam(final String name, final Class<T> clazz)
508                    throws JSONRPC2Error {
509                    
510                    ensureParameter(name, clazz, false);
511            }
512    
513    
514            /**
515             * @see #ensureParam(String, Class)
516             */
517            @Deprecated
518            public <T> void ensureParameter(final String name, final Class<T> clazz)
519                    throws JSONRPC2Error {
520    
521                    ensureParam(name, clazz);
522            }
523            
524            
525            /**
526             * Throws a {@code JSONRPC2Error.INVALID_PARAMS} exception if there is
527             * no parameter by the specified name or its type doesn't map to the 
528             * specified.
529             * 
530             * <p>You may use this method to fire the proper JSON-RPC 2.0 error
531             * on a missing or badly-typed mandatory parameter.
532             *
533             * @param name      The parameter name.
534             * @param clazz     The corresponding Java class that the parameter 
535             *                  should map to (any one of the return types of the 
536             *                  {@code getXXX()} getter methods. Set to 
537             *                  {@code Object.class} to allow any type. Must not be
538             *                  {@code null}.
539             * @param allowNull If {@code true} allows a {@code null} parameter
540             *                  value.
541             *
542             * @throws JSONRPC2Error On a missing parameter or bad type 
543             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
544             */
545            public <T> void ensureParam(final String name, 
546                                        final Class<T> clazz, 
547                                        final boolean allowNull)
548                    throws JSONRPC2Error {
549                    
550                    // First, check existence only
551                    ensureParameter(name);
552                    
553                    // Now check type
554                    Object value = params.get(name);
555                    
556                    if (value == null) {
557                            
558                            if (allowNull)
559                                    return; // ok
560                    
561                            else
562                                    throwNullParameterException(name);
563                    }
564                    
565                    if (! clazz.isAssignableFrom(value.getClass()))
566                            throw newUnexpectedParameterTypeException(name);
567            }
568    
569    
570            /**
571             * @see #ensureParam(String, Class, boolean)
572             */
573            @Deprecated
574            public <T> void ensureParameter(final String name, 
575                                            final Class<T> clazz, 
576                                            final boolean allowNull)
577                    throws JSONRPC2Error {
578    
579                    ensureParam(name, clazz, allowNull);
580            }
581            
582            
583            /**
584             * Retrieves the specified parameter which can be of any type. Use this
585             * generic getter if you want to cast the value yourself. Otherwise 
586             * look at the typed {@code get*} methods.
587             *
588             * @param name The parameter name.
589             *
590             * @return The parameter value.
591             *
592             * @throws JSONRPC2Error On a missing parameter
593             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
594             */
595            public Object get(final String name)
596                    throws JSONRPC2Error {
597            
598                    ensureParameter(name);
599            
600                    return params.get(name);
601            }
602            
603            
604            /**
605             * Retrieves the specified parameter which must map to the provided
606             * class (use the appropriate wrapper class for primitive types).
607             *
608             * @param name  The parameter name.
609             * @param clazz The corresponding Java class that the parameter should 
610             *              map to (any one of the return types of the 
611             *              {@code getXXX()}  getter methods. Set to 
612             *              {@code Object.class} to allow any type. Must not be
613             *              {@code null}.
614             *
615             * @return The parameter value.
616             *
617             * @throws JSONRPC2Error On a missing parameter, {@code null} value or 
618             *                       bad type ({@link JSONRPC2Error#INVALID_PARAMS}).
619             */
620            public <T> T get(final String name, final Class<T> clazz)
621                    throws JSONRPC2Error {
622            
623                    return get(name, clazz, false);
624            }
625            
626            
627            /**
628             * Retrieves the specified parameter which must map to the provided
629             * class (use the appropriate wrapper class for primitive types).
630             *
631             * @param name      The parameter name.
632             * @param clazz     The corresponding Java class that the parameter 
633             *                  should map to (any one of the return types of the 
634             *                  {@code getXXX()} getter methods. Set to 
635             *                  {@code Object.class} to allow any type. Must not be
636             *                  {@code null}.
637             * @param allowNull If {@code true} allows a {@code null} parameter 
638             *                  value.
639             *
640             * @return The parameter value.
641             *
642             * @throws JSONRPC2Error On a missing parameter or bad type 
643             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
644             */
645            @SuppressWarnings("unchecked")
646            public <T> T get(final String name, final Class<T> clazz, final boolean allowNull)
647                    throws JSONRPC2Error {
648            
649                    ensureParameter(name, clazz, allowNull);
650                    
651                    try {
652                            return (T)params.get(name);
653                            
654                    } catch (ClassCastException e) {
655                            
656                            throw newUnexpectedParameterTypeException(name);
657                    }
658            }
659            
660            
661            /**
662             * Retrieves the specified optional parameter which must map to the
663             * provided class (use the appropriate wrapper class for primitive 
664             * types). If the parameter doesn't exist the method returns the 
665             * specified default value.
666             *
667             * @param name         The parameter name.
668             * @param clazz        The corresponding Java class that the parameter 
669             *                     should map to (any one of the return types of 
670             *                     the {@code getXXX()} getter methods. Set to 
671             *                     {@code Object.class} to allow any type. Must not
672             *                     be {@code null}.
673             * @param defaultValue The default return value if the parameter
674             *                     doesn't exist. May be {@code null}.
675             *
676             * @return The parameter value.
677             *
678             * @throws JSONRPC2Error On a bad parameter type or {@code null} value
679             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
680             */
681            public <T> T getOpt(final String name, final Class<T> clazz, final T defaultValue)
682                    throws JSONRPC2Error {
683            
684                    return getOpt(name, clazz, false, defaultValue);
685            }
686            
687            
688            /**
689             * Retrieves the specified optional parameter which must map to the
690             * provided class (use the appropriate wrapper class for primitive 
691             * types). If the parameter doesn't exist the method returns the 
692             * specified default value.
693             *
694             * @param name         The parameter name.
695             * @param clazz        The corresponding Java class that the parameter 
696             *                     should map to (any one of the return types of 
697             *                     the {@code getXXX()} getter methods. Set to 
698             *                     {@code Object.class} to allow any type. Must not
699             *                     be {@code null}.
700             * @param allowNull    If {@code true} allows a {@code null} parameter
701             *                     value.
702             * @param defaultValue The default return value if the parameter
703             *                     doesn't exist. May be {@code null}.
704             *
705             * @return The parameter value.
706             *
707             * @throws JSONRPC2Error On a bad parameter type
708             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
709             */
710            @SuppressWarnings("unchecked")
711            public <T> T getOpt(final String name, 
712                                final Class<T> clazz, 
713                                final boolean allowNull, 
714                                final T defaultValue)
715                    throws JSONRPC2Error {
716            
717                    if (! hasParameter(name))
718                            return defaultValue;
719                    
720                    ensureParameter(name, clazz, allowNull);
721                    
722                    try {
723                            return (T)params.get(name);
724                            
725                    } catch (ClassCastException e) {
726                            
727                            throw newUnexpectedParameterTypeException(name);
728                    }
729            }
730            
731            
732            /**
733             * Retrieves the specified string parameter.
734             *
735             * @param name The parameter name.
736             *
737             * @return The parameter value as a string.
738             *
739             * @throws JSONRPC2Error On a missing parameter, bad type or 
740             *                       {@code null} value 
741             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
742             */
743            public String getString(final String name)
744                    throws JSONRPC2Error {
745                    
746                    return getString(name, false);
747            }
748            
749            
750            /**
751             * Retrieves the specified string parameter.
752             *
753             * @param name      The parameter name.
754             * @param allowNull If {@code true} allows a {@code null} value.
755             *
756             * @return The parameter value as a string.
757             *
758             * @throws JSONRPC2Error On a missing parameter or bad type
759             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
760             */
761            public String getString(final String name, final boolean allowNull)
762                    throws JSONRPC2Error {
763                    
764                    return get(name, String.class, allowNull);
765            }
766            
767            
768            /**
769             * Retrieves the specified optional string parameter. If it doesn't 
770             * exist the method will return the specified default value.
771             *
772             * @param name         The parameter name.
773             * @param defaultValue The default return value if the parameter 
774             *                     doesn't exist. May be {@code null}.
775             *
776             * @return The parameter value as a string.
777             *
778             * @throws JSONRPC2Error On a bad type or {@code null} value
779             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
780             */
781            public String getOptString(final String name, final String defaultValue)
782                    throws JSONRPC2Error {
783            
784                    return getOptString(name, false, defaultValue);
785            }
786            
787            
788            /**
789             * Retrieves the specified optional string parameter. If it doesn't 
790             * exist the method will return the specified default value.
791             *
792             * @param name         The parameter name.
793             * @param allowNull    If {@code true} allows a {@code null} value.
794             * @param defaultValue The default return value if the parameter 
795             *                     doesn't exist. May be {@code null}.
796             *
797             * @return The parameter value as a string.
798             *
799             * @throws JSONRPC2Error On a bad type ({@link JSONRPC2Error#INVALID_PARAMS}).
800             */
801            public String getOptString(final String name, final boolean allowNull, final String defaultValue)
802                    throws JSONRPC2Error {
803            
804                    return getOpt(name, String.class, allowNull, defaultValue);
805            }
806            
807            
808            /**
809             * Retrieves the specified enumerated string parameter.
810             *
811             * @param name        The parameter name.
812             * @param enumStrings The acceptable string values. Must not be
813             *                    {@code null}.
814             *
815             * @return The parameter value as a string.
816             *
817             * @throws JSONRPC2Error On a missing parameter, bad type or 
818             *                       bad enumeration value
819             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
820             */
821            public String getEnumString(final String name, final String[] enumStrings)
822                    throws JSONRPC2Error {
823                    
824                    return getEnumString(name, enumStrings, false); 
825            }
826            
827            
828            /**
829             * Retrieves the specified enumerated string parameter, allowing for a
830             * case insenstive match.
831             *
832             * @param name        The parameter name.
833             * @param enumStrings The acceptable string values. Must not be
834             *                    {@code null}.
835             * @param ignoreCase  {@code true} for a case insensitive match.
836             *
837             * @return The matching parameter value as a string.
838             *
839             * @throws JSONRPC2Error On a missing parameter, bad type or 
840             *                       bad enumeration value
841             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
842             */
843            public String getEnumString(final String name, 
844                                        final String[] enumStrings, 
845                                        final boolean ignoreCase)
846                    throws JSONRPC2Error {
847                    
848                    String value = get(name, String.class);
849                    
850                    String match = getEnumStringMatch(value, enumStrings, ignoreCase);
851    
852                    if (match == null)
853                            throwEnumParameterException(name, enumStrings);
854    
855                    return match;
856            }
857            
858            
859            /**
860             * Retrieves the specified optional enumerated string parameter.
861             *
862             * @param name         The parameter name.
863             * @param enumStrings  The acceptable string values. Must not be
864             *                     {@code null}.
865             * @param defaultValue The default return value if the parameter 
866             *                     doesn't exist. May be {@code null}.
867             *
868             * @return The parameter value as a string.
869             *
870             * @throws JSONRPC2Error On a bad type or bad enumeration value
871             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
872             */
873            public String getOptEnumString(final String name, 
874                                           final String[] enumStrings, 
875                                           final String defaultValue)
876                    throws JSONRPC2Error {
877                    
878                    return getOptEnumString(name, enumStrings, defaultValue, false); 
879            }
880            
881            
882            /**
883             * Retrieves the specified optional enumerated string parameter, 
884             * allowing for a case insenstive match. If it doesn't exist the method 
885             * will return the specified default value.
886             *
887             * @param name         The parameter name.
888             * @param enumStrings  The acceptable string values. Must not be
889             *                     {@code null}.
890             * @param defaultValue The default return value if the parameter 
891             *                     doesn't exist. May be {@code null}.
892             * @param ignoreCase   {@code true} for a case insensitive match.
893             *
894             * @return The matching parameter value as a string.
895             *
896             * @throws JSONRPC2Error On a bad type or bad enumeration value
897             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
898             */
899            public String getOptEnumString(final String name, 
900                                           final String[] enumStrings, 
901                                           final String defaultValue, 
902                                           final boolean ignoreCase)
903                    throws JSONRPC2Error {
904                    
905                    String value = getOpt(name, String.class, defaultValue);
906    
907                    if (defaultValue == null && value == null)
908                            return null;
909    
910                    String match = getEnumStringMatch(value, enumStrings, ignoreCase);
911    
912                    if (match == null)
913                            throwEnumParameterException(name, enumStrings);
914    
915                    return match;
916            }
917            
918            
919            /**
920             * Retrieves the specified enumerated parameter (from a JSON string 
921             * that has a predefined set of possible values).
922             *
923             * @param name      The parameter name.
924             * @param enumClass An enumeration type with constant names 
925             *                  representing the acceptable string values. Must not
926             *                  be {@code null}.
927             *
928             * @return The matching enumeration constant.
929             *
930             * @throws JSONRPC2Error On a missing parameter, bad type or 
931             *                       bad enumeration value
932             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
933             */
934            public <T extends Enum<T>> T getEnum(final String name, final Class<T> enumClass)
935                    throws JSONRPC2Error {
936                    
937                    return getEnum(name, enumClass, false);
938            }
939            
940            
941            /**
942             * Retrieves the specified enumerated parameter (from a JSON string 
943             * that has a predefined set of possible values), allowing for a case 
944             * insensitive match.
945             *
946             * @param name       The parameter name.
947             * @param enumClass  An enumeration type with constant names 
948             *                   representing the acceptable string values. Must
949             *                   not be {@code null}.
950             * @param ignoreCase If {@code true} a case insensitive match against
951             *                   the acceptable constant names is performed.
952             *
953             * @return The matching enumeration constant.
954             *
955             * @throws JSONRPC2Error On a missing parameter, bad type or 
956             *                       bad enumeration value
957             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
958             */
959            public <T extends Enum<T>> T getEnum(final String name, 
960                                                 final Class<T> enumClass, 
961                                                 final boolean ignoreCase)
962                    throws JSONRPC2Error {
963                    
964                    String value = get(name, String.class);
965                    
966                    T match = getEnumStringMatch(value, enumClass, ignoreCase);
967    
968                    if (match == null)
969                            throwEnumParameterException(name, enumClass);
970    
971                    return match;
972            }
973            
974            
975            /**
976             * Retrieves the specified optional enumerated parameter (from a JSON
977             * string that has a predefined set of possible values).
978             *
979             * @param name         The parameter name.
980             * @param enumClass    An enumeration type with constant names 
981             *                     representing the acceptable string values. Must
982             *                     not be {@code null}.
983             * @param defaultValue The default return value if the parameter 
984             *                     doesn't exist. May be {@code null}.
985             *
986             * @return The matching enumeration constant.
987             *
988             * @throws JSONRPC2Error On a bad type or bad enumeration value
989             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
990             */
991            public <T extends Enum<T>> T getOptEnum(final String name, 
992                                                    final Class<T> enumClass, 
993                                                    final T defaultValue)
994                    throws JSONRPC2Error {
995                    
996                    return getOptEnum(name, enumClass, defaultValue, false); 
997            }
998            
999            
1000            /**
1001             * Retrieves the specified optional enumerated parameter (from a JSON
1002             * string that has a predefined set of possible values), allowing for 
1003             * a case insenstive match. If it doesn't exist the method will return 
1004             * the specified default value.
1005             *
1006             * @param name         The parameter name.
1007             * @param enumClass    An enumeration type with constant names 
1008             *                     representing the acceptable string values. Must
1009             *                     not be {@code null}.
1010             * @param defaultValue The default return value if the parameter 
1011             *                     doesn't exist. May be {@code null}.
1012             * @param ignoreCase   If {@code true} a case insensitive match against
1013             *                     the acceptable constant names is performed.
1014             *
1015             * @return The matching enumeration constant.
1016             *
1017             * @throws JSONRPC2Error On a bad type or bad enumeration value
1018             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1019             */
1020            public <T extends Enum<T>> T getOptEnum(final String name, 
1021                                                    final Class<T> enumClass, 
1022                                                    final T defaultValue, 
1023                                                    final boolean ignoreCase)
1024                    throws JSONRPC2Error {
1025    
1026                    String value = null;
1027    
1028                    if (defaultValue != null)
1029                            value = getOpt(name, String.class, defaultValue.toString());
1030    
1031                    else 
1032                            value = getOpt(name, String.class, null);
1033    
1034                    if (defaultValue == null && value == null)
1035                            return null;
1036    
1037                    T match = getEnumStringMatch(value, enumClass, ignoreCase);
1038    
1039                    if (match == null)
1040                            throwEnumParameterException(name, enumClass);
1041    
1042                    return match;
1043            }
1044            
1045            
1046            /**
1047             * Retrieves the specified boolean (maps from JSON true/false)
1048             * parameter.
1049             *
1050             * @param name The parameter name.
1051             *
1052             * @return The parameter value as a {@code boolean}.
1053             *
1054             * @throws JSONRPC2Error On a missing parameter, bad type or
1055             *                       {@code null} value
1056             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1057             */
1058            public boolean getBoolean(final String name)
1059                    throws JSONRPC2Error {
1060            
1061                    return get(name, Boolean.class);
1062            }
1063            
1064            
1065            /**
1066             * Retrieves the specified optional boolean (maps from JSON true/false)
1067             * parameter. If it doesn't exist the method will return the specified 
1068             * default value.
1069             *
1070             * @param name         The parameter name.
1071             * @param defaultValue The default return value if the parameter 
1072             *                     doesn't exist.
1073             *
1074             * @return The parameter value as a {@code boolean}.
1075             *
1076             * @throws JSONRPC2Error On a bad parameter type or {@code null} value
1077             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1078             */
1079            public boolean getOptBoolean(final String name, final boolean defaultValue)
1080                    throws JSONRPC2Error {
1081            
1082                    return getOpt(name, Boolean.class, defaultValue);
1083            }
1084            
1085            
1086            /**
1087             * Retrieves the specified numeric parameter as an {@code int}.
1088             *
1089             * @param name The parameter name.
1090             *
1091             * @return The parameter value as an {@code int}.
1092             *
1093             * @throws JSONRPC2Error On a missing parameter, bad type or 
1094             *                       {@code null} value
1095             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1096             */
1097            public int getInt(final String name)
1098                    throws JSONRPC2Error {
1099                    
1100                    Number number = get(name, Number.class);
1101                    return number.intValue();
1102            }
1103            
1104            
1105            /**
1106             * Retrieves the specified optional numeric parameter as an 
1107             * {@code int}. If it doesn't exist the method will return the 
1108             * specified default value.
1109             *
1110             * @param name         The parameter name.
1111             * @param defaultValue The default return value if the parameter 
1112             *                     doesn't exist.
1113             *
1114             * @return The parameter value as an {@code int}.
1115             *
1116             * @throws JSONRPC2Error On a bad parameter type or {@code null} value
1117             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1118             */
1119            public int getOptInt(final String name, final int defaultValue)
1120                    throws JSONRPC2Error {
1121            
1122                    Number number = getOpt(name, Number.class, defaultValue);
1123                    return number.intValue();
1124            }
1125            
1126            
1127            /**
1128             * Retrieves the specified numeric parameter as a {@code long}.
1129             *
1130             * @param name The parameter name.
1131             *
1132             * @return The parameter value as a {@code long}.
1133             *
1134             * @throws JSONRPC2Error On a missing parameter, bad type or
1135             *                       {@code null} value
1136             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1137             */
1138            public long getLong(final String name)
1139                    throws JSONRPC2Error {
1140            
1141                    Number number = get(name, Number.class);
1142                    return number.longValue();
1143            }
1144            
1145            
1146            /**
1147             * Retrieves the specified optional numeric parameter as a 
1148             * {@code long}. If it doesn't exist the method will return the 
1149             * specified default value.
1150             *
1151             * @param name         The parameter name.
1152             * @param defaultValue The default return value if the parameter 
1153             *                     doesn't exist.
1154             *
1155             * @return The parameter value as a long.
1156             *
1157             * @throws JSONRPC2Error On a bad parameter type or {@code null} value
1158             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1159             */
1160            public long getOptLong(final String name, final long defaultValue)
1161                    throws JSONRPC2Error {
1162            
1163                    Number number = getOpt(name, Number.class, defaultValue);
1164                    return number.longValue();
1165            }
1166            
1167            
1168            /**
1169             * Retrieves the specified numeric parameter as a {@code float}.
1170             *
1171             * @param name The parameter name.
1172             *
1173             * @return The parameter value as a {@code float}.
1174             *
1175             * @throws JSONRPC2Error On a missing parameter, bad type or 
1176             *                       {@code null} value
1177             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1178             */
1179            public float getFloat(final String name)
1180                    throws JSONRPC2Error {
1181            
1182                    Number number = get(name, Number.class);
1183                    return number.floatValue();
1184            }
1185            
1186            
1187            /**
1188             * Retrieves the specified optional numeric parameter as a 
1189             * {@code float}. If it doesn't exist the method will return the 
1190             * specified default value.
1191             *
1192             * @param name         The parameter name.
1193             * @param defaultValue The default return value if the parameter 
1194             *                     doesn't exist.
1195             *
1196             * @return The parameter value as a {@code float}.
1197             *
1198             * @throws JSONRPC2Error On a bad parameter type or {@code null} value
1199             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1200             */
1201            public float getOptFloat(final String name, final float defaultValue)
1202                    throws JSONRPC2Error {
1203            
1204                    Number number = getOpt(name, Number.class, defaultValue);
1205                    return number.floatValue();
1206            }
1207            
1208            
1209            /**
1210             * Retrieves the specified numeric parameter as a {@code double}.
1211             *
1212             * @param name The parameter name.
1213             *
1214             * @return The parameter value as a {@code double}.
1215             *
1216             * @throws JSONRPC2Error On a missing parameter, bad type or
1217             *                       {@code null} value
1218             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1219             */
1220            public double getDouble(final String name)
1221                    throws JSONRPC2Error {
1222            
1223                    Number number = get(name, Number.class);
1224                    return number.doubleValue();
1225            }
1226            
1227            
1228            /**
1229             * Retrieves the specified optional numeric parameter as a 
1230             * {@code double}. If it doesn't exist the method will return the 
1231             * specified default value.
1232             *
1233             * @param name         The parameter name.
1234             * @param defaultValue The default return value if the parameter 
1235             *                     doesn't exist.
1236             *
1237             * @return The parameter value as a {@code double}.
1238             *
1239             * @throws JSONRPC2Error On a bad parameter type or {@code null} value
1240             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1241             */
1242            public double getOptDouble(final String name, final double defaultValue)
1243                    throws JSONRPC2Error {
1244            
1245                    Number number = getOpt(name, Number.class, defaultValue);
1246                    return number.doubleValue();
1247            }
1248            
1249            
1250            /**
1251             * Retrieves the specified list (maps from JSON array) parameter.
1252             *
1253             * @param name The parameter name.
1254             *
1255             * @return The parameter value as a list.
1256             *
1257             * @throws JSONRPC2Error On a missing parameter, bad type or
1258             *                       {@code null} value
1259             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1260             */
1261            public List<Object> getList(final String name)
1262                    throws JSONRPC2Error {
1263            
1264                    final boolean allowNull = false;
1265            
1266                    return getList(name, allowNull);
1267            }
1268            
1269            
1270            /**
1271             * Retrieves the specified list (maps from JSON array) parameter.
1272             *
1273             * @param name      The parameter name.
1274             * @param allowNull If {@code true} allows a {@code null} value.
1275             *
1276             * @return The parameter value as a list.
1277             *
1278             * @throws JSONRPC2Error On a missing parameter or bad type
1279             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1280             */
1281            @SuppressWarnings("unchecked")
1282            public List<Object> getList(final String name, final boolean allowNull)
1283                    throws JSONRPC2Error {
1284            
1285                    return (List<Object>)get(name, List.class, allowNull);
1286            }
1287            
1288            
1289            /**
1290             * Retrieves the specified optional list (maps from JSON array) 
1291             * parameter. If it doesn't exist the method will return the specified 
1292             * default value.
1293             *
1294             * @param name         The parameter name.
1295             * @param defaultValue The default return value if the parameter 
1296             *                     doesn't exist. May be {@code null}.
1297             *
1298             * @return The parameter value as a list.
1299             *
1300             * @throws JSONRPC2Error On a bad parameter type or {@code null} value
1301             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1302             */
1303            public List<Object> getOptList(final String name, final List<Object> defaultValue)
1304                    throws JSONRPC2Error {
1305            
1306                    final boolean allowNull = false;
1307            
1308                    return getOptList(name, allowNull, defaultValue);
1309            }
1310            
1311            
1312            /**
1313             * Retrieves the specified optional list (maps from JSON array) 
1314             * parameter. If it doesn't exist the method will return the specified 
1315             * default value.
1316             *
1317             * @param name         The parameter name.
1318             * @param allowNull    If {@code true} allows a {@code null} value.
1319             * @param defaultValue The default return value if the parameter 
1320             *                     doesn't exist. May be {@code null}.
1321             *
1322             * @return The parameter value as a list.
1323             *
1324             * @throws JSONRPC2Error On a bad parameter type
1325             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1326             */
1327            @SuppressWarnings("unchecked")
1328            public List<Object> getOptList(final String name, 
1329                                           final boolean allowNull, 
1330                                           final List<Object> defaultValue)
1331                    throws JSONRPC2Error {
1332            
1333                    return (List<Object>)getOpt(name, List.class, allowNull, defaultValue);
1334            }
1335            
1336            
1337            /**
1338             * Retrieves the specified string array (maps from JSON array of 
1339             * strings) parameter.
1340             *
1341             * @param name The parameter name.
1342             *
1343             * @return The parameter value as a string array.
1344             *
1345             * @throws JSONRPC2Error On a missing parameter, bad type or
1346             *                       {@code null} value
1347             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1348             */
1349            public String[] getStringArray(final String name)
1350                    throws JSONRPC2Error {
1351            
1352                    final boolean allowNull = false;
1353            
1354                    return getStringArray(name, allowNull);
1355            }
1356            
1357            
1358            /**
1359             * Retrieves the specified string array (maps from JSON array of 
1360             * strings) parameter.
1361             *
1362             * @param name      The parameter name.
1363             * @param allowNull If {@code true} allows a {@code null} value.
1364             *
1365             * @return The parameter value as a string array.
1366             *
1367             * @throws JSONRPC2Error On a missing parameter or bad type
1368             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1369             */
1370            @SuppressWarnings("unchecked")
1371            public String[] getStringArray(final String name, final boolean allowNull)
1372                    throws JSONRPC2Error {
1373            
1374                    List<Object> list = getList(name, allowNull);
1375                    
1376                    if (list == null)
1377                            return null;
1378                    
1379                    try {
1380                            return list.toArray(new String[0]);
1381                            
1382                    } catch (ArrayStoreException e) {
1383                            
1384                            throw newArrayException(name);
1385                    }
1386            }
1387            
1388            
1389            /**
1390             * Retrieves the specified optional string array (maps from JSON array
1391             * of strings) parameter. If it doesn't exist the method will return 
1392             * the specified default value.
1393             *
1394             * @param name         The parameter name.
1395             * @param defaultValue The default return value if the parameter 
1396             *                     doesn't exist. May be {@code null}.
1397             *
1398             * @return The parameter value as a string array.
1399             *
1400             * @throws JSONRPC2Error On a bad parameter type or {@code null} value
1401             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1402             */
1403            public String[] getOptStringArray(final String name, final String[] defaultValue)
1404                    throws JSONRPC2Error {
1405            
1406                    final boolean allowNull = false;
1407            
1408                    return getOptStringArray(name, allowNull, defaultValue);
1409            }
1410            
1411            
1412            /**
1413             * Retrieves the specified optional string array (maps from JSON array
1414             * of strings) parameter. If it doesn't exist the method will return 
1415             * the specified default value.
1416             *
1417             * @param name         The parameter name.
1418             * @param allowNull    If {@code true} allows a {@code null} value.
1419             * @param defaultValue The default return value if the parameter 
1420             *                     doesn't exist. May be {@code null}.
1421             *
1422             * @return The parameter value as a string array.
1423             *
1424             * @throws JSONRPC2Error On a bad parameter type
1425             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1426             */
1427            public String[] getOptStringArray(final String name, 
1428                                              final boolean allowNull, 
1429                                              final String[] defaultValue)
1430                    throws JSONRPC2Error {
1431            
1432                    if (! hasParameter(name))
1433                            return defaultValue;
1434            
1435                    return getStringArray(name, allowNull);
1436            }
1437            
1438            
1439            /**
1440             * Retrieves the specified map (maps from JSON object) parameter.
1441             *
1442             * @param name The parameter name.
1443             *
1444             * @return The parameter value as a map.
1445             *
1446             * @throws JSONRPC2Error On a missing parameter, bad type or
1447             *                       {@code null} value
1448             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1449             */
1450            public Map<String,Object> getMap(final String name)
1451                    throws JSONRPC2Error {
1452                    
1453                    return getMap(name, false);
1454            }
1455            
1456            
1457            /**
1458             * Retrieves the specified map (maps from JSON object) parameter.
1459             *
1460             * @param name      The parameter name.
1461             * @param allowNull If {@code true} allows a {@code null} value.
1462             *
1463             * @return The parameter value as a map.
1464             *
1465             * @throws JSONRPC2Error On a missing parameter or bad type
1466             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1467             */
1468            @SuppressWarnings("unchecked")
1469            public Map<String,Object> getMap(final String name, final boolean allowNull)
1470                    throws JSONRPC2Error {
1471                    
1472                    try {
1473                            return (Map<String,Object>)get(name, Map.class, allowNull);
1474                            
1475                    } catch (ClassCastException e) {
1476                            
1477                            throw newUnexpectedParameterTypeException(name);
1478                    }
1479            }
1480            
1481            
1482            /**
1483             * Retrieves the specified optional map (maps from JSON object) 
1484             * parameter. If it doesn't exist the method will return the specified 
1485             * default value.
1486             *
1487             * @param name         The parameter name.
1488             * @param defaultValue The default return value if the parameter 
1489             *                     doesn't exist. May be {@code null}.
1490             *
1491             * @return The parameter value as a map.
1492             *
1493             * @throws JSONRPC2Error On a bad parameter type or {@code null} value
1494             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1495             */
1496            public Map<String,Object> getOptMap(final String name, final Map<String,Object> defaultValue)
1497                    throws JSONRPC2Error {
1498            
1499                    return getOptMap(name, false, defaultValue);
1500            }
1501            
1502            
1503            /**
1504             * Retrieves the specified optional map (maps from JSON object) 
1505             * parameter. If it doesn't exist the method will return the specified 
1506             * default value.
1507             *
1508             * @param name         The parameter name.
1509             * @param allowNull    If {@code true} allows a {@code null} value.
1510             * @param defaultValue The default return value if the parameter 
1511             *                     doesn't exist. May be {@code null}.
1512             *
1513             * @return The parameter value as a map.
1514             *
1515             * @throws JSONRPC2Error On a bad parameter type
1516             *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1517             */
1518            @SuppressWarnings("unchecked")
1519            public Map<String,Object> getOptMap(final String name, 
1520                                                final boolean allowNull, 
1521                                                final Map<String,Object> defaultValue)
1522                    throws JSONRPC2Error {
1523                    
1524                    try {
1525                            return (Map<String,Object>)getOpt(name, Map.class, allowNull, defaultValue);
1526                            
1527                    } catch (ClassCastException e) {
1528                            
1529                            throw newUnexpectedParameterTypeException(name);
1530                    }
1531            }
1532    }