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