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