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