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