001/*
002 * Copyright 2002-2012 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.web.bind;
018
019import javax.servlet.ServletRequest;
020
021/**
022 * Parameter extraction methods, for an approach distinct from data binding,
023 * in which parameters of specific types are required.
024 *
025 * <p>This approach is very useful for simple submissions, where binding
026 * request parameters to a command object would be overkill.
027 *
028 * @author Juergen Hoeller
029 * @author Keith Donald
030 * @since 2.0
031 */
032public abstract class ServletRequestUtils {
033
034        private static final IntParser INT_PARSER = new IntParser();
035
036        private static final LongParser LONG_PARSER = new LongParser();
037
038        private static final FloatParser FLOAT_PARSER = new FloatParser();
039
040        private static final DoubleParser DOUBLE_PARSER = new DoubleParser();
041
042        private static final BooleanParser BOOLEAN_PARSER = new BooleanParser();
043
044        private static final StringParser STRING_PARSER = new StringParser();
045
046
047        /**
048         * Get an Integer parameter, or {@code null} if not present.
049         * Throws an exception if it the parameter value isn't a number.
050         * @param request current HTTP request
051         * @param name the name of the parameter
052         * @return the Integer value, or {@code null} if not present
053         * @throws ServletRequestBindingException a subclass of ServletException,
054         * so it doesn't need to be caught
055         */
056        public static Integer getIntParameter(ServletRequest request, String name)
057                        throws ServletRequestBindingException {
058
059                if (request.getParameter(name) == null) {
060                        return null;
061                }
062                return getRequiredIntParameter(request, name);
063        }
064
065        /**
066         * Get an int parameter, with a fallback value. Never throws an exception.
067         * Can pass a distinguished value as default to enable checks of whether it was supplied.
068         * @param request current HTTP request
069         * @param name the name of the parameter
070         * @param defaultVal the default value to use as fallback
071         */
072        public static int getIntParameter(ServletRequest request, String name, int defaultVal) {
073                if (request.getParameter(name) == null) {
074                        return defaultVal;
075                }
076                try {
077                        return getRequiredIntParameter(request, name);
078                }
079                catch (ServletRequestBindingException ex) {
080                        return defaultVal;
081                }
082        }
083
084        /**
085         * Get an array of int parameters, return an empty array if not found.
086         * @param request current HTTP request
087         * @param name the name of the parameter with multiple possible values
088         */
089        public static int[] getIntParameters(ServletRequest request, String name) {
090                try {
091                        return getRequiredIntParameters(request, name);
092                }
093                catch (ServletRequestBindingException ex) {
094                        return new int[0];
095                }
096        }
097
098        /**
099         * Get an int parameter, throwing an exception if it isn't found or isn't a number.
100         * @param request current HTTP request
101         * @param name the name of the parameter
102         * @throws ServletRequestBindingException a subclass of ServletException,
103         * so it doesn't need to be caught
104         */
105        public static int getRequiredIntParameter(ServletRequest request, String name)
106                        throws ServletRequestBindingException {
107
108                return INT_PARSER.parseInt(name, request.getParameter(name));
109        }
110
111        /**
112         * Get an array of int parameters, throwing an exception if not found or one is not a number..
113         * @param request current HTTP request
114         * @param name the name of the parameter with multiple possible values
115         * @throws ServletRequestBindingException a subclass of ServletException,
116         * so it doesn't need to be caught
117         */
118        public static int[] getRequiredIntParameters(ServletRequest request, String name)
119                        throws ServletRequestBindingException {
120
121                return INT_PARSER.parseInts(name, request.getParameterValues(name));
122        }
123
124
125        /**
126         * Get a Long parameter, or {@code null} if not present.
127         * Throws an exception if it the parameter value isn't a number.
128         * @param request current HTTP request
129         * @param name the name of the parameter
130         * @return the Long value, or {@code null} if not present
131         * @throws ServletRequestBindingException a subclass of ServletException,
132         * so it doesn't need to be caught
133         */
134        public static Long getLongParameter(ServletRequest request, String name)
135                        throws ServletRequestBindingException {
136
137                if (request.getParameter(name) == null) {
138                        return null;
139                }
140                return getRequiredLongParameter(request, name);
141        }
142
143        /**
144         * Get a long parameter, with a fallback value. Never throws an exception.
145         * Can pass a distinguished value as default to enable checks of whether it was supplied.
146         * @param request current HTTP request
147         * @param name the name of the parameter
148         * @param defaultVal the default value to use as fallback
149         */
150        public static long getLongParameter(ServletRequest request, String name, long defaultVal) {
151                if (request.getParameter(name) == null) {
152                        return defaultVal;
153                }
154                try {
155                        return getRequiredLongParameter(request, name);
156                }
157                catch (ServletRequestBindingException ex) {
158                        return defaultVal;
159                }
160        }
161
162        /**
163         * Get an array of long parameters, return an empty array if not found.
164         * @param request current HTTP request
165         * @param name the name of the parameter with multiple possible values
166         */
167        public static long[] getLongParameters(ServletRequest request, String name) {
168                try {
169                        return getRequiredLongParameters(request, name);
170                }
171                catch (ServletRequestBindingException ex) {
172                        return new long[0];
173                }
174        }
175
176        /**
177         * Get a long parameter, throwing an exception if it isn't found or isn't a number.
178         * @param request current HTTP request
179         * @param name the name of the parameter
180         * @throws ServletRequestBindingException a subclass of ServletException,
181         * so it doesn't need to be caught
182         */
183        public static long getRequiredLongParameter(ServletRequest request, String name)
184                        throws ServletRequestBindingException {
185
186                return LONG_PARSER.parseLong(name, request.getParameter(name));
187        }
188
189        /**
190         * Get an array of long parameters, throwing an exception if not found or one is not a number.
191         * @param request current HTTP request
192         * @param name the name of the parameter with multiple possible values
193         * @throws ServletRequestBindingException a subclass of ServletException,
194         * so it doesn't need to be caught
195         */
196        public static long[] getRequiredLongParameters(ServletRequest request, String name)
197                        throws ServletRequestBindingException {
198
199                return LONG_PARSER.parseLongs(name, request.getParameterValues(name));
200        }
201
202
203        /**
204         * Get a Float parameter, or {@code null} if not present.
205         * Throws an exception if it the parameter value isn't a number.
206         * @param request current HTTP request
207         * @param name the name of the parameter
208         * @return the Float value, or {@code null} if not present
209         * @throws ServletRequestBindingException a subclass of ServletException,
210         * so it doesn't need to be caught
211         */
212        public static Float getFloatParameter(ServletRequest request, String name)
213                        throws ServletRequestBindingException {
214
215                if (request.getParameter(name) == null) {
216                        return null;
217                }
218                return getRequiredFloatParameter(request, name);
219        }
220
221        /**
222         * Get a float parameter, with a fallback value. Never throws an exception.
223         * Can pass a distinguished value as default to enable checks of whether it was supplied.
224         * @param request current HTTP request
225         * @param name the name of the parameter
226         * @param defaultVal the default value to use as fallback
227         */
228        public static float getFloatParameter(ServletRequest request, String name, float defaultVal) {
229                if (request.getParameter(name) == null) {
230                        return defaultVal;
231                }
232                try {
233                        return getRequiredFloatParameter(request, name);
234                }
235                catch (ServletRequestBindingException ex) {
236                        return defaultVal;
237                }
238        }
239
240        /**
241         * Get an array of float parameters, return an empty array if not found.
242         * @param request current HTTP request
243         * @param name the name of the parameter with multiple possible values
244         */
245        public static float[] getFloatParameters(ServletRequest request, String name) {
246                try {
247                        return getRequiredFloatParameters(request, name);
248                }
249                catch (ServletRequestBindingException ex) {
250                        return new float[0];
251                }
252        }
253
254        /**
255         * Get a float parameter, throwing an exception if it isn't found or isn't a number.
256         * @param request current HTTP request
257         * @param name the name of the parameter
258         * @throws ServletRequestBindingException a subclass of ServletException,
259         * so it doesn't need to be caught
260         */
261        public static float getRequiredFloatParameter(ServletRequest request, String name)
262                        throws ServletRequestBindingException {
263
264                return FLOAT_PARSER.parseFloat(name, request.getParameter(name));
265        }
266
267        /**
268         * Get an array of float parameters, throwing an exception if not found or one is not a number.
269         * @param request current HTTP request
270         * @param name the name of the parameter with multiple possible values
271         * @throws ServletRequestBindingException a subclass of ServletException,
272         * so it doesn't need to be caught
273         */
274        public static float[] getRequiredFloatParameters(ServletRequest request, String name)
275                        throws ServletRequestBindingException {
276
277                return FLOAT_PARSER.parseFloats(name, request.getParameterValues(name));
278        }
279
280
281        /**
282         * Get a Double parameter, or {@code null} if not present.
283         * Throws an exception if it the parameter value isn't a number.
284         * @param request current HTTP request
285         * @param name the name of the parameter
286         * @return the Double value, or {@code null} if not present
287         * @throws ServletRequestBindingException a subclass of ServletException,
288         * so it doesn't need to be caught
289         */
290        public static Double getDoubleParameter(ServletRequest request, String name)
291                        throws ServletRequestBindingException {
292
293                if (request.getParameter(name) == null) {
294                        return null;
295                }
296                return getRequiredDoubleParameter(request, name);
297        }
298
299        /**
300         * Get a double parameter, with a fallback value. Never throws an exception.
301         * Can pass a distinguished value as default to enable checks of whether it was supplied.
302         * @param request current HTTP request
303         * @param name the name of the parameter
304         * @param defaultVal the default value to use as fallback
305         */
306        public static double getDoubleParameter(ServletRequest request, String name, double defaultVal) {
307                if (request.getParameter(name) == null) {
308                        return defaultVal;
309                }
310                try {
311                        return getRequiredDoubleParameter(request, name);
312                }
313                catch (ServletRequestBindingException ex) {
314                        return defaultVal;
315                }
316        }
317
318        /**
319         * Get an array of double parameters, return an empty array if not found.
320         * @param request current HTTP request
321         * @param name the name of the parameter with multiple possible values
322         */
323        public static double[] getDoubleParameters(ServletRequest request, String name) {
324                try {
325                        return getRequiredDoubleParameters(request, name);
326                }
327                catch (ServletRequestBindingException ex) {
328                        return new double[0];
329                }
330        }
331
332        /**
333         * Get a double parameter, throwing an exception if it isn't found or isn't a number.
334         * @param request current HTTP request
335         * @param name the name of the parameter
336         * @throws ServletRequestBindingException a subclass of ServletException,
337         * so it doesn't need to be caught
338         */
339        public static double getRequiredDoubleParameter(ServletRequest request, String name)
340                        throws ServletRequestBindingException {
341
342                return DOUBLE_PARSER.parseDouble(name, request.getParameter(name));
343        }
344
345        /**
346         * Get an array of double parameters, throwing an exception if not found or one is not a number.
347         * @param request current HTTP request
348         * @param name the name of the parameter with multiple possible values
349         * @throws ServletRequestBindingException a subclass of ServletException,
350         * so it doesn't need to be caught
351         */
352        public static double[] getRequiredDoubleParameters(ServletRequest request, String name)
353                        throws ServletRequestBindingException {
354
355                return DOUBLE_PARSER.parseDoubles(name, request.getParameterValues(name));
356        }
357
358
359        /**
360         * Get a Boolean parameter, or {@code null} if not present.
361         * Throws an exception if it the parameter value isn't a boolean.
362         * <p>Accepts "true", "on", "yes" (any case) and "1" as values for true;
363         * treats every other non-empty value as false (i.e. parses leniently).
364         * @param request current HTTP request
365         * @param name the name of the parameter
366         * @return the Boolean value, or {@code null} if not present
367         * @throws ServletRequestBindingException a subclass of ServletException,
368         * so it doesn't need to be caught
369         */
370        public static Boolean getBooleanParameter(ServletRequest request, String name)
371                        throws ServletRequestBindingException {
372
373                if (request.getParameter(name) == null) {
374                        return null;
375                }
376                return (getRequiredBooleanParameter(request, name));
377        }
378
379        /**
380         * Get a boolean parameter, with a fallback value. Never throws an exception.
381         * Can pass a distinguished value as default to enable checks of whether it was supplied.
382         * <p>Accepts "true", "on", "yes" (any case) and "1" as values for true;
383         * treats every other non-empty value as false (i.e. parses leniently).
384         * @param request current HTTP request
385         * @param name the name of the parameter
386         * @param defaultVal the default value to use as fallback
387         */
388        public static boolean getBooleanParameter(ServletRequest request, String name, boolean defaultVal) {
389                if (request.getParameter(name) == null) {
390                        return defaultVal;
391                }
392                try {
393                        return getRequiredBooleanParameter(request, name);
394                }
395                catch (ServletRequestBindingException ex) {
396                        return defaultVal;
397                }
398        }
399
400        /**
401         * Get an array of boolean parameters, return an empty array if not found.
402         * <p>Accepts "true", "on", "yes" (any case) and "1" as values for true;
403         * treats every other non-empty value as false (i.e. parses leniently).
404         * @param request current HTTP request
405         * @param name the name of the parameter with multiple possible values
406         */
407        public static boolean[] getBooleanParameters(ServletRequest request, String name) {
408                try {
409                        return getRequiredBooleanParameters(request, name);
410                }
411                catch (ServletRequestBindingException ex) {
412                        return new boolean[0];
413                }
414        }
415
416        /**
417         * Get a boolean parameter, throwing an exception if it isn't found
418         * or isn't a boolean.
419         * <p>Accepts "true", "on", "yes" (any case) and "1" as values for true;
420         * treats every other non-empty value as false (i.e. parses leniently).
421         * @param request current HTTP request
422         * @param name the name of the parameter
423         * @throws ServletRequestBindingException a subclass of ServletException,
424         * so it doesn't need to be caught
425         */
426        public static boolean getRequiredBooleanParameter(ServletRequest request, String name)
427                        throws ServletRequestBindingException {
428
429                return BOOLEAN_PARSER.parseBoolean(name, request.getParameter(name));
430        }
431
432        /**
433         * Get an array of boolean parameters, throwing an exception if not found
434         * or one isn't a boolean.
435         * <p>Accepts "true", "on", "yes" (any case) and "1" as values for true;
436         * treats every other non-empty value as false (i.e. parses leniently).
437         * @param request current HTTP request
438         * @param name the name of the parameter
439         * @throws ServletRequestBindingException a subclass of ServletException,
440         * so it doesn't need to be caught
441         */
442        public static boolean[] getRequiredBooleanParameters(ServletRequest request, String name)
443                        throws ServletRequestBindingException {
444
445                return BOOLEAN_PARSER.parseBooleans(name, request.getParameterValues(name));
446        }
447
448
449        /**
450         * Get a String parameter, or {@code null} if not present.
451         * @param request current HTTP request
452         * @param name the name of the parameter
453         * @return the String value, or {@code null} if not present
454         * @throws ServletRequestBindingException a subclass of ServletException,
455         * so it doesn't need to be caught
456         */
457        public static String getStringParameter(ServletRequest request, String name)
458                        throws ServletRequestBindingException {
459
460                if (request.getParameter(name) == null) {
461                        return null;
462                }
463                return getRequiredStringParameter(request, name);
464        }
465
466        /**
467         * Get a String parameter, with a fallback value. Never throws an exception.
468         * Can pass a distinguished value to default to enable checks of whether it was supplied.
469         * @param request current HTTP request
470         * @param name the name of the parameter
471         * @param defaultVal the default value to use as fallback
472         */
473        public static String getStringParameter(ServletRequest request, String name, String defaultVal) {
474                String val = request.getParameter(name);
475                return (val != null ? val : defaultVal);
476        }
477
478        /**
479         * Get an array of String parameters, return an empty array if not found.
480         * @param request current HTTP request
481         * @param name the name of the parameter with multiple possible values
482         */
483        public static String[] getStringParameters(ServletRequest request, String name) {
484                try {
485                        return getRequiredStringParameters(request, name);
486                }
487                catch (ServletRequestBindingException ex) {
488                        return new String[0];
489                }
490        }
491
492        /**
493         * Get a String parameter, throwing an exception if it isn't found.
494         * @param request current HTTP request
495         * @param name the name of the parameter
496         * @throws ServletRequestBindingException a subclass of ServletException,
497         * so it doesn't need to be caught
498         */
499        public static String getRequiredStringParameter(ServletRequest request, String name)
500                        throws ServletRequestBindingException {
501
502                return STRING_PARSER.validateRequiredString(name, request.getParameter(name));
503        }
504
505        /**
506         * Get an array of String parameters, throwing an exception if not found.
507         * @param request current HTTP request
508         * @param name the name of the parameter
509         * @throws ServletRequestBindingException a subclass of ServletException,
510         * so it doesn't need to be caught
511         */
512        public static String[] getRequiredStringParameters(ServletRequest request, String name)
513                        throws ServletRequestBindingException {
514
515                return STRING_PARSER.validateRequiredStrings(name, request.getParameterValues(name));
516        }
517
518
519        private abstract static class ParameterParser<T> {
520
521                protected final T parse(String name, String parameter) throws ServletRequestBindingException {
522                        validateRequiredParameter(name, parameter);
523                        try {
524                                return doParse(parameter);
525                        }
526                        catch (NumberFormatException ex) {
527                                throw new ServletRequestBindingException(
528                                                "Required " + getType() + " parameter '" + name + "' with value of '" +
529                                                parameter + "' is not a valid number", ex);
530                        }
531                }
532
533                protected final void validateRequiredParameter(String name, Object parameter)
534                                throws ServletRequestBindingException {
535
536                        if (parameter == null) {
537                                throw new MissingServletRequestParameterException(name, getType());
538                        }
539                }
540
541                protected abstract String getType();
542
543                protected abstract T doParse(String parameter) throws NumberFormatException;
544        }
545
546
547        private static class IntParser extends ParameterParser<Integer> {
548
549                @Override
550                protected String getType() {
551                        return "int";
552                }
553
554                @Override
555                protected Integer doParse(String s) throws NumberFormatException {
556                        return Integer.valueOf(s);
557                }
558
559                public int parseInt(String name, String parameter) throws ServletRequestBindingException {
560                        return parse(name, parameter);
561                }
562
563                public int[] parseInts(String name, String[] values) throws ServletRequestBindingException {
564                        validateRequiredParameter(name, values);
565                        int[] parameters = new int[values.length];
566                        for (int i = 0; i < values.length; i++) {
567                                parameters[i] = parseInt(name, values[i]);
568                        }
569                        return parameters;
570                }
571        }
572
573
574        private static class LongParser extends ParameterParser<Long> {
575
576                @Override
577                protected String getType() {
578                        return "long";
579                }
580
581                @Override
582                protected Long doParse(String parameter) throws NumberFormatException {
583                        return Long.valueOf(parameter);
584                }
585
586                public long parseLong(String name, String parameter) throws ServletRequestBindingException {
587                        return parse(name, parameter);
588                }
589
590                public long[] parseLongs(String name, String[] values) throws ServletRequestBindingException {
591                        validateRequiredParameter(name, values);
592                        long[] parameters = new long[values.length];
593                        for (int i = 0; i < values.length; i++) {
594                                parameters[i] = parseLong(name, values[i]);
595                        }
596                        return parameters;
597                }
598        }
599
600
601        private static class FloatParser extends ParameterParser<Float> {
602
603                @Override
604                protected String getType() {
605                        return "float";
606                }
607
608                @Override
609                protected Float doParse(String parameter) throws NumberFormatException {
610                        return Float.valueOf(parameter);
611                }
612
613                public float parseFloat(String name, String parameter) throws ServletRequestBindingException {
614                        return parse(name, parameter);
615                }
616
617                public float[] parseFloats(String name, String[] values) throws ServletRequestBindingException {
618                        validateRequiredParameter(name, values);
619                        float[] parameters = new float[values.length];
620                        for (int i = 0; i < values.length; i++) {
621                                parameters[i] = parseFloat(name, values[i]);
622                        }
623                        return parameters;
624                }
625        }
626
627
628        private static class DoubleParser extends ParameterParser<Double> {
629
630                @Override
631                protected String getType() {
632                        return "double";
633                }
634
635                @Override
636                protected Double doParse(String parameter) throws NumberFormatException {
637                        return Double.valueOf(parameter);
638                }
639
640                public double parseDouble(String name, String parameter) throws ServletRequestBindingException {
641                        return parse(name, parameter);
642                }
643
644                public double[] parseDoubles(String name, String[] values) throws ServletRequestBindingException {
645                        validateRequiredParameter(name, values);
646                        double[] parameters = new double[values.length];
647                        for (int i = 0; i < values.length; i++) {
648                                parameters[i] = parseDouble(name, values[i]);
649                        }
650                        return parameters;
651                }
652        }
653
654
655        private static class BooleanParser extends ParameterParser<Boolean> {
656
657                @Override
658                protected String getType() {
659                        return "boolean";
660                }
661
662                @Override
663                protected Boolean doParse(String parameter) throws NumberFormatException {
664                        return (parameter.equalsIgnoreCase("true") || parameter.equalsIgnoreCase("on") ||
665                                        parameter.equalsIgnoreCase("yes") || parameter.equals("1"));
666                }
667
668                public boolean parseBoolean(String name, String parameter) throws ServletRequestBindingException {
669                        return parse(name, parameter);
670                }
671
672                public boolean[] parseBooleans(String name, String[] values) throws ServletRequestBindingException {
673                        validateRequiredParameter(name, values);
674                        boolean[] parameters = new boolean[values.length];
675                        for (int i = 0; i < values.length; i++) {
676                                parameters[i] = parseBoolean(name, values[i]);
677                        }
678                        return parameters;
679                }
680        }
681
682
683        private static class StringParser extends ParameterParser<String> {
684
685                @Override
686                protected String getType() {
687                        return "string";
688                }
689
690                @Override
691                protected String doParse(String parameter) throws NumberFormatException {
692                        return parameter;
693                }
694
695                public String validateRequiredString(String name, String value) throws ServletRequestBindingException {
696                        validateRequiredParameter(name, value);
697                        return value;
698                }
699
700                public String[] validateRequiredStrings(String name, String[] values) throws ServletRequestBindingException {
701                        validateRequiredParameter(name, values);
702                        for (String value : values) {
703                                validateRequiredParameter(name, value);
704                        }
705                        return values;
706                }
707        }
708
709}