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 = null;
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         * @see #hasParam
262         */
263        @Deprecated
264        public boolean hasParameter(final int position) {
265
266                return hasParam(position);
267        }
268        
269        
270        /**
271         * Throws a {@code JSONRPC2Error.INVALID_PARAMS} exception if there is
272         * no parameter at the specified position.
273         *
274         * <p>You may use this method to fire the proper JSON-RPC 2.0 error
275         * on a missing mandatory parameter.
276         *
277         * @param position The parameter position, starting with zero for the 
278         *                 first.
279         *
280         * @throws JSONRPC2Error On a missing parameter
281         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
282         */
283        public void ensureParam(final int position)
284                throws JSONRPC2Error {
285                
286                if (position >= params.size() )
287                        throwMissingParameterException(position);
288        }
289
290
291        /**
292         * @see #ensureParam
293         */
294        @Deprecated
295        public void ensureParameter(final int position)
296                throws JSONRPC2Error {
297
298                ensureParam(position);
299        }
300        
301        
302        /**
303         * Throws a {@code JSONRPC2Error.INVALID_PARAMS} exception if there is
304         * no parameter at the specified position, its value is {@code null}, 
305         * or its type doesn't map to the specified.
306         *
307         * <p>You may use this method to fire the proper JSON-RPC 2.0 error
308         * on a missing or badly-typed mandatory parameter.
309         *
310         * @param position The parameter position.
311         * @param clazz    The corresponding Java class that the parameter 
312         *                 should map to (any one of the return types of the 
313         *                 {@code getXXX()} getter methods. Set to 
314         *                 {@code Object.class} to allow any type. Must not be
315         *                 {@code null}.
316         *
317         * @throws JSONRPC2Error On a missing parameter, {@code null} value or 
318         *                       bad type ({@link JSONRPC2Error#INVALID_PARAMS}).
319         */
320        public <T> void ensureParam(final int position, final Class<T> clazz)
321                throws JSONRPC2Error {
322                
323                ensureParameter(position, clazz, false);
324        }
325
326
327        /**
328         * @see #ensureParam(int, Class)
329         */
330        @Deprecated
331        public <T> void ensureParameter(final int position, final Class<T> clazz)
332                throws JSONRPC2Error {
333
334                ensureParam(position, clazz);
335        }
336        
337        
338        /**
339         * Throws a {@code JSONRPC2Error.INVALID_PARAMS} exception if there is
340         * no parameter at the specified position or its type doesn't map to 
341         * the specified.
342         *
343         * <p>You may use this method to fire the proper JSON-RPC 2.0 error
344         * on a missing or badly-typed mandatory parameter.
345         *
346         * @param position  The parameter position.
347         * @param clazz     The corresponding Java class that the parameter 
348         *                  should map to (any one of the return types of the 
349         *                  {@code getXXX()} getter methods. Set to 
350         *                  {@code Object.class} to allow any type. Must not be
351         *                  {@code null}.
352         * @param allowNull If {@code true} allows a {@code null} parameter
353         *                  value.
354         *
355         * @throws JSONRPC2Error On a missing parameter or bad type 
356         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
357         */
358        public <T> void ensureParam(final int position, 
359                                    final Class<T> clazz, 
360                                    final boolean allowNull)
361                throws JSONRPC2Error {
362                
363                // First, check existence only
364                ensureParameter(position);
365                
366                // Now check type
367                Object value = params.get(position);
368                
369                if (value == null) {
370                        
371                        if (allowNull)
372                                return; // ok
373                        else
374                                throwNullParameterException(position);
375                }
376                
377                if (! clazz.isAssignableFrom(value.getClass()))
378                        throw newUnexpectedParameterTypeException(position);
379        }
380
381
382        /**
383         * @see #ensureParam(int, Class, boolean)
384         */
385        @Deprecated
386        public <T> void ensureParameter(final int position, 
387                                        final Class<T> clazz, 
388                                        final boolean allowNull)
389                throws JSONRPC2Error {
390
391                ensureParam(position, clazz, allowNull);
392        }
393        
394        
395        /**
396         * Retrieves the specified parameter which can be of any type. Use this 
397         * generic getter if you want to cast the value yourself. Otherwise 
398         * look at the typed {@code get*} methods.
399         *
400         * @param position The parameter position.
401         *
402         * @return The parameter value.
403         *
404         * @throws JSONRPC2Error On a missing parameter
405         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
406         */
407        public Object get(final int position)
408                throws JSONRPC2Error {
409                
410                ensureParameter(position);
411                
412                return params.get(position);
413        }
414        
415        
416        /**
417         * Retrieves the specified parameter which must map to the provided
418         * class (use the appropriate wrapper class for primitive types).
419         *
420         * @param position The parameter position.
421         * @param clazz    The corresponding Java class that the parameter 
422         *                 should map to (any one of the return types of the 
423         *                 {@code getXXX()} getter methods. Set to
424         *                 {@code Object.class} to allow any type. Must not be
425         *                 {@code null}.
426         *
427         * @return The parameter value.
428         *
429         * @throws JSONRPC2Error On a missing parameter, {@code null} value or 
430         *                       bad type ({@link JSONRPC2Error#INVALID_PARAMS}).
431         */
432        public <T> T get(final int position, final Class<T> clazz)
433                throws JSONRPC2Error {
434        
435                return get(position, clazz, false);
436        }
437        
438        
439        /**
440         * Retrieves the specified parameter which must map to the provided
441         * class (use the appropriate wrapper class for primitive types).
442         *
443         * @param position  The parameter position.
444         * @param clazz     The corresponding Java class that the parameter 
445         *                  should map to (any one of the return types of the 
446         *                  {@code getXXX()} getter methods. Set to
447         *                  {@code Object.class} to allow any type. Must not be
448         *                  {@code null}.
449         * @param allowNull If {@code true} allows a {@code null} parameter
450         *                  value.
451         *
452         * @return The parameter value.
453         *
454         * @throws JSONRPC2Error On a missing parameter or bad type 
455         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
456         */
457        @SuppressWarnings("unchecked")
458        public <T> T get(final int position, final Class<T> clazz, final boolean allowNull)
459                throws JSONRPC2Error {
460        
461                ensureParameter(position, clazz, allowNull);
462                
463                try {
464                        return (T)params.get(position);
465                        
466                } catch (ClassCastException e) {
467                        
468                        throw newUnexpectedParameterTypeException(position);
469                }
470        }
471        
472        
473        /**
474         * Retrieves the specified optional parameter which must map to the
475         * provided class (use the appropriate wrapper class for primitive 
476         * types). If the parameter doesn't exist the method returns the 
477         * specified default value.
478         *
479         * @param position     The parameter position.
480         * @param clazz        The corresponding Java class that the parameter 
481         *                     should map to (any one of the return types of 
482         *                     the {@code getXXX()} getter methods. Set to 
483         *                     {@code Object.class} to allow any type. Must not
484         *                     be {@code null}.
485         * @param defaultValue The default return value if the parameter
486         *                     doesn't exist. May be {@code null}.
487         *
488         * @return The parameter value.
489         *
490         * @throws JSONRPC2Error On a bad parameter type or {@code null} value
491         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
492         */
493        public <T> T getOpt(final int position, final Class<T> clazz, final T defaultValue)
494                throws JSONRPC2Error {
495        
496                return getOpt(position, clazz, false, defaultValue);
497        }
498        
499        
500        /**
501         * Retrieves the specified optional parameter which must map to the
502         * provided class (use the appropriate wrapper class for primitive 
503         * types). If the parameter doesn't exist the method returns the 
504         * specified default value.
505         *
506         * @param position     The parameter position.
507         * @param clazz        The corresponding Java class that the parameter 
508         *                     should map to (any one of the return types of 
509         *                     the {@code getXXX()} getter methods. Set to 
510         *                     {@code Object.class} to allow any type. Must not
511         *                     be {@code null}.
512         * @param allowNull    If {@code true} allows a {@code null} parameter
513         *                     value.
514         * @param defaultValue The default return value if the parameter
515         *                     doesn't exist. May be {@code null}.
516         *
517         * @return The parameter value.
518         *
519         * @throws JSONRPC2Error On a bad parameter type
520         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
521         */
522        @SuppressWarnings("unchecked")
523        public <T> T getOpt(final int position, 
524                            final Class<T> clazz, 
525                            final boolean allowNull, 
526                            final T defaultValue)
527                throws JSONRPC2Error {
528        
529                if (! hasParameter(position))
530                        return defaultValue;
531                
532                ensureParameter(position, clazz, allowNull);
533                
534                try {
535                        return (T)params.get(position);
536                        
537                } catch (ClassCastException e) {
538                        
539                        throw newUnexpectedParameterTypeException(position);
540                }
541        }
542        
543        
544        /**
545         * Retrieves the specified string parameter.
546         *
547         * @param position The parameter position.
548         *
549         * @return The parameter value as a string.
550         *
551         * @throws JSONRPC2Error On a missing parameter, bad type or 
552         *                       {@code null} value 
553         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
554         */
555        public String getString(final int position)
556                throws JSONRPC2Error {
557                
558                return getString(position, false);
559        }
560        
561        
562        /**
563         * Retrieves the specified string parameter.
564         *
565         * @param position  The parameter position.
566         * @param allowNull If {@code true} allows a {@code null} value.
567         *
568         * @return The parameter value as a string.
569         *
570         * @throws JSONRPC2Error On a missing parameter or bad type
571         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
572         */
573        public String getString(final int position, final boolean allowNull)
574                throws JSONRPC2Error {
575                
576                return get(position, String.class, allowNull);
577        }
578        
579        
580        /**
581         * Retrieves the specified optional string parameter. If it doesn't 
582         * exist the method will return the specified default value.
583         *
584         * @param position     The parameter position.
585         * @param defaultValue The default return value if the parameter 
586         *                     doesn't exist. May be {@code null}.
587         *
588         * @return The parameter value as a string.
589         *
590         * @throws JSONRPC2Error On a bad type or {@code null} value
591         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
592         */
593        public String getOptString(final int position, final String defaultValue)
594                throws JSONRPC2Error {
595        
596                return getOptString(position, false, defaultValue);
597        }
598        
599        
600        /**
601         * Retrieves the specified optional string parameter. If it doesn't 
602         * exist the method will return the specified default value.
603         *
604         * @param position     The parameter position.
605         * @param allowNull    If {@code true} allows a {@code null} value.
606         * @param defaultValue The default return value if the parameter 
607         *                     doesn't exist. May be {@code null}.
608         *
609         * @return The parameter value as a string.
610         *
611         * @throws JSONRPC2Error On a bad type ({@link JSONRPC2Error#INVALID_PARAMS}).
612         */
613        public String getOptString(final int position, final boolean allowNull, final String defaultValue)
614                throws JSONRPC2Error {
615        
616                return getOpt(position, String.class, allowNull, defaultValue);
617        }
618        
619        
620        /**
621         * Retrieves the specified enumerated string parameter.
622         *
623         * @param position    The parameter position.
624         * @param enumStrings The acceptable string values. Must not be
625         *                    {@code null}.
626         *
627         * @return The parameter value as a string.
628         *
629         * @throws JSONRPC2Error On a missing parameter, bad type or 
630         *                       bad enumeration value
631         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
632         */
633        public String getEnumString(final int position, final String[] enumStrings)
634                throws JSONRPC2Error {
635                
636                return getEnumString(position, enumStrings, false); 
637        }
638        
639        
640        /**
641         * Retrieves the specified enumerated string parameter, allowing for a
642         * case insenstive match.
643         *
644         * @param position    The parameter position.
645         * @param enumStrings The acceptable string values. Must not be
646         *                    {@code null}.
647         * @param ignoreCase  {@code true} for a case insensitive match.
648         *
649         * @return The matching parameter value as a string.
650         *
651         * @throws JSONRPC2Error On a missing parameter, bad type or 
652         *                       bad enumeration value
653         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
654         */
655        public String getEnumString(final int position, 
656                                    final String[] enumStrings, 
657                                    final boolean ignoreCase)
658                throws JSONRPC2Error {
659                
660                String value = get(position, String.class);
661                
662                String match = getEnumStringMatch(value, enumStrings, ignoreCase);
663
664                if (match == null)
665                        throwEnumParameterException(position, enumStrings);
666
667                return match;
668        }
669        
670        
671        /**
672         * Retrieves the specified optional enumerated string parameter. If it
673         * doesn't exist the method will return the specified default value.
674         *
675         * @param position     The parameter position.
676         * @param enumStrings  The acceptable string values. Must not be
677         *                     {@code null}.
678         * @param defaultValue The default return value if the parameter 
679         *                     doesn't exist. May be {@code null}.
680         *
681         * @return The parameter value as a string.
682         *
683         * @throws JSONRPC2Error On a bad type or bad enumeration value
684         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
685         */
686        public String getOptEnumString(final int position, 
687                                       final String[] enumStrings, 
688                                       final String defaultValue)
689                throws JSONRPC2Error {
690                
691                return getOptEnumString(position, enumStrings, defaultValue, false); 
692        }
693        
694        
695        /**
696         * Retrieves the specified optional enumerated string parameter, 
697         * allowing for a case insenstive match. If it doesn't exist the method 
698         * will return the specified default value.
699         *
700         * @param position     The parameter position.
701         * @param enumStrings  The acceptable string values. Must not be
702         *                     {@code null}.
703         * @param defaultValue The default return value if the parameter 
704         *                     doesn't exist. May be {@code null}.
705         * @param ignoreCase   {@code true} for a case insensitive match.
706         *
707         * @return The parameter value as a string.
708         *
709         * @throws JSONRPC2Error On a bad type or bad enumeration value
710         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
711         */
712        public String getOptEnumString(final int position, 
713                                       final String[] enumStrings, 
714                                       final String defaultValue, 
715                                       final boolean ignoreCase)
716                throws JSONRPC2Error {
717                
718                String value = getOpt(position, String.class, defaultValue);
719
720                if (defaultValue == null && value == null)
721                        return null;
722                
723                String match = getEnumStringMatch(value, enumStrings, ignoreCase);
724
725                if (match == null)
726                        throwEnumParameterException(position, enumStrings);
727
728                return match;
729        }
730        
731        
732        /**
733         * Retrieves the specified enumerated parameter (from a JSON string 
734         * that has a predefined set of possible values).
735         *
736         * @param position  The parameter position.
737         * @param enumClass An enumeration type with constant names 
738         *                  representing the acceptable string values. Must not
739         *                  be {@code null}.
740         *
741         * @return The matching enumeration constant.
742         *
743         * @throws JSONRPC2Error On a missing parameter, bad type or 
744         *                       bad enumeration value
745         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
746         */
747        public <T extends Enum<T>> T getEnum(final int position, final Class<T> enumClass)
748                throws JSONRPC2Error {
749                
750                return getEnum(position, enumClass, false); 
751        }
752        
753        
754        /**
755         * Retrieves the specified enumerated parameter (from a JSON string 
756         * that has a predefined set of possible values), allowing for a case 
757         * insensitive match.
758         *
759         * @param position   The parameter position.
760         * @param enumClass  An enumeration type with constant names 
761         *                   representing the acceptable string values. Must
762         *                   not be {@code null}.
763         * @param ignoreCase If {@code true} a case insensitive match against
764         *                   the acceptable constant names is performed.
765         *
766         * @return The matching enumeration constant.
767         *
768         * @throws JSONRPC2Error On a missing parameter, bad type or 
769         *                       bad enumeration value
770         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
771         */
772        public <T extends Enum<T>> T getEnum(final int position, 
773                                             final Class<T> enumClass, 
774                                             final boolean ignoreCase)
775                throws JSONRPC2Error {
776                
777                String value = get(position, String.class);
778                
779                T match = getEnumStringMatch(value, enumClass, ignoreCase);
780
781                if (match == null)
782                        throwEnumParameterException(position, enumClass);
783
784                return match;
785        }
786        
787        
788        /**
789         * Retrieves the specified optional enumerated parameter (from a JSON
790         * string that has a predefined set of possible values). If it doesn't
791         * exist the method will return the specified default value.
792         *
793         * @param position     The parameter position.
794         * @param enumClass    An enumeration type with constant names 
795         *                     representing the acceptable string values. Must
796         *                     not be {@code null}.
797         * @param defaultValue The default return value if the parameter 
798         *                     doesn't exist. May be {@code null}.
799         *
800         * @return The matching enumeration constant.
801         *
802         * @throws JSONRPC2Error On a bad type or bad enumeration value
803         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
804         */
805        public <T extends Enum<T>> T getOptEnum(final int position, 
806                                                final Class<T> enumClass, 
807                                                final String defaultValue)
808                throws JSONRPC2Error {
809                
810                return getOptEnum(position, enumClass, defaultValue, false); 
811        }
812        
813        
814        /**
815         * Retrieves the specified optional enumerated parameter (from a JSON
816         * string that has a predefined set of possible values), allowing for a 
817         * case insenstive match. If it doesn't exist the method will return 
818         * the specified default value.
819         *
820         * @param position     The parameter position.
821         * @param enumClass    An enumeration type with constant names 
822         *                     representing the acceptable string values. Must
823         *                     not be {@code null}.
824         * @param defaultValue The default return value if the parameter 
825         *                     doesn't exist. May be {@code null}.
826         * @param ignoreCase   If {@code true} a case insensitive match against
827         *                     the acceptable constant names is performed.
828         *
829         * @return The matching enumeration constant.
830         *
831         * @throws JSONRPC2Error On a bad type or bad enumeration value
832         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
833         */
834        public <T extends Enum<T>> T getOptEnum(final int position, 
835                                                final Class<T> enumClass, 
836                                                final String defaultValue, 
837                                                final boolean ignoreCase)
838                throws JSONRPC2Error {
839                
840                String value = getOpt(position, String.class, defaultValue);
841
842                if (defaultValue == null && value == null)
843                        return null;
844                
845                T match = getEnumStringMatch(value, enumClass, ignoreCase);
846
847                if (match == null)
848                        throwEnumParameterException(position, enumClass);
849
850                return match;
851        }
852        
853        
854        /**
855         * Retrieves the specified boolean (maps from JSON true/false)
856         * parameter.
857         *
858         * @param position The parameter position.
859         *
860         * @return The parameter value as a {@code boolean}.
861         *
862         * @throws JSONRPC2Error On a missing parameter, bad type or
863         *                       {@code null} value
864         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
865         */
866        public boolean getBoolean(final int position)
867                throws JSONRPC2Error {
868        
869                return get(position, Boolean.class);
870        }
871        
872        
873        /**
874         * Retrieves the specified optional boolean (maps from JSON true/false)
875         * parameter. If it doesn't exist the method will return the specified 
876         * default value.
877         *
878         * @param position     The parameter position.
879         * @param defaultValue The default return value if the parameter 
880         *                     doesn't exist.
881         *
882         * @return The parameter value as a {@code boolean}.
883         *
884         * @throws JSONRPC2Error On a bad parameter type or {@code null} value
885         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
886         */
887        public boolean getOptBoolean(final int position, final boolean defaultValue)
888                throws JSONRPC2Error {
889        
890                return getOpt(position, Boolean.class, defaultValue);
891        }
892        
893        
894        /**
895         * Retrieves the specified numeric parameter as an {@code int}.
896         *
897         * @param position The parameter position.
898         *
899         * @return The parameter value as an {@code int}.
900         *
901         * @throws JSONRPC2Error On a missing parameter, bad type or 
902         *                       {@code null} value
903         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
904         */
905        public int getInt(final int position)
906                throws JSONRPC2Error {
907                
908                Number number = get(position, Number.class);
909                return number.intValue();
910        }
911        
912        
913        /**
914         * Retrieves the specified optional numeric parameter as an 
915         * {@code int}. If it doesn't exist the method will return the 
916         * specified default value.
917         *
918         * @param position     The parameter position.
919         * @param defaultValue The default return value if the parameter 
920         *                     doesn't exist.
921         *
922         * @return The parameter value as an {@code int}.
923         *
924         * @throws JSONRPC2Error On a bad parameter type or {@code null} value
925         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
926         */
927        public int getOptInt(final int position, final int defaultValue)
928                throws JSONRPC2Error {
929        
930                Number number = getOpt(position, Number.class, defaultValue);
931                return number.intValue();
932        }
933        
934        
935        /**
936         * Retrieves the specified numeric parameter as a {@code long}.
937         *
938         * @param position The parameter position.
939         *
940         * @return The parameter value as a {@code long}.
941         *
942         * @throws JSONRPC2Error On a missing parameter, bad type or
943         *                       {@code null} value
944         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
945         */
946        public long getLong(final int position)
947                throws JSONRPC2Error {
948        
949                Number number = get(position, Number.class);
950                return number.longValue();
951        }
952        
953        
954        /**
955         * Retrieves the specified optional numeric parameter as a 
956         * {@code long}. If it doesn't exist the method will return the 
957         * specified default value.
958         *
959         * @param position     The parameter position.
960         * @param defaultValue The default return value if the parameter 
961         *                     doesn't exist.
962         *
963         * @return The parameter value as a {@code long}.
964         *
965         * @throws JSONRPC2Error On a bad parameter type or {@code null} value
966         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
967         */
968        public long getOptLong(final int position, final long defaultValue)
969                throws JSONRPC2Error {
970        
971                Number number = getOpt(position, Number.class, defaultValue);
972                return number.longValue();
973        }
974        
975        
976        /**
977         * Retrieves the specified numeric parameter as a {@code float}.
978         *
979         * @param position The parameter position.
980         *
981         * @return The parameter value as a {@code float}.
982         *
983         * @throws JSONRPC2Error On a missing parameter, bad type or 
984         *                       {@code null} value
985         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
986         */
987        public float getFloat(final int position)
988                throws JSONRPC2Error {
989        
990                Number number = get(position, Number.class);
991                return number.floatValue();
992        }
993        
994        
995        /**
996         * Retrieves the specified optional numeric parameter as a 
997         * {@code float}. If it doesn't exist the method will return the 
998         * specified default value.
999         *
1000         * @param position     The parameter position.
1001         * @param defaultValue The default return value if the parameter 
1002         *                     doesn't exist.
1003         *
1004         * @return The parameter value as a {@code float}.
1005         *
1006         * @throws JSONRPC2Error On a bad parameter type or {@code null} value
1007         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1008         */
1009        public float getOptFloat(final int position, final float defaultValue)
1010                throws JSONRPC2Error {
1011        
1012                Number number = getOpt(position, Number.class, defaultValue);
1013                return number.floatValue();
1014        }
1015        
1016        
1017        /**
1018         * Retrieves the specified numeric parameter as a {@code double}.
1019         *
1020         * @param position The parameter position.
1021         *
1022         * @return The parameter value as a {@code double}.
1023         *
1024         * @throws JSONRPC2Error On a missing parameter, bad type or
1025         *                       {@code null} value
1026         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1027         */
1028        public double getDouble(final int position)
1029                throws JSONRPC2Error {
1030        
1031                Number number = get(position, Number.class);
1032                return number.doubleValue();
1033        }
1034        
1035        
1036        /**
1037         * Retrieves the specified optional numeric parameter as a 
1038         * {@code double}. If it doesn't exist the method will return the 
1039         * specified default value.
1040         *
1041         * @param position     The parameter position.
1042         * @param defaultValue The default return value if the parameter 
1043         *                     doesn't exist.
1044         *
1045         * @return The parameter value as a {@code double}.
1046         *
1047         * @throws JSONRPC2Error On a bad parameter type or {@code null} value
1048         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1049         */
1050        public double getOptDouble(final int position, final double defaultValue)
1051                throws JSONRPC2Error {
1052        
1053                Number number = getOpt(position, Number.class, defaultValue);
1054                return number.doubleValue();
1055        }
1056        
1057        
1058        /**
1059         * Retrieves the specified list (maps from JSON array) parameter.
1060         *
1061         * @param position The parameter position.
1062         *
1063         * @return The parameter value as a list.
1064         *
1065         * @throws JSONRPC2Error On a missing parameter, bad type or
1066         *                       {@code null} value
1067         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1068         */
1069        public List<Object> getList(final int position)
1070                throws JSONRPC2Error {
1071        
1072                final boolean allowNull = false;
1073        
1074                return getList(position, allowNull);
1075        }
1076        
1077        
1078        /**
1079         * Retrieves the specified list (maps from JSON array) parameter.
1080         *
1081         * @param position  The parameter position.
1082         * @param allowNull If {@code true} allows a {@code null} value.
1083         *
1084         * @return The parameter value as a list.
1085         *
1086         * @throws JSONRPC2Error On a missing parameter or bad type
1087         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1088         */
1089        @SuppressWarnings("unchecked")
1090        public List<Object> getList(final int position, final boolean allowNull)
1091                throws JSONRPC2Error {
1092        
1093                return (List<Object>)get(position, List.class, allowNull);
1094        }
1095        
1096        
1097        /**
1098         * Retrieves the specified optional list (maps from JSON array) 
1099         * parameter. If it doesn't exist the method will return the specified 
1100         * default value.
1101         *
1102         * @param position     The parameter position.
1103         * @param defaultValue The default return value if the parameter 
1104         *                     doesn't exist. May be {@code null}.
1105         *
1106         * @return The parameter value as a list.
1107         *
1108         * @throws JSONRPC2Error On a bad parameter type or {@code null} value
1109         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1110         */
1111        public List<Object> getOptList(final int position, final List<Object> defaultValue)
1112                throws JSONRPC2Error {
1113        
1114                final boolean allowNull = false;
1115        
1116                return getOptList(position, allowNull, defaultValue);
1117        }
1118        
1119        
1120        /**
1121         * Retrieves the specified optional list (maps from JSON array) 
1122         * parameter. If it doesn't exist the method will return the specified 
1123         * default value.
1124         *
1125         * @param position     The parameter position.
1126         * @param allowNull    If {@code true} allows a {@code null} value.
1127         * @param defaultValue The default return value if the parameter 
1128         *                     doesn't exist. May be {@code null}.
1129         *
1130         * @return The parameter value as a list.
1131         *
1132         * @throws JSONRPC2Error On a bad parameter type
1133         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1134         */
1135        @SuppressWarnings("unchecked")
1136        public List<Object> getOptList(final int position, 
1137                                       final boolean allowNull, 
1138                                       final List<Object> defaultValue)
1139                throws JSONRPC2Error {
1140        
1141                return (List<Object>)getOpt(position, List.class, allowNull, defaultValue);
1142        }
1143        
1144        
1145        /**
1146         * Retrieves the specified string array (maps from JSON array of 
1147         * strings) parameter.
1148         *
1149         * @param position The parameter position.
1150         *
1151         * @return The parameter value as a string array.
1152         *
1153         * @throws JSONRPC2Error On a missing parameter, bad type or
1154         *                       {@code null} value
1155         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1156         */
1157        public String[] getStringArray(final int position)
1158                throws JSONRPC2Error {
1159        
1160                final boolean allowNull = false;
1161        
1162                return getStringArray(position, allowNull);
1163        }
1164        
1165        
1166        /**
1167         * Retrieves the specified string array (maps from JSON array of 
1168         * strings) parameter.
1169         *
1170         * @param position  The parameter position.
1171         * @param allowNull If {@code true} allows a {@code null} value.
1172         *
1173         * @return The parameter value as a string array.
1174         *
1175         * @throws JSONRPC2Error On a missing parameter or bad type
1176         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1177         */
1178        @SuppressWarnings("unchecked")
1179        public String[] getStringArray(final int position, final boolean allowNull)
1180                throws JSONRPC2Error {
1181        
1182                List<Object> list = getList(position, allowNull);
1183                
1184                if (list == null)
1185                        return null;
1186                
1187                try {
1188                        return list.toArray(new String[list.size()]);
1189                        
1190                } catch (ArrayStoreException e) {
1191                        
1192                        throw newArrayException(position);
1193                }
1194        }
1195        
1196        
1197        /**
1198         * Retrieves the specified optional string array (maps from JSON array
1199         * of strings) parameter. If it doesn't exist the method will return 
1200         * the specified default value.
1201         *
1202         * @param position     The parameter position.
1203         * @param defaultValue The default return value if the parameter 
1204         *                     doesn't exist. May be {@code null}.
1205         *
1206         * @return The parameter value as a string array.
1207         *
1208         * @throws JSONRPC2Error On a bad parameter type or {@code null} value
1209         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1210         */
1211        public String[] getOptStringArray(final int position, final String[] defaultValue)
1212                throws JSONRPC2Error {
1213        
1214                final boolean allowNull = false;
1215        
1216                return getOptStringArray(position, allowNull, defaultValue);
1217        }
1218        
1219        
1220        /**
1221         * Retrieves the specified optional string array (maps from JSON array
1222         * of strings) parameter. If it doesn't exist the method will return 
1223         * the specified default value.
1224         *
1225         * @param position     The parameter position.
1226         * @param allowNull    If {@code true} allows a {@code null} value.
1227         * @param defaultValue The default return value if the parameter 
1228         *                     doesn't exist.  May be {@code null}.
1229         *
1230         * @return The parameter value as a string array.
1231         *
1232         * @throws JSONRPC2Error On a bad parameter type
1233         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1234         */
1235        public String[] getOptStringArray(final int position, 
1236                                          final boolean allowNull, 
1237                                          final String[] defaultValue)
1238                throws JSONRPC2Error {
1239        
1240                if (! hasParameter(position))
1241                        return defaultValue;
1242        
1243                return getStringArray(position, allowNull);
1244        }
1245        
1246        
1247        /**
1248         * Retrieves the specified map (maps from JSON object) parameter.
1249         *
1250         * @param position The parameter position.
1251         *
1252         * @return The parameter value as a map.
1253         *
1254         * @throws JSONRPC2Error On a missing parameter, bad type or
1255         *                       {@code null} value
1256         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1257         */
1258        public Map<String,Object> getMap(final int position)
1259                throws JSONRPC2Error {
1260                
1261                return getMap(position, false);
1262        }
1263        
1264        
1265        /**
1266         * Retrieves the specified map (maps from JSON object) parameter.
1267         *
1268         * @param position  The parameter position.
1269         * @param allowNull If {@code true} allows a {@code null} value.
1270         *
1271         * @return The parameter value as a map.
1272         *
1273         * @throws JSONRPC2Error On a missing parameter or bad type
1274         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1275         */
1276        @SuppressWarnings("unchecked")
1277        public Map<String,Object> getMap(final int position, final boolean allowNull)
1278                throws JSONRPC2Error {
1279                
1280                try {
1281                        return (Map<String,Object>)get(position, Map.class, allowNull);
1282                        
1283                } catch (ClassCastException e) {
1284                        
1285                        throw newUnexpectedParameterTypeException(position);
1286                }
1287        }
1288        
1289        
1290        /**
1291         * Retrieves the specified optional map (maps from JSON object) 
1292         * parameter. If it doesn't exist the method will return the specified 
1293         * default value.
1294         *
1295         * @param position     The parameter position.
1296         * @param defaultValue The default return value if the parameter 
1297         *                     doesn't exist. May be {@code null}.
1298         *
1299         * @return The parameter value as a map.
1300         *
1301         * @throws JSONRPC2Error On a bad parameter type or {@code null} value
1302         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1303         */
1304        public Map<String,Object> getOptMap(final int position, final Map<String,Object> defaultValue)
1305                throws JSONRPC2Error {
1306        
1307                return getOptMap(position, false, defaultValue);
1308        }
1309        
1310        
1311        /**
1312         * Retrieves the specified optional map (maps from JSON object) 
1313         * parameter. If it doesn't exist the method will return the specified 
1314         * default value.
1315         *
1316         * @param position     The parameter position.
1317         * @param allowNull    If {@code true} allows a {@code null} value.
1318         * @param defaultValue The default return value if the parameter 
1319         *                     doesn't exist.  May be {@code null}.
1320         *
1321         * @return The parameter value as a map.
1322         *
1323         * @throws JSONRPC2Error On a bad parameter type
1324         *                       ({@link JSONRPC2Error#INVALID_PARAMS}).
1325         */
1326        @SuppressWarnings("unchecked")
1327        public Map<String,Object> getOptMap(final int position, 
1328                                            final boolean allowNull, 
1329                                            final Map<String,Object> defaultValue)
1330                throws JSONRPC2Error {
1331        
1332                try {
1333                        return (Map<String,Object>)getOpt(position, Map.class, allowNull, defaultValue);
1334                        
1335                } catch (ClassCastException e) {
1336                        
1337                        throw newUnexpectedParameterTypeException(position);
1338                }
1339        }
1340}