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